@sincpro/web-ui 0.1.0 → 1.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 (198) hide show
  1. package/README.md +40 -13
  2. package/dist/components/activity/Activity.d.ts +69 -0
  3. package/dist/components/activity/Activity.js +279 -0
  4. package/dist/components/activity/ActivityBadge.d.ts +16 -0
  5. package/dist/components/activity/ActivityBadge.js +29 -0
  6. package/dist/components/{chatter → activity}/Messages.d.ts +2 -2
  7. package/dist/components/{chatter → activity}/Messages.js +9 -6
  8. package/dist/components/activity/index.d.ts +4 -0
  9. package/dist/components/activity/index.js +5 -0
  10. package/dist/components/assist/AssistMark.d.ts +3 -3
  11. package/dist/components/assist/AssistMark.js +1 -1
  12. package/dist/components/assist/assist.d.ts +1 -1
  13. package/dist/components/assist/assist.js +1 -1
  14. package/dist/components/calendar/RecordCalendar.d.ts +48 -0
  15. package/dist/components/calendar/RecordCalendar.js +101 -0
  16. package/dist/components/calendar/index.d.ts +1 -0
  17. package/dist/components/calendar/index.js +1 -0
  18. package/dist/components/form/Sheet.d.ts +4 -0
  19. package/dist/components/form/Sheet.js +16 -5
  20. package/dist/components/gantt/RecordGantt.d.ts +44 -0
  21. package/dist/components/gantt/RecordGantt.js +110 -0
  22. package/dist/components/gantt/index.d.ts +1 -0
  23. package/dist/components/gantt/index.js +1 -0
  24. package/dist/components/index.d.ts +4 -1
  25. package/dist/components/index.js +4 -1
  26. package/dist/components/lines/LineItems.d.ts +45 -5
  27. package/dist/components/lines/LineItems.js +106 -18
  28. package/dist/components/listing/BoardColumns.d.ts +39 -0
  29. package/dist/components/listing/BoardColumns.js +138 -0
  30. package/dist/components/listing/Listing.js +1 -1
  31. package/dist/components/listing/ListingBar.d.ts +46 -2
  32. package/dist/components/listing/ListingBar.js +47 -5
  33. package/dist/components/listing/PivotExplorer.d.ts +34 -0
  34. package/dist/components/listing/PivotExplorer.js +86 -0
  35. package/dist/components/listing/RecordCard.d.ts +6 -8
  36. package/dist/components/listing/RecordCard.js +13 -7
  37. package/dist/components/listing/Row.d.ts +25 -2
  38. package/dist/components/listing/Row.js +30 -13
  39. package/dist/components/listing/index.d.ts +2 -0
  40. package/dist/components/listing/index.js +2 -0
  41. package/dist/components/listing/set.d.ts +1 -1
  42. package/dist/components/navigation/index.d.ts +0 -2
  43. package/dist/components/navigation/index.js +2 -3
  44. package/dist/components/record/RecordAudit.d.ts +17 -0
  45. package/dist/components/record/RecordAudit.js +27 -0
  46. package/dist/components/record/RecordHeader.d.ts +27 -17
  47. package/dist/components/record/RecordHeader.js +64 -20
  48. package/dist/components/record/RecordNotices.d.ts +37 -0
  49. package/dist/components/record/RecordNotices.js +75 -0
  50. package/dist/components/record/RecordRibbon.d.ts +13 -0
  51. package/dist/components/record/RecordRibbon.js +22 -0
  52. package/dist/components/record/ThumbBar.js +1 -1
  53. package/dist/components/record/index.d.ts +2 -0
  54. package/dist/components/record/index.js +2 -0
  55. package/dist/components/search/CriteriaChips.d.ts +6 -1
  56. package/dist/components/search/CriteriaChips.js +71 -20
  57. package/dist/components/search/SearchBar.d.ts +4 -1
  58. package/dist/components/search/SearchBar.js +15 -7
  59. package/dist/components/timeline/RecordTimeline.d.ts +60 -0
  60. package/dist/components/timeline/RecordTimeline.js +214 -0
  61. package/dist/components/timeline/TimelineToolbar.d.ts +30 -0
  62. package/dist/components/timeline/TimelineToolbar.js +31 -0
  63. package/dist/components/timeline/index.d.ts +3 -0
  64. package/dist/components/timeline/index.js +3 -0
  65. package/dist/components/timeline/time.d.ts +43 -0
  66. package/dist/components/timeline/time.js +135 -0
  67. package/dist/foundations/copy.d.ts +194 -18
  68. package/dist/foundations/copy.es.js +142 -15
  69. package/dist/foundations/copy.js +133 -11
  70. package/dist/foundations/index.d.ts +1 -1
  71. package/dist/foundations/match.d.ts +23 -0
  72. package/dist/foundations/match.js +63 -0
  73. package/dist/foundations/slots.d.ts +1 -1
  74. package/dist/foundations/slots.js +8 -3
  75. package/dist/foundations/types.d.ts +7 -0
  76. package/dist/foundations/types.js +0 -36
  77. package/dist/primitives/block/index.d.ts +1 -1
  78. package/dist/primitives/block/index.js +1 -1
  79. package/dist/primitives/cell/CellStack.d.ts +7 -1
  80. package/dist/primitives/cell/CellStack.js +5 -2
  81. package/dist/primitives/choice/ComboBox.d.ts +3 -0
  82. package/dist/primitives/choice/ComboBox.js +10 -6
  83. package/dist/primitives/choice/KanbanState.d.ts +14 -0
  84. package/dist/primitives/choice/KanbanState.js +37 -0
  85. package/dist/primitives/choice/Priority.d.ts +20 -0
  86. package/dist/primitives/choice/Priority.js +41 -0
  87. package/dist/primitives/choice/SearchField.d.ts +10 -1
  88. package/dist/primitives/choice/SearchField.js +21 -2
  89. package/dist/primitives/choice/Slider.d.ts +6 -1
  90. package/dist/primitives/choice/Slider.js +2 -2
  91. package/dist/primitives/choice/index.d.ts +2 -0
  92. package/dist/primitives/choice/index.js +2 -0
  93. package/dist/primitives/date/Calendar.js +30 -6
  94. package/dist/primitives/date/DatePicker.d.ts +11 -3
  95. package/dist/primitives/date/DatePicker.js +12 -8
  96. package/dist/primitives/date/DateTime.d.ts +35 -0
  97. package/dist/primitives/date/DateTime.js +100 -0
  98. package/dist/primitives/date/index.d.ts +1 -0
  99. package/dist/primitives/date/index.js +4 -2
  100. package/dist/primitives/dialog/Dialog.d.ts +9 -3
  101. package/dist/primitives/dialog/Dialog.js +41 -5
  102. package/dist/primitives/field/DropZone.js +1 -1
  103. package/dist/primitives/field/Field.d.ts +14 -2
  104. package/dist/primitives/field/Field.js +25 -14
  105. package/dist/primitives/field/copy.d.ts +16 -0
  106. package/dist/primitives/field/copy.js +69 -0
  107. package/dist/primitives/field/index.d.ts +2 -0
  108. package/dist/primitives/field/index.js +4 -0
  109. package/dist/primitives/field/note.d.ts +55 -0
  110. package/dist/primitives/field/note.js +117 -0
  111. package/dist/primitives/index.d.ts +1 -0
  112. package/dist/primitives/index.js +1 -0
  113. package/dist/primitives/menu/Menu.d.ts +28 -0
  114. package/dist/primitives/menu/Menu.js +47 -17
  115. package/dist/primitives/notice/Callout.js +1 -1
  116. package/dist/primitives/notice/Flag.d.ts +13 -0
  117. package/dist/primitives/notice/Flag.js +31 -0
  118. package/dist/primitives/notice/index.d.ts +2 -1
  119. package/dist/primitives/notice/index.js +2 -1
  120. package/dist/primitives/overlay/Preview.d.ts +19 -0
  121. package/dist/primitives/overlay/Preview.js +69 -0
  122. package/dist/primitives/overlay/index.d.ts +1 -0
  123. package/dist/primitives/overlay/index.js +3 -1
  124. package/dist/primitives/picker/MatchedText.d.ts +10 -0
  125. package/dist/primitives/picker/MatchedText.js +12 -0
  126. package/dist/primitives/picker/Picker.d.ts +67 -4
  127. package/dist/primitives/picker/Picker.js +142 -23
  128. package/dist/primitives/table/CellCopy.d.ts +20 -0
  129. package/dist/primitives/table/CellCopy.js +23 -0
  130. package/dist/primitives/table/DataTable.d.ts +55 -1
  131. package/dist/primitives/table/DataTable.js +126 -40
  132. package/dist/primitives/table/EditableTable.d.ts +102 -2
  133. package/dist/primitives/table/EditableTable.js +287 -32
  134. package/dist/primitives/table/ReportTable.js +2 -1
  135. package/dist/primitives/table/RowActions.js +7 -2
  136. package/dist/primitives/table/SheetTable.d.ts +4 -1
  137. package/dist/primitives/table/SheetTable.js +38 -5
  138. package/dist/primitives/table/Table.d.ts +5 -0
  139. package/dist/primitives/table/Table.js +17 -8
  140. package/dist/primitives/table/index.d.ts +2 -2
  141. package/dist/primitives/table/index.js +1 -1
  142. package/dist/primitives/tabs/Tabs.d.ts +2 -2
  143. package/dist/primitives/tabs/Tabs.js +1 -1
  144. package/dist/primitives/tag/Tags.d.ts +2 -0
  145. package/dist/primitives/tag/Tags.js +2 -1
  146. package/dist/primitives/tag/TagsField.d.ts +13 -0
  147. package/dist/primitives/tag/TagsField.js +39 -10
  148. package/dist/primitives/tag/choices.d.ts +2 -0
  149. package/dist/primitives/tree/Tree.d.ts +56 -0
  150. package/dist/primitives/tree/Tree.js +74 -0
  151. package/dist/primitives/tree/index.d.ts +1 -0
  152. package/dist/primitives/tree/index.js +3 -0
  153. package/dist/primitives/value/Avatar.d.ts +16 -0
  154. package/dist/primitives/value/Avatar.js +91 -0
  155. package/dist/primitives/value/Copyable.d.ts +11 -0
  156. package/dist/primitives/value/Copyable.js +27 -6
  157. package/dist/primitives/value/Fecha.js +10 -1
  158. package/dist/primitives/value/Person.d.ts +6 -51
  159. package/dist/primitives/value/Person.js +9 -148
  160. package/dist/primitives/value/ProgressBar.d.ts +31 -0
  161. package/dist/primitives/value/ProgressBar.js +53 -0
  162. package/dist/primitives/value/Relation.d.ts +22 -5
  163. package/dist/primitives/value/Relation.js +42 -14
  164. package/dist/primitives/value/Totals.d.ts +102 -7
  165. package/dist/primitives/value/Totals.js +52 -2
  166. package/dist/primitives/value/Value.d.ts +0 -37
  167. package/dist/primitives/value/Value.js +0 -39
  168. package/dist/primitives/value/contact.d.ts +30 -0
  169. package/dist/primitives/value/contact.js +51 -0
  170. package/dist/primitives/value/index.d.ts +5 -4
  171. package/dist/primitives/value/index.js +5 -4
  172. package/dist/tokens.css +179 -7
  173. package/dist/views/list/List.d.ts +32 -3
  174. package/dist/views/list/List.js +51 -7
  175. package/dist/views/list/ListView.d.ts +1 -1
  176. package/dist/views/list/ListView.js +70 -13
  177. package/dist/views/list/columns.js +8 -1
  178. package/dist/views/list/derive.d.ts +12 -1
  179. package/dist/views/list/derive.js +29 -3
  180. package/dist/views/list/presentations.d.ts +5 -2
  181. package/dist/views/list/presentations.js +35 -3
  182. package/dist/views/list/types.d.ts +57 -1
  183. package/dist/views/list/useNarrowList.d.ts +10 -0
  184. package/dist/views/list/useNarrowList.js +20 -6
  185. package/dist/views/record/Record.d.ts +41 -8
  186. package/dist/views/record/Record.js +113 -28
  187. package/dist/views/record/index.d.ts +1 -1
  188. package/package.json +15 -4
  189. package/dist/components/chatter/Chatter.d.ts +0 -161
  190. package/dist/components/chatter/Chatter.js +0 -232
  191. package/dist/components/chatter/index.d.ts +0 -3
  192. package/dist/components/chatter/index.js +0 -4
  193. package/dist/components/navigation/CommandPalette.d.ts +0 -40
  194. package/dist/components/navigation/CommandPalette.js +0 -114
  195. package/dist/components/navigation/commands.d.ts +0 -30
  196. package/dist/components/navigation/commands.js +0 -30
  197. /package/dist/components/{chatter → activity}/Timeline.d.ts +0 -0
  198. /package/dist/components/{chatter → activity}/Timeline.js +0 -0
@@ -0,0 +1,86 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { grainName } from "../../foundations/calendar.js";
4
+ import { fillCopy, useUiCopy } from "../../foundations/copy.js";
5
+ import { pivotFrom } from "../../foundations/pivot.js";
6
+ import { downloadPivot } from "../../foundations/pivotSheet.js";
7
+ import { Button, IconButton } from "../../primitives/button/Button.js";
8
+ import { Menu } from "../../primitives/menu/Menu.js";
9
+ import { PivotTable } from "../../primitives/table/PivotTable.js";
10
+ import { Tags } from "../../primitives/tag/Tags.js";
11
+ import { ArrowLeftRight, Download, Plus, Sigma } from "lucide-react";
12
+ import { useMemo, useState } from "react";
13
+ const levelId = (level) => `${level.field}:${level.grain ?? ""}`;
14
+ const fromId = (id) => {
15
+ const [field = "", grain = ""] = id.split(":");
16
+ return grain === "" ? { field } : { field, grain: grain };
17
+ };
18
+ /**
19
+ * A pivot people actually pivot: what goes down the rows, what goes across the columns, which
20
+ * measures — chosen on the grid, not fixed in code.
21
+ *
22
+ * Context: the fold (`pivotFrom`) was already a real pivot — N levels each way, margins folded
23
+ * from the base — but its axes and measures were written once by whoever composed it. Here
24
+ * they are the person's: «+» adds a field (a date with its grain) to an axis, a level's ×
25
+ * drops it, ⇄ swaps the axes, the measures are ticked, and the grid downloads as it is seen.
26
+ * Inside a list the rows FOLLOW the search bar's grouping and the records are the ones the
27
+ * search left: the pivot answers the same question the list is asking.
28
+ */
29
+ export function PivotExplorer({ items, of, fields, measures, defaultMeasures, rows: outerRows, defaultRows = [], defaultColumns = [], onCell, height, fileName, }) {
30
+ const copy = useUiCopy();
31
+ const words = copy.pivot;
32
+ const [ownRows, setOwnRows] = useState(defaultRows);
33
+ const [columns, setColumns] = useState(defaultColumns);
34
+ const [chosen, setChosen] = useState(() => new Set(defaultMeasures ?? measures.slice(0, 1).map((one) => one.id)));
35
+ const rows = outerRows ?? ownRows;
36
+ const following = outerRows !== undefined;
37
+ const labelOf = (level) => {
38
+ const field = fields.find((one) => one.field === level.field)?.label ?? level.field;
39
+ return level.grain === undefined
40
+ ? field
41
+ : `${field} · ${grainName(level.grain).toLowerCase()}`;
42
+ };
43
+ const shownMeasures = measures.filter((one) => chosen.has(one.id));
44
+ const pivot = useMemo(() => pivotFrom(items, { rows, columns, of, measures: shownMeasures, labelOf }),
45
+ // eslint-disable-next-line react-hooks/exhaustive-deps
46
+ [items, rows, columns, of, shownMeasures.map((one) => one.id).join("|")]);
47
+ /* What can be added to an axis: every field (each grain of a date) not on either axis. */
48
+ const used = new Set([...rows, ...columns].map(levelId));
49
+ const options = fields.flatMap((field) => (field.grains ?? [undefined]).map((grain) => {
50
+ const level = grain === undefined ? { field: field.field } : { field: field.field, grain };
51
+ return {
52
+ id: levelId(level),
53
+ label: labelOf(level),
54
+ disabled: used.has(levelId(level)),
55
+ };
56
+ }));
57
+ const axis = (name, levels, set) => (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: [_jsx("span", { className: "text-micro font-medium text-muted", children: name }), _jsx(Tags, { label: name, items: levels.map((level) => ({
58
+ id: levelId(level),
59
+ label: labelOf(level),
60
+ ...(set === null
61
+ ? {}
62
+ : {
63
+ onRemove: () => set(levels.filter((one) => levelId(one) !== levelId(level))),
64
+ removeLabel: fillCopy(words.removeLevel, { level: labelOf(level) }),
65
+ }),
66
+ })) }), set === null ? (_jsx("span", { className: "text-micro text-muted italic", children: words.fromSearch })) : (_jsx(Menu, { label: fillCopy(words.addLevel, { axis: name }), icon: Plus, iconOnly: true, chevron: false, variant: "text", groups: [{ options }], onSelect: (id) => set([...levels, fromId(id)]) }))] }));
67
+ return (_jsxs("div", { className: "flex min-w-0 flex-col gap-3", children: [_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-5 gap-y-2", children: [_jsx(Menu, { label: words.measures, icon: Sigma, groups: [
68
+ {
69
+ options: measures.map((one) => ({
70
+ id: one.id,
71
+ label: one.header,
72
+ checked: chosen.has(one.id),
73
+ })),
74
+ },
75
+ ], onSelect: (id) => setChosen((current) => {
76
+ const next = new Set(current);
77
+ if (next.has(id))
78
+ next.delete(id);
79
+ else
80
+ next.add(id);
81
+ return next;
82
+ }) }), axis(words.rowsAxis, rows, following ? null : setOwnRows), axis(words.columnsAxis, columns, setColumns), _jsxs("div", { className: "ml-auto flex items-center gap-1", children: [following ? null : (_jsx(IconButton, { icon: ArrowLeftRight, label: words.swap, onClick: () => {
83
+ setOwnRows(columns);
84
+ setColumns(rows);
85
+ } })), _jsx(Button, { role: "ghost", size: "sm", icon: Download, onClick: () => downloadPivot(pivot, fileName === undefined ? {} : { name: fileName }), children: words.download })] })] }), _jsx(PivotTable, { pivot: pivot, ...(onCell === undefined ? {} : { onCell }), ...(height === undefined ? {} : { height }) })] }));
86
+ }
@@ -12,22 +12,20 @@ import type { ReactNode } from "react";
12
12
  * Its hierarchy is the row's: the state first (what it is), the name as the entry point,
13
13
  * the facts below in the quiet size.
14
14
  */
15
- export declare function RecordCard({ title, tags, meta, figure, media, onOpen, }: {
16
- /** The record's name — the entry point of the card. */
15
+ export declare function RecordCard({ title, subtitle, tags, meta, figure, media, footer, onOpen, }: {
17
16
  title: ReactNode;
18
- /** The state, as a `Tag`: read before the name, the way a board is scanned. */
17
+ /** Who or what it is about, under the title: the customer of an opportunity. */
18
+ subtitle?: ReactNode;
19
19
  tags?: ReactNode;
20
- /** The facts that tell two cards apart — a number, a date. */
21
20
  meta?: ReactNode;
22
- /** The record's key figure, on the right of the name — an amount. */
23
21
  figure?: ReactNode;
24
- /** The record's picture — a product photo, a customer's logo — or its icon. A board of
25
- * products is recognised by the pictures before a single name is read. */
26
22
  media?: {
27
23
  src?: string;
28
24
  alt?: string;
29
25
  icon?: LucideIcon;
30
26
  };
31
- /** Opens the record. Without it the card is not pressable. */
27
+ /** The card's last line, its controls: priority, kanban state, next activity, the owner's
28
+ * avatar — each its own button, pressable without opening the card. */
29
+ footer?: ReactNode;
32
30
  onOpen?: () => void;
33
31
  }): import("react").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Thumb } from "../../primitives/cell/CellStack.js";
4
4
  /**
5
5
  * One record as a card — the board presentation of a list.
@@ -13,10 +13,16 @@ import { Thumb } from "../../primitives/cell/CellStack.js";
13
13
  * Its hierarchy is the row's: the state first (what it is), the name as the entry point,
14
14
  * the facts below in the quiet size.
15
15
  */
16
- export function RecordCard({ title, tags, meta, figure, media, onOpen, }) {
17
- const body = (_jsxs(_Fragment, { children: [media === undefined ? null : (_jsx(Thumb, { size: "lg", ...(media.src === undefined ? {} : { src: media.src }), ...(media.alt === undefined ? {} : { alt: media.alt }), ...(media.icon === undefined ? {} : { icon: media.icon }) })), tags, _jsxs("div", { className: "flex min-w-0 items-baseline justify-between gap-3", children: [_jsx("p", { className: "min-w-0 truncate text-lead font-medium text-ink", children: title }), figure === undefined ? null : (_jsx("p", { className: "shrink-0 text-lead font-semibold tabular-nums text-ink", children: figure }))] }), meta === undefined ? null : (_jsx("p", { className: "text-micro tabular-nums text-muted", children: meta }))] }));
18
- const box = "flex min-w-0 flex-col gap-2 rounded-box border border-line bg-surface p-4 text-left";
19
- if (onOpen === undefined)
20
- return _jsx("div", { className: box, children: body });
21
- return (_jsx("button", { type: "button", onClick: onOpen, className: `${box} transition-colors duration-(--duration-instant) hover:border-line-strong focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary`, children: body }));
16
+ export function RecordCard({ title, subtitle, tags, meta, figure, media, footer, onOpen, }) {
17
+ /* The card is a box, and the TITLE is the button that opens it, stretched over the whole
18
+ card (`after:inset-0`). It used to be a `<button>` around everything: a priority star or a
19
+ kanban state inside it would be a control inside a control, which is invalid and which a
20
+ screen reader announces as one thing. The footer's controls sit above the stretch. */
21
+ const titleText = _jsx("span", { className: "min-w-0 truncate", children: title });
22
+ return (_jsxs("div", { className: `relative flex w-full min-w-0 flex-col gap-2 rounded-box border border-line bg-surface p-4 text-left ${onOpen === undefined
23
+ ? ""
24
+ : "transition-colors duration-(--duration-instant) focus-within:border-line-strong hover:border-line-strong"}`, children: [media === undefined ? null : (_jsx(Thumb, { size: "lg", ...(media.src === undefined ? {} : { src: media.src }), ...(media.alt === undefined ? {} : { alt: media.alt }), ...(media.icon === undefined ? {} : { icon: media.icon }) })), tags === undefined ? null : _jsx("div", { className: "flex flex-wrap gap-1.5", children: tags }), _jsxs("div", { className: "flex min-w-0 items-baseline justify-between gap-3", children: [onOpen === undefined ? (_jsx("p", { className: "flex min-w-0 text-lead font-medium text-ink", children: titleText })) : (_jsx("button", { type: "button", onClick: onOpen, className: "flex min-w-0 text-left text-lead font-medium text-ink outline-none after:absolute after:inset-0 after:rounded-box focus-visible:after:outline-2 focus-visible:after:outline-primary", children: titleText })), figure === undefined ? null : (_jsx("p", { className: "shrink-0 text-lead font-semibold tabular-nums text-ink", children: figure }))] }), subtitle === undefined ? null : (
25
+ /* Above the stretched title, at its own width: the subtitle may be a link to ANOTHER
26
+ record (the customer), which must open that one and not the card. */
27
+ _jsx("p", { className: "relative z-10 -mt-1.5 w-fit max-w-full min-w-0 truncate text-meta text-muted", children: subtitle })), meta === undefined ? null : (_jsx("p", { className: "text-micro tabular-nums text-muted", children: meta })), footer === undefined ? null : (_jsx("div", { className: "relative z-10 flex min-w-0 items-center gap-1.5", children: footer }))] }));
22
28
  }
@@ -18,11 +18,24 @@ import type { ReactNode } from "react";
18
18
  *
19
19
  * «some metadata that might help is not visible» — and it was literal: to know
20
20
  * whether `FAC/2026/0118` was paid or overdue you had to open it. */
21
- export declare function Row({ icon: Icon, name, onOpen, onPeek, tags, meta, selected, onSelectedChange, actions, more, onMoreSelect, openHint, children, }: {
21
+ export declare function Row({ icon: Icon, avatar, name, onOpen, onPeek, click, openers, tags, meta, selected, onSelectedChange, actions, more, onMoreSelect, openHint, children, copy: copyAs, }: {
22
22
  icon?: LucideIcon;
23
+ /** A person or a company in the icon's place: who the row is, recognised before it is read. */
24
+ avatar?: {
25
+ name: string;
26
+ src?: string;
27
+ };
23
28
  name: string;
29
+ /** The copy button among the row's actions: on by default, copying the row's name — the
30
+ * number of an invoice, a customer — without opening it. A string copies that instead;
31
+ * `false` removes it. */
32
+ copy?: string | false;
24
33
  onOpen?: () => void;
25
34
  onPeek?: () => void;
35
+ /** What a press on the body does when both exist: the look (default) or the record. */
36
+ click?: "peek" | "open";
37
+ /** The 👁 / ↗ buttons. Off in a list that chooses: there the row answers, it does not open. */
38
+ openers?: boolean;
26
39
  tags?: ReactNode;
27
40
  /** The secondary data, already formatted. In mono: it aligns across rows. */
28
41
  meta?: string;
@@ -63,7 +76,7 @@ export declare function Row({ icon: Icon, name, onOpen, onPeek, tags, meta, sele
63
76
  * espaciadores de casilla, ícono y acciones— en vez de declarar un ancho
64
77
  * propio. Si `Row` cambia su geometría, esto la sigue.
65
78
  */
66
- export declare function RowHeader({ name, meta, tags, withSelection, withIcon, withActions, }: {
79
+ export declare function RowHeader({ name, meta, tags, withSelection, withIcon, withAvatar, withActions, trail, }: {
67
80
  name: string;
68
81
  meta?: string;
69
82
  tags?: string;
@@ -71,10 +84,20 @@ export declare function RowHeader({ name, meta, tags, withSelection, withIcon, w
71
84
  * es el único que sabe si esta lista elige y si sus filas tienen ícono. */
72
85
  withSelection?: boolean;
73
86
  withIcon?: boolean;
87
+ /** The rows carry a face instead of an icon: a wider spacer, so the labels stay aligned. */
88
+ withAvatar?: boolean;
74
89
  /** La zona de acciones de la derecha ocupa su lugar aunque esté invisible
75
90
  * —así nada se corre al pasar el puntero—, así que el rótulo de las fichas
76
91
  * queda desalineado si acá no se reserva lo mismo. */
77
92
  withActions?: boolean;
93
+ /** What the rows have at their end — the same flags `Row` gets — drawn invisible here so
94
+ * the header reserves exactly that width. Takes over from `withActions`. */
95
+ trail?: {
96
+ actions?: ReactNode;
97
+ peek?: boolean;
98
+ open?: boolean;
99
+ copy?: boolean;
100
+ };
78
101
  }): import("react").JSX.Element;
79
102
  /**
80
103
  * The header of a group inside a list.
@@ -1,11 +1,28 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { RecordLink } from "./RecordLink.js";
4
4
  import { fillCopy, useUiCopy } from "../../foundations/copy.js";
5
5
  import { byDensity, useDensity } from "../../foundations/density.js";
6
6
  import { stickyTop } from "../../foundations/sticky.js";
7
7
  import { Menu } from "../../primitives/menu/Menu.js";
8
- import { ChevronRight, MoreHorizontal, SquareArrowOutUpRight } from "lucide-react";
8
+ import { HEADER_TEXT } from "../../primitives/table/Table.js";
9
+ import { CopyButton } from "../../primitives/value/Copyable.js";
10
+ import { Avatar } from "../../primitives/value/Person.js";
11
+ import { ChevronRight, Eye, MoreHorizontal, SquareArrowOutUpRight } from "lucide-react";
12
+ const ICON_BUTTON = "inline-flex size-(--size-control-sm) items-center justify-center rounded-pill text-muted transition-colors duration-(--duration-instant) hover:bg-ink/6 hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary";
13
+ /** The buttons at the end of a row. Its own piece so `RowHeader` can draw the same one,
14
+ * invisible, and reserve exactly its width — a spacer of a guessed width drifted every time
15
+ * a button was added. */
16
+ function Trail({ name, openAs, actions, copy: copyAs, onPeek, onOpen, more, onMoreSelect, }) {
17
+ const copy = useUiCopy();
18
+ return (_jsxs(_Fragment, { children: [actions, copyAs === false ? null : (_jsx(CopyButton, { value: copyAs ?? name, label: fillCopy(copy.copyable.field, { label: name }) })), onPeek === undefined ? null : (_jsx("button", { type: "button", onClick: (e) => {
19
+ e.stopPropagation();
20
+ onPeek();
21
+ }, title: copy.list.peek, "aria-label": `${copy.list.peek}: ${name}`, className: ICON_BUTTON, children: _jsx(Eye, { className: "size-(--size-icon-sm)", strokeWidth: 2 }) })), onOpen === undefined ? null : (_jsx("button", { type: "button", onClick: (e) => {
22
+ e.stopPropagation();
23
+ onOpen();
24
+ }, title: openAs, "aria-label": `${openAs}: ${name}`, className: ICON_BUTTON, children: _jsx(SquareArrowOutUpRight, { className: "size-(--size-icon-sm)", strokeWidth: 2 }) })), more === undefined || more.length === 0 ? null : (_jsx("span", { onClick: (e) => e.stopPropagation(), role: "presentation", children: _jsx(Menu, { label: fillCopy(copy.selection.moreOf, { name }), groups: more, icon: MoreHorizontal, iconOnly: true, chevron: false, align: "end", width: "sm", variant: "text", ...(onMoreSelect === undefined ? {} : { onSelect: onMoreSelect }) }) }))] }));
25
+ }
9
26
  /* ---------------------------------------------------------------- *
10
27
  * THE RICH ROW — the other density
11
28
  * ---------------------------------------------------------------- */
@@ -26,10 +43,13 @@ import { ChevronRight, MoreHorizontal, SquareArrowOutUpRight } from "lucide-reac
26
43
  *
27
44
  * «some metadata that might help is not visible» — and it was literal: to know
28
45
  * whether `FAC/2026/0118` was paid or overdue you had to open it. */
29
- export function Row({ icon: Icon, name, onOpen, onPeek, tags, meta, selected, onSelectedChange, actions, more, onMoreSelect, openHint, children, }) {
46
+ export function Row({ icon: Icon, avatar, name, onOpen, onPeek, click = "peek", openers = true, tags, meta, selected, onSelectedChange, actions, more, onMoreSelect, openHint, children, copy: copyAs, }) {
30
47
  const d = useDensity();
31
48
  const copy = useUiCopy();
32
- const openAs = openHint ?? copy.shell.openAsPanel;
49
+ const openAs = openHint ?? copy.list.open;
50
+ /* A read-only row (nothing to peek, nothing to open) does not light up on hover: a wash
51
+ and a hand promise a click that does nothing. */
52
+ const interactive = (onPeek ?? onOpen) !== undefined;
33
53
  const row = (
34
54
  /* A `div` and not an `li`: whoever composes decides whether this is a list,
35
55
  a table or a branch of a tree. And the zone that peeks is a `div` with
@@ -41,21 +61,18 @@ export function Row({ icon: Icon, name, onOpen, onPeek, tags, meta, selected, on
41
61
  no rule between them read as a paragraph. The wash is the inner row,
42
62
  inset and rounded, so the hover is a mark and not a stripe. The inner
43
63
  padding is shorter by exactly that inset, so the text does not move. */
44
- className: `group/row flex min-w-0 items-center rounded-control transition-colors duration-(--duration-instant) hover:bg-ink/4 ${byDensity(d, "gap-3 px-2 py-2", "gap-2 px-1.5 py-1")}`,
64
+ className: `group/row flex min-w-0 items-center rounded-control transition-colors duration-(--duration-instant) ${interactive ? "cursor-pointer hover:bg-ink/4" : ""} ${byDensity(d, "gap-3 px-2 py-2", "gap-2 px-1.5 py-1")}`,
45
65
  /* `onPeek` con `onOpen` de reserva: quien sólo declara `onOpen` —lo
46
66
  natural si nunca oyó hablar de la vista previa— tiene que poder abrir
47
67
  igual haciendo click en cualquier parte de la fila, no sólo en el
48
68
  nombre. Con la vista previa apagada (`PeekLayer.tsx`) esto es hoy el
49
69
  caso de todos: un click en la fila abre, sin más. */
50
- onClick: onPeek ?? onOpen, role: "presentation", children: [onSelectedChange === undefined ? null : (_jsx("input", { type: "checkbox", checked: selected ?? false, "aria-label": fillCopy(copy.selection.named, { name }), onChange: (e) => onSelectedChange(e.target.checked), onClick: (e) => e.stopPropagation(), className: "size-3.5 shrink-0 cursor-pointer accent-primary" })), Icon === undefined ? null : (_jsx(Icon, { className: "size-(--size-icon-md) shrink-0 text-muted", strokeWidth: 1.75, "aria-hidden": true })), _jsxs("div", { className: `flex min-w-0 shrink grow basis-(--container-name) flex-wrap items-center gap-y-0 ${byDensity(d, "gap-x-3", "gap-x-2")}`, children: [_jsx("span", { className: "min-w-0 grow basis-(--container-name)", children: _jsx(RecordLink, { ...((onOpen ?? onPeek) === undefined ? {} : { onClick: (onOpen ?? onPeek) }), title: name, children: name }) }), meta === undefined ? null : (
70
+ onClick: click === "open" ? (onOpen ?? onPeek) : (onPeek ?? onOpen), role: "presentation", children: [onSelectedChange === undefined ? null : (_jsx("input", { type: "checkbox", checked: selected ?? false, "aria-label": fillCopy(copy.selection.named, { name }), onChange: (e) => onSelectedChange(e.target.checked), onClick: (e) => e.stopPropagation(), className: "size-3.5 shrink-0 cursor-pointer accent-primary" })), avatar !== undefined ? (_jsx(Avatar, { name: avatar.name, size: "sm", ...(avatar.src === undefined ? {} : { src: avatar.src }) })) : Icon === undefined ? null : (_jsx(Icon, { className: "size-(--size-icon-md) shrink-0 text-muted", strokeWidth: 1.75, "aria-hidden": true })), _jsxs("div", { className: `flex min-w-0 shrink grow basis-(--container-name) flex-wrap items-center gap-y-0 ${byDensity(d, "gap-x-3", "gap-x-2")}`, children: [_jsx("span", { className: "min-w-0 grow basis-(--container-name)", children: _jsx(RecordLink, { ...((onOpen ?? onPeek) === undefined ? {} : { onClick: (onOpen ?? onPeek) }), title: name, children: name }) }), meta === undefined ? null : (
51
71
  /* Wrapped or not, the metadata is still there —it is what saves
52
72
  having to open a set to know whether it is a thousand rows or a
53
73
  million— and it truncates instead of wrapping into itself: three
54
74
  lines per row in a 480 px drawer means three rows fit on screen. */
55
- _jsx("span", { className: "min-w-0 truncate text-micro tabular-nums text-muted", children: meta }))] }), tags === undefined ? null : (_jsx("span", { className: "flex min-w-fit shrink basis-(--container-tags) items-center justify-end gap-1.5", children: tags })), _jsxs("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity duration-(--duration-instant) group-hover/row:opacity-100 focus-within:opacity-100", children: [actions, onOpen === undefined ? null : (_jsx("button", { type: "button", onClick: (e) => {
56
- e.stopPropagation();
57
- onOpen();
58
- }, title: openAs, "aria-label": `${openAs}: ${name}`, className: "inline-flex size-(--size-control-sm) items-center justify-center rounded-pill text-muted transition-colors duration-(--duration-instant) hover:bg-ink/6 hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary", children: _jsx(SquareArrowOutUpRight, { className: "size-(--size-icon-sm)", strokeWidth: 2 }) })), more === undefined || more.length === 0 ? null : (_jsx("span", { onClick: (e) => e.stopPropagation(), role: "presentation", children: _jsx(Menu, { label: fillCopy(copy.selection.moreOf, { name }), groups: more, icon: MoreHorizontal, iconOnly: true, chevron: false, align: "end", width: "sm", variant: "text", ...(onMoreSelect === undefined ? {} : { onSelect: onMoreSelect }) }) }))] })] }) }));
75
+ _jsx("span", { className: "min-w-0 truncate text-micro tabular-nums text-muted", children: meta }))] }), tags === undefined ? null : (_jsx("span", { className: "flex min-w-fit shrink basis-(--container-tags) items-center justify-end gap-1.5", children: tags })), _jsx("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity duration-(--duration-instant) group-hover/row:opacity-100 focus-within:opacity-100 pointer-coarse:opacity-100", children: _jsx(Trail, { name: name, openAs: openAs, ...(actions === undefined ? {} : { actions }), copy: copyAs, ...(onPeek === undefined || !openers ? {} : { onPeek }), ...(onOpen === undefined || !openers ? {} : { onOpen }), ...(more === undefined ? {} : { more }), ...(onMoreSelect === undefined ? {} : { onMoreSelect }) }) })] }) }));
59
76
  if (children === undefined)
60
77
  return row;
61
78
  /* What the resource produced, hanging below. The indent reaches up to where
@@ -86,13 +103,13 @@ export function Row({ icon: Icon, name, onOpen, onPeek, tags, meta, selected, on
86
103
  * espaciadores de casilla, ícono y acciones— en vez de declarar un ancho
87
104
  * propio. Si `Row` cambia su geometría, esto la sigue.
88
105
  */
89
- export function RowHeader({ name, meta, tags, withSelection = false, withIcon = false, withActions = false, }) {
106
+ export function RowHeader({ name, meta, tags, withSelection = false, withIcon = false, withAvatar = false, withActions = false, trail, }) {
90
107
  const d = useDensity();
91
- const rotulo = "truncate text-micro font-medium text-muted";
108
+ const rotulo = `truncate ${HEADER_TEXT}`;
92
109
  return (_jsxs("div", { "data-slot": "row-header",
93
110
  /* Se pega debajo de la barra de la lista, como el encabezado de la tabla
94
111
  y el de la planilla. Ver `system/sticky.tsx`. */
95
- className: `sticky z-10 flex min-w-0 items-center border-b border-line-strong bg-ground ${byDensity(d, "gap-3 px-3 py-2", "gap-2 px-2.5 py-1.5")}`, style: stickyTop, role: "presentation", children: [!withSelection ? null : _jsx("span", { className: "size-3.5 shrink-0", "aria-hidden": true }), !withIcon ? null : _jsx("span", { className: "size-(--size-icon-md) shrink-0", "aria-hidden": true }), _jsxs("div", { className: `flex min-w-0 shrink grow basis-(--container-name) flex-wrap items-center gap-y-0 ${byDensity(d, "gap-x-3", "gap-x-2")}`, children: [_jsx("span", { className: `min-w-0 grow basis-(--container-name) ${rotulo}`, children: name }), meta === undefined ? null : _jsx("span", { className: `min-w-0 ${rotulo}`, children: meta })] }), tags === undefined ? null : (_jsx("span", { className: "flex min-w-fit shrink basis-(--container-tags) items-center justify-end", children: _jsx("span", { className: rotulo, children: tags }) })), !withActions ? null : (_jsx("span", { className: "size-(--size-control-sm) shrink-0", "aria-hidden": true }))] }));
112
+ className: `sticky z-10 flex min-w-0 items-center border-b border-line-strong bg-ground ${byDensity(d, "gap-3 px-3 py-2", "gap-2 px-2.5 py-1.5")}`, style: stickyTop, role: "presentation", children: [!withSelection ? null : _jsx("span", { className: "size-3.5 shrink-0", "aria-hidden": true }), withAvatar ? (_jsx("span", { className: "size-5 shrink-0", "aria-hidden": true })) : !withIcon ? null : (_jsx("span", { className: "size-(--size-icon-md) shrink-0", "aria-hidden": true })), _jsxs("div", { className: `flex min-w-0 shrink grow basis-(--container-name) flex-wrap items-center gap-y-0 ${byDensity(d, "gap-x-3", "gap-x-2")}`, children: [_jsx("span", { className: `min-w-0 grow basis-(--container-name) ${rotulo}`, children: name }), meta === undefined ? null : _jsx("span", { className: `min-w-0 ${rotulo}`, children: meta })] }), tags === undefined ? null : (_jsx("span", { className: "flex min-w-fit shrink basis-(--container-tags) items-center justify-end", children: _jsx("span", { className: rotulo, children: tags }) })), trail !== undefined ? (_jsx("span", { inert: true, "aria-hidden": true, className: "invisible flex shrink-0 items-center gap-1", children: _jsx(Trail, { name: "", openAs: "", ...(trail.actions === undefined ? {} : { actions: trail.actions }), ...(trail.copy === false ? { copy: false } : {}), ...(trail.peek === true ? { onPeek: () => { } } : {}), ...(trail.open === true ? { onOpen: () => { } } : {}) }) })) : !withActions ? null : (_jsx("span", { className: "size-(--size-control-sm) shrink-0", "aria-hidden": true }))] }));
96
113
  }
97
114
  /**
98
115
  * The header of a group inside a list.
@@ -11,7 +11,9 @@ export { type Selection, useSelection } from "./selection.js";
11
11
  export { type SetControl, useSetControl } from "./set.js";
12
12
  export { toggleCollapsed, type TreeControl, useTreeControl, } from "./tree.js";
13
13
  export { type ViewOption, ViewSwitcher, } from "./ViewSwitcher.js";
14
+ export { type BoardColumn, BoardColumns, type BoardStages, } from "./BoardColumns.js";
14
15
  export { type GroupingControl, useGroupingControl, } from "./grouping.js";
15
16
  export { GroupingLevels, type GroupingLevelsProps, } from "./GroupingLevels.js";
17
+ export { PivotExplorer, type PivotExplorerProps, } from "./PivotExplorer.js";
16
18
  export { RecordCard } from "./RecordCard.js";
17
19
  export { type SortField, SortMenu, type SortMenuProps, } from "./SortMenu.js";
@@ -14,7 +14,9 @@ export { useSetControl } from "./set.js";
14
14
  export { toggleCollapsed, useTreeControl, } from "./tree.js";
15
15
  export { ViewSwitcher, } from "./ViewSwitcher.js";
16
16
  /* El control de los niveles. El vocabulario está en `system/grouping.ts`. */
17
+ export { BoardColumns, } from "./BoardColumns.js";
17
18
  export { useGroupingControl, } from "./grouping.js";
18
19
  export { GroupingLevels, } from "./GroupingLevels.js";
20
+ export { PivotExplorer, } from "./PivotExplorer.js";
19
21
  export { RecordCard } from "./RecordCard.js";
20
22
  export { SortMenu, } from "./SortMenu.js";
@@ -33,7 +33,7 @@ import { type Pages } from "./pages.js";
33
33
  * también; lo que se lee está en el cruce. Tampoco se
34
34
  * ordena: el orden de cada eje es el de sus claves.
35
35
  */
36
- export type ListPresent = "rows" | "table" | "board" | "sheet" | "report" | "pivot";
36
+ export type ListPresent = "rows" | "table" | "board" | "sheet" | "report" | "pivot" | "calendar" | "gantt" | "timeline";
37
37
  export interface SetControl<Row, Present extends string = ListPresent> extends Pages<Row> {
38
38
  present: Present;
39
39
  setPresent: (next: Present) => void;
@@ -1,3 +1 @@
1
- export { CommandPalette, useCommandPaletteShortcut, } from "./CommandPalette.js";
2
- export { type PaletteCommand } from "./commands.js";
3
1
  export { NavigationTree, type NavItem, } from "./NavigationTree.js";
@@ -1,4 +1,3 @@
1
- /* LA NAVEGACIÓN — el árbol lateral de una aplicación y la paleta de comandos. */
2
- export { CommandPalette, useCommandPaletteShortcut, } from "./CommandPalette.js";
3
- export {} from "./commands.js";
1
+ /* LA NAVEGACIÓN — el árbol lateral de una aplicación. La paleta de comandos (⌘K) es del
2
+ espacio de trabajo (composer), no de acá. */
4
3
  export { NavigationTree, } from "./NavigationTree.js";
@@ -0,0 +1,17 @@
1
+ import type { ReactNode } from "react";
2
+ /** Who did something to the record and when, already written: «Carla Rojas», «hace 3 días». */
3
+ export type RecordStamp = {
4
+ by: ReactNode;
5
+ at: ReactNode;
6
+ };
7
+ /**
8
+ * The last line of the paper: who created the record and who touched it last.
9
+ *
10
+ * Context: «¿quién cambió esto?» is the first question in front of a number that looks wrong,
11
+ * and the activity's timeline answers it only for what was logged. Small and at the end: it is
12
+ * consulted, not read.
13
+ */
14
+ export declare function RecordAudit({ created, updated, }: {
15
+ created?: RecordStamp;
16
+ updated?: RecordStamp;
17
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,27 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { fillCopy, useUiCopy } from "../../foundations/copy.js";
4
+ /**
5
+ * The last line of the paper: who created the record and who touched it last.
6
+ *
7
+ * Context: «¿quién cambió esto?» is the first question in front of a number that looks wrong,
8
+ * and the activity's timeline answers it only for what was logged. Small and at the end: it is
9
+ * consulted, not read.
10
+ */
11
+ export function RecordAudit({ created, updated, }) {
12
+ const copy = useUiCopy();
13
+ const stamp = (template, one) => {
14
+ const [before, middle, after] = splitStamp(template);
15
+ return (_jsxs("span", { children: [before, _jsx("span", { className: "text-ink", children: one.by }), middle, _jsx("span", { className: "tabular-nums", children: one.at }), after] }));
16
+ };
17
+ if (created === undefined && updated === undefined)
18
+ return null;
19
+ return (_jsxs("p", { className: "flex flex-wrap gap-x-4 gap-y-1 border-t border-line px-4 py-2.5 text-micro text-muted", children: [created === undefined ? null : stamp(copy.record.created, created), updated === undefined ? null : stamp(copy.record.updated, updated)] }));
20
+ }
21
+ /** «Creado por {by} · {at}» cut around its two holes, so `by` and `at` can be nodes. */
22
+ function splitStamp(template) {
23
+ const marked = fillCopy(template, { by: "\u0000", at: "\u0001" });
24
+ const [before = "", rest = ""] = marked.split("\u0000");
25
+ const [middle = "", after = ""] = rest.split("\u0001");
26
+ return [before, middle, after];
27
+ }
@@ -2,9 +2,9 @@ import { type Crumb } from "./Breadcrumb.js";
2
2
  import { type DirtyState } from "./DirtyActions.js";
3
3
  import type { Surface, Tone } from "../../foundations/types.js";
4
4
  import type { LucideIcon } from "lucide-react";
5
- import { type ReactNode } from "react";
5
+ import type { ReactNode } from "react";
6
6
  export type { Surface };
7
- export declare function RecordHeader({ crumbs, pager, actions, status, kicker, icon: Icon, title, subtitle, tag, figure, railSurface, smart, aspects, dirty, chatter, chatterLayout, children, }: {
7
+ export declare function RecordHeader({ crumbs, pager, actions, status, kicker, icon: Icon, title, subtitle, tag, figure, railSurface, smart, aspects, dirty, activity, activityLayout, width, ribbon, blocking, indicator, children, }: {
8
8
  crumbs?: readonly Crumb[];
9
9
  pager?: ReactNode;
10
10
  /** The action buttons. At most three visible + a `Menu` with `iconOnly`, and
@@ -12,25 +12,29 @@ export declare function RecordHeader({ crumbs, pager, actions, status, kicker, i
12
12
  * never the navigation ones. They take the rail's surface from context: write
13
13
  * them without `surface`, light by default, dark on `Record.Rail surface="chrome"`. */
14
14
  actions?: ReactNode;
15
- /** La historia del registro: `<Chatter …/>`. En `page` es la última fila de
16
- * la hoja —el mismo papel, separado por una línea— y no una segunda tarjeta.
17
- * En `aside` sigue al costado, con su propio marco. */
18
- chatter?: ReactNode;
15
+ /** Everything around the record: its notices, conversation and history — an
16
+ * `Activity`. Never inside the sheet: it is about the record, not part of the form. */
17
+ activity?: ReactNode;
19
18
  /**
20
- * Dónde se pone respecto de la hoja.
19
+ * Where the activity goes, as the record's width allows (THE PAGE, below).
21
20
  *
22
- * page debajo y a todo el ancho, como hoja propia. **El default**: un
23
- * formulario necesita su ancho, y una columna al costado se lo
24
- * come en cuanto la pantalla no sobra.
25
- * aside al costado cuando hay ancho, debajo cuando no.
21
+ * auto a column at the RIGHT of the sheet, joined to it, when the page is
22
+ * `--container-beside` wide, and a card BELOW the sheet when it is not.
23
+ * **The default.**
24
+ * below always below the sheet, at its width.
26
25
  *
27
- * Lo dice el REGISTRO y no se adivina del hijo: la dirección del flex la pone
28
- * este contenedor, y un padre que mira qué le pasaron adentro para decidir
29
- * cómo acomodarse se rompe la próxima vez que alguien envuelva el chatter en
30
- * otra cosa.
26
+ * It is said by the RECORD and not guessed from the child: the flex direction is set by
27
+ * this container, and a parent that inspects what it was handed to decide how to lay
28
+ * itself out breaks the next time someone wraps the panel in something else.
31
29
  */
32
- chatterLayout?: "aside" | "page";
33
- /** `StatusBar` (where is it at?), the chatter's `Timeline` (what happened to get
30
+ activityLayout?: "auto" | "below";
31
+ /** How wide the page column gets. `reading` (the default) stops the sheet at
32
+ * `--container-sheet`, centred, and lines the rail up with it; `full` takes the whole page
33
+ * — a record whose body is a wide table. */
34
+ width?: "reading" | "full";
35
+ /** The band across the paper's corner (`RecordRibbon`): what the record IS now. */
36
+ ribbon?: ReactNode;
37
+ /** `StatusBar` (where is it at?), the activity's `Timeline` (what happened to get
34
38
  * here?), or both: they are different questions and many records only have one. */
35
39
  status?: ReactNode;
36
40
  /** The category, small and above the name: «factura de cliente». It makes a
@@ -65,6 +69,12 @@ export declare function RecordHeader({ crumbs, pager, actions, status, kicker, i
65
69
  * it has no way to know. As long as there are changes, the only thing that
66
70
  * can be done is confirm them or throw them away. */
67
71
  dirty?: DirtyState;
72
+ /** What is WRONG with the record — `RecordBlocking`. Always visible, outside the sheet:
73
+ * between the rail and the paper, never among the fields (see `RecordNotices`). */
74
+ blocking?: ReactNode;
75
+ /** The rest of the record's notices, as one indicator — `RecordNoticeIndicator`. In the
76
+ * rail next to the counters; without a rail, first in the record, next to them. */
77
+ indicator?: ReactNode;
68
78
  /** The record body. It sits IN the paper with the title, so groups, tabs
69
79
  * and the page are one white sheet on the ground — Odoo's `<sheet>`. */
70
80
  children?: ReactNode;