@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
@@ -1,6 +1,6 @@
1
1
  import { type Pile } from "../../foundations/grouping.js";
2
2
  import { type DataColumn } from "../../primitives/table/DataTable.js";
3
- import type { ReportRow } from "../../primitives/table/ReportTable.js";
3
+ import type { ReportColumn, ReportRow } from "../../primitives/table/ReportTable.js";
4
4
  import type { SheetColumn } from "../../primitives/table/SheetTable.js";
5
5
  import type { ListGroup, ListSheet, RowOf } from "./types.js";
6
6
  /**
@@ -74,3 +74,14 @@ export declare function sheetRowsOf<T extends object>(piles: readonly Pile<T>[]
74
74
  * no digan totales distintos del mismo conjunto.
75
75
  */
76
76
  export declare function reportRowsOf<T extends object>(piles: readonly Pile<T>[] | null, columns: readonly DataColumn<T>[] | undefined, row: (item: T) => RowOf): readonly ReportRow[];
77
+ /**
78
+ * The columns of a report built from the piles, written like the list's own columns.
79
+ *
80
+ * `reportRowsOf` puts in position `i` the aggregate of the `i`-th column that declared one,
81
+ * so `report.columns[i]` is THAT column's: a report column without its own `format` takes
82
+ * the one the list column declared — otherwise the report wrote «Bs. 78.752,90» in the
83
+ * table's footer and «78.752,90» in its own total line. A format declared on the report
84
+ * column wins: it is the more specific declaration. A `count` column keeps the report's own
85
+ * number format — a count of rows is not an amount.
86
+ */
87
+ export declare function reportColumnsOf<T extends object>(reportColumns: readonly ReportColumn[], columns: readonly DataColumn<T>[] | undefined): readonly ReportColumn[];
@@ -1,5 +1,5 @@
1
1
  import { levelKey, pathKey, pileItems, pileLabel, } from "../../foundations/grouping.js";
2
- import { aggregateOf } from "../../primitives/table/DataTable.js";
2
+ import { aggregateFormat, aggregateOf, aggregateText, } from "../../primitives/table/DataTable.js";
3
3
  /**
4
4
  * Context: what a list DERIVES from its set, as pure functions — the table's group levels,
5
5
  * the sheet drawn from the declared columns, the sheet's flattened band/row sequence and
@@ -114,10 +114,14 @@ export function sheetRowsOf(piles, isOpen, columns) {
114
114
  las `columns` de la lista —las mismas que suma el pie de la
115
115
  tabla— para que la planilla y la tabla no digan números distintos
116
116
  del mismo montón. La planilla declara SUS columnas aparte, así que
117
- se busca por id. */
117
+ se busca por id. Y se ESCRIBE como el pie: con el `format` de la
118
+ columna, para que el montón no diga «78752,9» debajo de «Bs.». */
118
119
  total: (column) => {
119
120
  const declared = columns?.find((one) => one.id === column.id);
120
- return declared === undefined ? null : aggregateOf(declared, inside);
121
+ const value = declared === undefined ? null : aggregateOf(declared, inside);
122
+ return declared === undefined || value === null
123
+ ? null
124
+ : aggregateText(declared, value);
121
125
  },
122
126
  },
123
127
  });
@@ -179,3 +183,25 @@ export function reportRowsOf(piles, columns, row) {
179
183
  };
180
184
  return piles.map(line);
181
185
  }
186
+ /**
187
+ * The columns of a report built from the piles, written like the list's own columns.
188
+ *
189
+ * `reportRowsOf` puts in position `i` the aggregate of the `i`-th column that declared one,
190
+ * so `report.columns[i]` is THAT column's: a report column without its own `format` takes
191
+ * the one the list column declared — otherwise the report wrote «Bs. 78.752,90» in the
192
+ * table's footer and «78.752,90» in its own total line. A format declared on the report
193
+ * column wins: it is the more specific declaration. A `count` column keeps the report's own
194
+ * number format — a count of rows is not an amount.
195
+ */
196
+ export function reportColumnsOf(reportColumns, columns) {
197
+ if (columns === undefined)
198
+ return reportColumns;
199
+ const numeric = columns.filter((column) => column.aggregate !== undefined);
200
+ return reportColumns.map((column, index) => {
201
+ const declared = numeric[index];
202
+ const format = declared === undefined ? undefined : aggregateFormat(declared);
203
+ return column.format !== undefined || format === undefined
204
+ ? column
205
+ : { ...column, format };
206
+ });
207
+ }
@@ -15,11 +15,14 @@ type TableProps<T extends object> = {
15
15
  levels: NonNullable<Parameters<typeof DataTable<T>>[0]["levels"]>;
16
16
  group?: ListGroup<T>;
17
17
  totals: boolean;
18
- } & Pick<ListViewProps<T>, "sort" | "onSortChange" | "selection" | "rowActions" | "onPeek" | "onOpen">;
18
+ } & Pick<ListViewProps<T>, "sort" | "onSortChange" | "selection" | "rowActions" | "create" | "onPeek" | "onOpen"> & {
19
+ rowClick?: "peek" | "open";
20
+ onChoose?: (item: T) => void;
21
+ };
19
22
  /** The table receives EVERYTHING that belongs to the set —sorting, selection, grouping,
20
23
  * which columns are on screen— and not a trimmed down version: that was exactly the trap
21
24
  * this archetype exists to avoid, that switching presentation loses capabilities. */
22
- export declare function TablePresentation<T extends object>({ items, columns, keyOf, levels, group, totals, sort, onSortChange, selection, rowActions, onPeek, onOpen, }: TableProps<T>): import("react").JSX.Element;
25
+ export declare function TablePresentation<T extends object>({ items, columns, keyOf, levels, group, totals, sort, onSortChange, selection, rowActions, create, onPeek, onOpen, rowClick, onChoose, }: TableProps<T>): import("react").JSX.Element;
23
26
  type SheetProps<T extends object> = {
24
27
  spec: ListSheet<T>;
25
28
  /** The flattened bands and rows when the set is grouped; `null` when it is not. */
@@ -3,10 +3,40 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { useUiCopy } from "../../foundations/copy.js";
4
4
  import { DataTable } from "../../primitives/table/DataTable.js";
5
5
  import { SheetTable } from "../../primitives/table/SheetTable.js";
6
+ import { Eye, SquareArrowOutUpRight } from "lucide-react";
6
7
  /** The table receives EVERYTHING that belongs to the set —sorting, selection, grouping,
7
8
  * which columns are on screen— and not a trimmed down version: that was exactly the trap
8
9
  * this archetype exists to avoid, that switching presentation loses capabilities. */
9
- export function TablePresentation({ items, columns, keyOf, levels, group, totals, sort, onSortChange, selection, rowActions, onPeek, onOpen, }) {
10
+ export function TablePresentation({ items, columns, keyOf, levels, group, totals, sort, onSortChange, selection, rowActions, create, onPeek, onOpen, rowClick = "peek", onChoose, }) {
11
+ const copy = useUiCopy();
12
+ const press = onChoose ?? (rowClick === "open" ? (onOpen ?? onPeek) : (onPeek ?? onOpen));
13
+ /* The row's ways in go FIRST among its actions, so they are always the inline ones and
14
+ always in the same place; the domain's actions follow (and fold into ⋯ past two). */
15
+ const withOpeners = onPeek === undefined && onOpen === undefined
16
+ ? rowActions
17
+ : (item) => [
18
+ ...(onPeek === undefined
19
+ ? []
20
+ : [
21
+ {
22
+ id: "peek",
23
+ label: copy.list.peek,
24
+ icon: Eye,
25
+ onSelect: () => onPeek(item),
26
+ },
27
+ ]),
28
+ ...(onOpen === undefined
29
+ ? []
30
+ : [
31
+ {
32
+ id: "open",
33
+ label: copy.list.open,
34
+ icon: SquareArrowOutUpRight,
35
+ onSelect: () => onOpen(item),
36
+ },
37
+ ]),
38
+ ...(rowActions?.(item) ?? []),
39
+ ];
10
40
  return (_jsx(DataTable, { data: items, columns: columns, getRowId: keyOf, ...(sort === undefined ? {} : { sort }), ...(onSortChange === undefined ? {} : { onSortChange }), ...(levels.length === 0 ? {} : { levels: levels }), ...(group?.collapsed === undefined ? {} : { collapsed: group.collapsed }), ...(group?.onToggle === undefined ? {} : { onGroupToggle: group.onToggle }), ...(selection === undefined
11
41
  ? {}
12
42
  : {
@@ -22,7 +52,7 @@ export function TablePresentation({ items, columns, keyOf, levels, group, totals
22
52
  selection.toggle(id);
23
53
  }
24
54
  },
25
- }), ...(totals ? { totals: true } : {}), ...(rowActions === undefined ? {} : { rowActions }), ...((onPeek ?? onOpen) === undefined ? {} : { onRowClick: onPeek ?? onOpen }) }));
55
+ }), ...(totals ? { totals: true } : {}), ...(withOpeners === undefined ? {} : { rowActions: withOpeners }), ...(create === undefined ? {} : { create }), ...(press === undefined ? {} : { onRowClick: press }) }));
26
56
  }
27
57
  /** The sheet receives the set by INDEX and not a ready-made matrix: `SheetTable` is
28
58
  * virtualized precisely so as not to materialize a million rows, and handing it copies
@@ -99,5 +129,7 @@ export function SheetPresentation({ spec, entries, ordered, group, }) {
99
129
  ? false
100
130
  : (spec.editable?.(item, column, columnIndex) ?? false);
101
131
  },
102
- }) }));
132
+ }), ...(spec.onAddRow === undefined || entries !== null
133
+ ? {}
134
+ : { onAddRow: spec.onAddRow }) }));
103
135
  }
@@ -1,10 +1,15 @@
1
+ import type { RecordCalendarProps } from "../../components/calendar/RecordCalendar.js";
2
+ import type { RecordGanttProps } from "../../components/gantt/RecordGantt.js";
3
+ import type { BoardStages } from "../../components/listing/BoardColumns.js";
1
4
  import type { ListIdentity } from "../../components/listing/ListingBar.js";
2
5
  import type { ListPresent } from "../../components/listing/set.js";
3
6
  import type { SortField } from "../../components/listing/SortMenu.js";
7
+ import type { RecordTimelineProps } from "../../components/timeline/RecordTimeline.js";
8
+ import type { GroupableField } from "../../foundations/grouping.js";
4
9
  import type { Grouping, GroupLevel } from "../../foundations/grouping.js";
5
10
  import type { Pivot, PivotKey, PivotSpec } from "../../foundations/pivot.js";
6
11
  import type { Surface, Tone } from "../../foundations/types.js";
7
- import type { DataColumn } from "../../primitives/table/DataTable.js";
12
+ import type { DataColumn, DataTableCreate } from "../../primitives/table/DataTable.js";
8
13
  import type { ReportColumn, ReportRow } from "../../primitives/table/ReportTable.js";
9
14
  import type { RowAction } from "../../primitives/table/RowActions.js";
10
15
  import type { SheetColumn } from "../../primitives/table/SheetTable.js";
@@ -23,6 +28,11 @@ export type { ListPresent };
23
28
  /** What an item looks like when the presentation is rows. */
24
29
  export type RowOf = {
25
30
  icon?: LucideIcon;
31
+ /** A person or a company: its face in the icon's place — the photo or the initials. */
32
+ avatar?: {
33
+ name: string;
34
+ src?: string;
35
+ };
26
36
  name: string;
27
37
  tags?: ReactNode;
28
38
  meta?: string;
@@ -87,6 +97,9 @@ export type ListSheet<T> = {
87
97
  * convertirlo al tipo de la columna es del dominio, no de acá. */
88
98
  onCommit?: (item: T, column: SheetColumn, columnIndex: number, next: string) => void;
89
99
  editable?: (item: T, column: SheetColumn, columnIndex: number) => boolean;
100
+ /** Present = the sheet adds rows: «add a row» under it, and `Enter` on the last row.
101
+ * The consumer appends the item; the sheet lands on it. Not while grouped. */
102
+ onAddRow?: () => void;
90
103
  };
91
104
  /**
92
105
  * El INFORME de una lista: los períodos y la jerarquía que termina en totales.
@@ -150,6 +163,12 @@ export type ListPivot<T> = {
150
163
  /** Qué pasa al tocar un cruce: abrir las filas que cayeron ahí. */
151
164
  onCell?: (row: PivotKey, column: PivotKey) => void;
152
165
  height?: number | string;
166
+ /** The fields the person may pivot by. With them (and `spec`) the pivot is EXPLORED — axes,
167
+ * measures and download on the grid (`PivotExplorer`) — and its rows follow the search
168
+ * bar's grouping while there is one; `spec.rows` / `spec.columns` are where it starts. */
169
+ fields?: readonly GroupableField[];
170
+ /** Which of `spec.measures` start on. The first one by default. */
171
+ defaultMeasures?: readonly string[];
153
172
  };
154
173
  export type ListViewProps<T extends object> = {
155
174
  items: readonly T[];
@@ -160,6 +179,11 @@ export type ListViewProps<T extends object> = {
160
179
  /** The metrics that also NARROW: «2 por confirmar», «41 por entregar». They go
161
180
  * below the bar and above the rows — they belong to the set, not to the frame. */
162
181
  metrics?: ReactNode;
182
+ /** The list is a FIELD of a record (a one2many / many2many) and not a screen: the bar
183
+ * is one line — search, count, sort, columns, switcher, actions —, it does not stick
184
+ * and it does not move into the frame's header. Identity and crumbs are not drawn: the
185
+ * record around it already says them. See `ListingBar`'s header. */
186
+ embedded?: boolean;
163
187
  /** Which surface the BAR sits on. `canvas` (the default) is a light control bar;
164
188
  * `chrome` turns it into a dark band — an explicit choice, since the application bar
165
189
  * is the screen's one dark band (same as the rail of a record). */
@@ -212,8 +236,20 @@ export type ListViewProps<T extends object> = {
212
236
  /** What gets done to ONE row without entering it, in the table presentation.
213
237
  * They are drawn hidden and appear on the row being pointed at. */
214
238
  rowActions?: (item: T) => readonly RowAction[];
239
+ /** Write a new record as a row of the table presentation — Odoo's editable list. The
240
+ * columns that declare `input` get a control in that row. See `DataTable`'s header.
241
+ * While a record is being written the table shows even if the set is empty. */
242
+ create?: DataTableCreate<T>;
215
243
  /** The board presentation. */
216
244
  card?: (item: T) => ReactNode;
245
+ /** The board by stages: one column each, cards moved between them. */
246
+ boardStages?: BoardStages<T>;
247
+ /** The calendar presentation: when each record happens. */
248
+ calendar?: ListCalendar<T>;
249
+ /** The Gantt presentation: each record as a span on a timeline. */
250
+ gantt?: ListGantt<T>;
251
+ /** The planning presentation: records grouped in rows across time (Odoo's Gantt). */
252
+ timeline?: ListTimeline<T>;
217
253
  /**
218
254
  * La PLANILLA. Con esto, `present="sheet"` dibuja un `SheetTable`.
219
255
  *
@@ -254,10 +290,24 @@ export type ListViewProps<T extends object> = {
254
290
  count: number;
255
291
  clear: () => void;
256
292
  actions?: ReactNode;
293
+ /** Every record that matches, not just the page: «Elegir los 187 que coinciden». The total
294
+ * is the server's; `onSelect` makes the selection mean «all of them». */
295
+ matching?: {
296
+ total: number;
297
+ onSelect: () => void;
298
+ };
257
299
  };
258
300
  onOpen?: (item: T) => void;
259
301
  onPeek?: (item: T) => void;
260
302
  isPeeked?: (item: T) => boolean;
303
+ /** What a press on the row's body does when both ways in exist. `peek` (the default) looks
304
+ * without leaving the list; `open` goes to the record — a workspace whose rule is «a row
305
+ * opens the page» (the composer's) says `open`, and 👁 stays for the look. */
306
+ rowClick?: "peek" | "open";
307
+ /** The list CHOOSES instead of opening — a «Buscar más…», a chooser a workspace asked for:
308
+ * a press on a row (its body, its name, a card) calls this, and the row offers no 👁 / ↗.
309
+ * For several at once, give `selection` too and toggle it here. */
310
+ onChoose?: (item: T) => void;
261
311
  /** The three states, in a single place. Every view used to re-assemble them
262
312
  * with three nested ternaries, and that is why «empty because of a filter»
263
313
  * and «really empty» got mixed up in half the screens. */
@@ -270,3 +320,9 @@ export type ListViewProps<T extends object> = {
270
320
  /** The escape hatch. */
271
321
  children?: ReactNode;
272
322
  };
323
+ /** `List.Calendar`: the calendar's own props; the records, their key and «open» are the list's. */
324
+ export type ListCalendar<T> = Omit<RecordCalendarProps<T>, "items" | "keyOf" | "onOpen">;
325
+ /** `List.Gantt`: the chart's own props; the records, their key and «open» are the list's. */
326
+ export type ListGantt<T> = Omit<RecordGanttProps<T>, "items" | "keyOf" | "onOpen">;
327
+ /** `List.Timeline`: the planning's own props; the records, their key and «open» are the list's. */
328
+ export type ListTimeline<T> = Omit<RecordTimelineProps<T>, "items" | "keyOf" | "onOpen">;
@@ -9,9 +9,19 @@ import { type RefObject } from "react";
9
9
  *
10
10
  * Attach `frame` to the list's root element; `offered` is what the switcher may show and
11
11
  * `present` what to draw (a wide one that was asked for falls back to rows).
12
+ *
13
+ * Measured BEFORE paint: waiting for the observer's first report drew the table and swapped it
14
+ * for rows a frame later, so a click on a row of a list in a panel landed on a detached `<tr>`.
12
15
  */
13
16
  export declare function useNarrowList(presents: readonly ListPresent[] | undefined, requested: ListPresent): {
14
17
  frame: RefObject<HTMLDivElement | null>;
15
18
  offered: readonly ListPresent[];
16
19
  present: ListPresent;
17
20
  };
21
+ /** What a list offers and draws at its width. Only a list that HAS rows folds into them: one
22
+ * that declares just a table fell back to rows it had no `row` for, and drew empty lines — a
23
+ * table that scrolls sideways says more. */
24
+ export declare function narrowed(presents: readonly ListPresent[] | undefined, requested: ListPresent, narrow: boolean): {
25
+ offered: readonly ListPresent[];
26
+ present: ListPresent;
27
+ };
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { HANDHELD_WIDTH } from "../../foundations/layout.js";
3
3
  import { WIDE_PRESENTS } from "./columns.js";
4
- import { useEffect, useRef, useState } from "react";
4
+ import { useLayoutEffect, useRef, useState } from "react";
5
5
  /**
6
6
  * A narrow LIST — a phone, a drawer, a panel among four — does not offer the presentations
7
7
  * that need width: a seven-column table at 360 px is a horizontal scroll nobody reads.
@@ -11,13 +11,19 @@ import { useEffect, useRef, useState } from "react";
11
11
  *
12
12
  * Attach `frame` to the list's root element; `offered` is what the switcher may show and
13
13
  * `present` what to draw (a wide one that was asked for falls back to rows).
14
+ *
15
+ * Measured BEFORE paint: waiting for the observer's first report drew the table and swapped it
16
+ * for rows a frame later, so a click on a row of a list in a panel landed on a detached `<tr>`.
14
17
  */
15
18
  export function useNarrowList(presents, requested) {
16
19
  const frame = useRef(null);
17
20
  const [narrow, setNarrow] = useState(false);
18
- useEffect(() => {
21
+ useLayoutEffect(() => {
19
22
  const el = frame.current;
20
- if (el === null || typeof ResizeObserver === "undefined")
23
+ if (el === null)
24
+ return;
25
+ setNarrow(el.getBoundingClientRect().width < HANDHELD_WIDTH);
26
+ if (typeof ResizeObserver === "undefined")
21
27
  return;
22
28
  const watch = new ResizeObserver(([entry]) => {
23
29
  if (entry !== undefined)
@@ -26,7 +32,15 @@ export function useNarrowList(presents, requested) {
26
32
  watch.observe(el);
27
33
  return () => watch.disconnect();
28
34
  }, []);
29
- const offered = (presents ?? []).filter((p) => !narrow || !WIDE_PRESENTS.includes(p));
30
- const present = narrow && WIDE_PRESENTS.includes(requested) ? "rows" : requested;
31
- return { frame, offered, present };
35
+ return { frame, ...narrowed(presents, requested, narrow) };
36
+ }
37
+ /** What a list offers and draws at its width. Only a list that HAS rows folds into them: one
38
+ * that declares just a table fell back to rows it had no `row` for, and drew empty lines — a
39
+ * table that scrolls sideways says more. */
40
+ export function narrowed(presents, requested, narrow) {
41
+ const folds = narrow && (presents === undefined || presents.includes("rows"));
42
+ return {
43
+ offered: (presents ?? []).filter((p) => !folds || !WIDE_PRESENTS.includes(p)),
44
+ present: folds && WIDE_PRESENTS.includes(requested) ? "rows" : requested,
45
+ };
32
46
  }
@@ -1,12 +1,15 @@
1
+ import { type ActivityProps } from "../../components/activity/Activity.js";
1
2
  import { type SheetField, type SheetLayout } from "../../components/form/Sheet.js";
2
3
  import type { Crumb } from "../../components/record/Breadcrumb.js";
3
4
  import type { DirtyState } from "../../components/record/DirtyActions.js";
5
+ import { type RecordStamp } from "../../components/record/RecordAudit.js";
6
+ import { type RecordNoticeSpec } from "../../components/record/RecordNotices.js";
4
7
  import { RecordSkeleton } from "../../components/record/RecordSkeleton.js";
5
8
  import { type Stage } from "../../components/record/StatusBar.js";
6
9
  import type { Surface, Tone } from "../../foundations/types.js";
7
10
  import { type TabNotice } from "../../primitives/tabs/Tabs.js";
8
11
  import type { LucideIcon } from "lucide-react";
9
- import type { ReactNode } from "react";
12
+ import { type ReactNode } from "react";
10
13
  export type RecordIdentity = {
11
14
  icon?: LucideIcon;
12
15
  title: ReactNode;
@@ -42,6 +45,9 @@ export type RecordPager = {
42
45
  onNext?: () => void;
43
46
  };
44
47
  export type { SheetField };
48
+ /** A notice of the record: the props of `Record.Notice` (a `Callout` without its × label,
49
+ * which the record writes from the copy table). */
50
+ export type RecordNotice = RecordNoticeSpec;
45
51
  type RailProps = {
46
52
  children?: ReactNode;
47
53
  /** The rail's surface. `canvas` (the default) is a light control bar; `chrome` is the dark
@@ -63,6 +69,8 @@ type StatusProps = {
63
69
  /** Cuál de las etapas es la de ahora. Sin esto, la primera. */
64
70
  current?: string;
65
71
  failed?: boolean;
72
+ /** The stages can be chosen: pressing one moves the record there (a CRM's pipeline). */
73
+ onSelect?: (id: string) => void;
66
74
  /** La escotilla del estado, para lo que no es ninguna de las dos. */
67
75
  children?: ReactNode;
68
76
  };
@@ -71,6 +79,9 @@ type PaperProps = {
71
79
  * valor, que es la forma de Odoo. */
72
80
  layout?: SheetLayout;
73
81
  columns?: 1 | 2 | 3 | 4;
82
+ /** `reading` (default): the sheet stops at a reading width, centred, with the rail lined
83
+ * up to it. `full`: the whole page, for a record whose body is mostly a wide table. */
84
+ width?: "reading" | "full";
74
85
  children?: ReactNode;
75
86
  };
76
87
  type FactsProps = {
@@ -95,9 +106,27 @@ type PageProps = {
95
106
  notice?: TabNotice;
96
107
  children?: ReactNode;
97
108
  };
109
+ /** The record's ACTIVITY panel, as DATA: its conversation, its history, its composer.
110
+ * The record draws the `Activity` itself and gives it what only the record has — its
111
+ * notices (the ones that are not blocking), and the indicator in the rail that opens the
112
+ * panel on them.
113
+ *
114
+ * Context: it was `<Record.Chatter><Chatter open onToggle …/></Record.Chatter>`, a node
115
+ * the record could only place. Written as data — like `Record.Status` — the record owns
116
+ * the panel's open state (`defaultOpen`, or `open` + `onOpenChange` to control it), so
117
+ * «⚠ 2 avisos» in the rail can open it and move the focus to its notices. */
118
+ export type RecordActivityProps = Omit<ActivityProps, "open" | "onToggle" | "alerts" | "alertsId"> & {
119
+ /** Starts OPEN unless this says otherwise: on a big screen you see what the record has. */
120
+ defaultOpen?: boolean;
121
+ /** Controls the panel: then `onOpenChange` must follow it. Remembering it between
122
+ * records is the app's — the system only reports. */
123
+ open?: boolean;
124
+ onOpenChange?: (open: boolean) => void;
125
+ };
98
126
  export type RecordProps = {
99
127
  identity: RecordIdentity;
100
- /** Lo que falló al operar sobre el registro. Va arriba del papel, adentro. */
128
+ /** Lo que falló al operar sobre el registro. Va con los avisos que bloquean, primero:
129
+ * entre el riel y el papel, siempre a la vista (ver `Record.Notice`). */
101
130
  error?: string | null;
102
131
  /** Hay cambios sin guardar. Su presencia REEMPLAZA los botones de acción por
103
132
  * guardar y descartar — ver la nota en `RecordHeader`. */
@@ -114,13 +143,17 @@ export declare namespace Record {
114
143
  var Actions: ((props: ActionsProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"actions">;
115
144
  var Status: ((props: StatusProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"status">;
116
145
  var Paper: ((props: PaperProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"paper">;
117
- var Chatter: ((props: {
118
- children?: ReactNode;
119
- /** `page` lo pone SIEMPRE debajo y a todo el ancho, como una hoja propia. Sin
120
- * esto va al costado cuando hay ancho, que es la forma angosta. */
121
- layout?: "aside" | "page";
122
- }) => ReactNode) & import("../../foundations/slots.js").Slotted<"chatter">;
146
+ var Activity: ((props: RecordActivityProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"activity">;
123
147
  var Facts: ((props: FactsProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"facts">;
124
148
  var Pages: ((props: PagesProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"pages">;
125
149
  var Page: ((props: PageProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"page">;
150
+ var Notice: ((props: RecordNoticeSpec) => ReactNode) & import("../../foundations/slots.js").Slotted<"notice">;
151
+ var Ribbon: ((props: {
152
+ label: string;
153
+ tone?: Tone;
154
+ }) => ReactNode) & import("../../foundations/slots.js").Slotted<"ribbon">;
155
+ var Audit: ((props: {
156
+ created?: RecordStamp;
157
+ updated?: RecordStamp;
158
+ }) => ReactNode) & import("../../foundations/slots.js").Slotted<"audit">;
126
159
  }