@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
package/dist/tokens.css CHANGED
@@ -11,6 +11,13 @@
11
11
  @import "@fontsource/ibm-plex-mono/latin-ext-400.css";
12
12
  @import "@fontsource/ibm-plex-mono/latin-500.css";
13
13
  @import "@fontsource/ibm-plex-mono/latin-ext-500.css";
14
+ /* The record calendar (`RecordCalendar`, FullCalendar v7): its structure and the `forma`
15
+ theme. The palette that theme reads is defined below, from web-ui's tokens. */
16
+ @import "@fullcalendar/react/skeleton.css";
17
+ @import "@fullcalendar/react/themes/forma/theme.css";
18
+ /* The record Gantt (`RecordGantt`, SVAR): its styles, Willow theme included. Its variables
19
+ are pointed at web-ui's tokens below. */
20
+ @import "@svar-ui/react-gantt/all.css";
14
21
 
15
22
  @import "tailwindcss";
16
23
 
@@ -491,6 +498,20 @@
491
498
  earlier. */
492
499
  --container-name: 12rem;
493
500
 
501
+ /* The widest a chip of the search bar gets before it truncates. A filter written by hand
502
+ («cliente contiene Importadora y Exportadora…») would otherwise take a whole line of the
503
+ bar and push the rest down; the full text stays in the chip's title. */
504
+ /* One stage of a board by stages: a card with its name, a tag and a figure, without
505
+ wrapping the name into three lines. Five stages fit a desk; more scroll sideways. */
506
+ --container-lane: 18rem;
507
+ /* The names column of a timeline (an employee with an avatar, a room, a machine): wide enough
508
+ for «Ammy Shalem Aranibar Zambrana» before it truncates. */
509
+ --container-timeline-label: 15rem;
510
+ --container-facet: 16rem;
511
+ /* The grouping chain is several levels in one chip, so it asks for more before it
512
+ truncates — at its end, as one sentence, never level by level. */
513
+ --container-chain: 28rem;
514
+
494
515
  /* The minimum width of a metric. It belongs to the system and not to the
495
516
  component: three metrics of different widths read as three different
496
517
  things, and what they have to let you compare is precisely the number. */
@@ -501,13 +522,25 @@
501
522
  —which has to stay clickable— reduced to a useless strip. */
502
523
  --container-peek: 30rem;
503
524
 
504
- /* The width of the chatter, beside a record. Narrower than the peek because it
505
- is not a record: it is a column of one-line entries —«se evaluó el
506
- resultado», «el proyecto pasó a generado»— and 22rem holds one of those
507
- without wrapping while leaving the record the width it needs. A system
508
- decision for the same reason the peek is: two records whose history column
509
- is a different width read as two different screens. */
510
- --container-chatter: 22rem;
525
+ /* THE ACTIVITY COLUMN — at the RIGHT of a record's sheet, joined to it (`Activity`,
526
+ `RecordHeader` THE PAGE). It holds what is true now (the record's
527
+ notices and its fields' and rows' notes, compact) over what happened (the timeline:
528
+ messages, notes and events by time) — both always in sight.
529
+
530
+ `--container-activity` is the column: 22rem holds a notice with its action and a
531
+ message bubble of readable measure.
532
+
533
+ `--container-beside` is how wide the record's PAGE has to be for the column to stand
534
+ beside the sheet: the column and a sheet that keeps 54rem — two groups of fields side by
535
+ side still fit their labels. Narrower (a laptop with a sidebar, a phone, a modal), the
536
+ panel goes BELOW the sheet as a card. Written as a number and not a `calc()` because a container query cannot
537
+ read a variable.
538
+
539
+ `--container-record` is the pair at its widest — the sheet's reading width and the
540
+ column — so on a big screen they are placed together, centred. At 1680 px: 72 + 22. */
541
+ --container-activity: 22rem;
542
+ --container-beside: 76rem;
543
+ --container-record: calc(var(--container-sheet) + var(--container-activity));
511
544
 
512
545
  /* Below this a surface is held in one hand: a phone, or a panel just as narrow. 40rem is
513
546
  the width the list already stops offering a table at (`ListView`), and it is the same
@@ -523,6 +556,12 @@
523
556
  covering the bottom of the screen, which is exactly where the sheet's actions are. */
524
557
  --size-sheet: 90dvh;
525
558
 
559
+ /* How tall a floating list grows before it scrolls inside: about eight two-line rows,
560
+ the height a capped `Picker` already has, so a list that loads page after page from the
561
+ backend opens at the same size as one that shows the first eight. Taller, and a list of
562
+ two thousand customers fills the window and covers the form it is choosing for. */
563
+ --size-dropdown: 20rem;
564
+
526
565
  /* The width of a module's sidebar. 15rem holds an icon, a screen name of two
527
566
  words —«Notas de crédito», «Órdenes de compra»— a count and one step of
528
567
  nesting without truncating, and still leaves a 1280-wide window more than
@@ -583,6 +622,25 @@
583
622
  --duration-panel: 240ms;
584
623
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
585
624
  --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
625
+
626
+ /* The one loop of the system: the bar of a task under way that cannot say how far
627
+ it is (`ProgressBar` without a value). It is not a move between two states — it
628
+ has no end until the task ends — so the 250ms ceiling above does not apply; at
629
+ 1.6s a bar that stays a minute on screen moves slowly enough not to flicker in
630
+ the corner of the eye. The bar uses it behind `motion-safe:`: with reduced
631
+ motion it stands still. */
632
+ --animate-progress-sweep: progress-sweep 1.6s var(--ease-in-out) infinite;
633
+ }
634
+
635
+ /* The segment is two fifths of its track: from just off the left edge (-100 % of
636
+ itself) to just off the right one (250 % of itself = the track's width). */
637
+ @keyframes progress-sweep {
638
+ from {
639
+ translate: -100%;
640
+ }
641
+ to {
642
+ translate: 250%;
643
+ }
586
644
  }
587
645
 
588
646
  /* ==================================================================== *
@@ -729,6 +787,113 @@
729
787
  0 2px 6px -2px rgba(26, 23, 20, 0.18), 0 18px 40px -20px rgba(26, 23, 20, 0.42);
730
788
  }
731
789
 
790
+ /* ====================================================================
791
+ * THE WORKSPACE (@sincpro/web-composer) dressed in these tokens.
792
+ *
793
+ * Context: the composer depends on no design system and paints its frames, tabs, windows
794
+ * and modals with `--sc-*` variables. Pointing them at web-ui's tokens here means a composer
795
+ * on a web-ui page takes its theme — light, dark, and any brand — with nothing to wire: no
796
+ * import, no dependency, only names. They are aliases, so they follow the dark theme on
797
+ * their own and carry no contrast of their own to measure.
798
+ * ==================================================================== */
799
+ :root {
800
+ --sc-ground: var(--color-ground);
801
+ --sc-surface: var(--color-surface);
802
+ --sc-line: var(--color-line);
803
+ --sc-line-strong: var(--color-line-strong);
804
+ --sc-ink: var(--color-ink);
805
+ --sc-muted: var(--color-muted);
806
+ /* The workspace's accent marks where you are (the active tab, a focused frame): the navy
807
+ of a link, not the gold of the one primary action. */
808
+ --sc-accent: var(--color-primary);
809
+ --sc-danger: var(--color-danger);
810
+ --sc-on-danger: var(--color-surface);
811
+ --sc-scrim: var(--color-scrim);
812
+ --sc-modal-scrim: color-mix(in srgb, var(--color-scrim) 40%, transparent);
813
+ --sc-shadow: var(--shadow-window);
814
+ --sc-radius: var(--radius-panel);
815
+ --sc-radius-control: var(--radius-control);
816
+ --sc-text: var(--text-meta);
817
+ --sc-text-small: var(--text-micro);
818
+ --sc-icon: var(--size-icon-md);
819
+ }
820
+
821
+ /* ====================================================================
822
+ * THE RECORD CALENDAR (FullCalendar v7, `forma` theme) in these tokens.
823
+ *
824
+ * Context: the theme reads a palette of `--fc-forma-*` variables. Pointed at web-ui's tokens it
825
+ * takes the theme — light, dark, a brand — with no dark block of its own: the tokens already
826
+ * flip. Aliases, so no contrast of their own to measure; the translucent washes are mixed from
827
+ * the ink so they read on either theme.
828
+ * ==================================================================== */
829
+ :root {
830
+ --fc-forma-primary: var(--color-primary);
831
+ --fc-forma-primary-over: var(--color-primary-hover);
832
+ --fc-forma-primary-down: var(--color-primary-hover);
833
+ --fc-forma-primary-foreground: var(--color-surface);
834
+ --fc-forma-event: var(--color-primary);
835
+ --fc-forma-event-contrast: var(--color-surface);
836
+ --fc-forma-background-event: var(--color-primary);
837
+ --fc-forma-highlight: color-mix(in srgb, var(--color-primary) 18%, transparent);
838
+ --fc-forma-background: var(--color-surface);
839
+ --fc-forma-faint: color-mix(in srgb, var(--color-ink) 3%, transparent);
840
+ --fc-forma-muted: color-mix(in srgb, var(--color-ink) 7%, transparent);
841
+ --fc-forma-strong: color-mix(in srgb, var(--color-ink) 12%, transparent);
842
+ --fc-forma-stronger: color-mix(in srgb, var(--color-ink) 16%, transparent);
843
+ --fc-forma-strongest: color-mix(in srgb, var(--color-ink) 20%, transparent);
844
+ --fc-forma-foreground: var(--color-ink);
845
+ --fc-forma-faint-foreground: var(--color-muted);
846
+ --fc-forma-muted-foreground: var(--color-muted);
847
+ --fc-forma-border: var(--color-line);
848
+ --fc-forma-muted-border: var(--color-line);
849
+ --fc-forma-strong-border: var(--color-line-strong);
850
+ }
851
+
852
+ /* ====================================================================
853
+ * THE RECORD GANTT (SVAR, Willow theme) in these tokens.
854
+ *
855
+ * Context: Willow declares its `--wx-*` on `.wx-willow-theme`; redeclared on the same class
856
+ * here, later, they win. Bars are the navy of the system, a summary the ink, a milestone the
857
+ * info tone; the type is the system's. Aliases: they flip with the theme.
858
+ * ==================================================================== */
859
+ .wx-willow-theme {
860
+ --wx-color-primary: var(--color-primary);
861
+ --wx-color-primary-selected: color-mix(in srgb, var(--color-primary) 14%, transparent);
862
+ --wx-color-primary-font: var(--color-surface);
863
+ --wx-color-secondary-hover: color-mix(in srgb, var(--color-primary) 10%, transparent);
864
+ --wx-color-success: var(--color-good);
865
+ --wx-color-warning: var(--color-warn);
866
+ --wx-color-info: var(--color-info);
867
+ --wx-color-danger: var(--color-danger);
868
+ --wx-color-font: var(--color-ink);
869
+ --wx-color-font-alt: var(--color-muted);
870
+ --wx-background: var(--color-surface);
871
+ --wx-background-alt: var(--color-ground);
872
+ --wx-background-hover: color-mix(in srgb, var(--color-ink) 5%, transparent);
873
+ --wx-font-family: inherit;
874
+ --wx-font-size: var(--text-meta);
875
+ --wx-font-size-sm: var(--text-micro);
876
+ --wx-icon-color: var(--color-muted);
877
+ --wx-border-color: var(--color-line);
878
+ --wx-border-radius: var(--radius-control);
879
+ --wx-radius-major: var(--radius-box);
880
+ --wx-gantt-border-color: var(--color-line);
881
+ --wx-gantt-task-color: var(--color-primary);
882
+ --wx-gantt-task-fill-color: var(--color-primary-hover);
883
+ --wx-gantt-task-border-color: var(--color-primary-hover);
884
+ --wx-gantt-task-font-color: var(--color-surface);
885
+ --wx-gantt-summary-color: var(--color-line-strong);
886
+ --wx-gantt-summary-fill-color: color-mix(in srgb, var(--color-ink) 45%, transparent);
887
+ --wx-gantt-summary-border-color: var(--color-line-strong);
888
+ --wx-gantt-summary-font-color: var(--color-ink);
889
+ --wx-gantt-milestone-color: var(--color-info);
890
+ --wx-gantt-select-color: color-mix(in srgb, var(--color-primary) 10%, transparent);
891
+ --wx-gantt-link-color: var(--color-muted);
892
+ --wx-gantt-link-color-hovered: var(--color-ink);
893
+ --wx-gantt-holiday-background: var(--color-ground);
894
+ --wx-gantt-holiday-color: var(--color-muted);
895
+ }
896
+
732
897
  @layer base {
733
898
  /* No forced height anywhere: the document is exactly as tall as its content.
734
899
  * The color still has to reach the bottom of the viewport on a short page —
@@ -1023,3 +1188,10 @@
1023
1188
  --size-control-lg: 48px;
1024
1189
  }
1025
1190
  }
1191
+
1192
+ /* A label written down a narrow strip: the name of a folded board column, read bottom-up
1193
+ like a book's spine. Tailwind has no writing-mode utility. */
1194
+ @utility text-vertical {
1195
+ writing-mode: vertical-rl;
1196
+ transform: rotate(180deg);
1197
+ }
@@ -1,10 +1,11 @@
1
+ import type { BoardStages } from "../../components/listing/BoardColumns.js";
1
2
  import type { ListIdentity } from "../../components/listing/ListingBar.js";
2
3
  import type { SortField } from "../../components/listing/SortMenu.js";
3
4
  import type { Surface } from "../../foundations/types.js";
4
- import type { DataColumn } from "../../primitives/table/DataTable.js";
5
+ import type { DataColumn, DataTableCreate } from "../../primitives/table/DataTable.js";
5
6
  import type { RowAction } from "../../primitives/table/RowActions.js";
6
7
  import type { SortState } from "../../primitives/table/Table.js";
7
- import { type ListCrumb, type ListEmpty, type ListGroup, type ListPivot, type ListPresent, type ListReport, type ListSheet, type RowOf } from "./types.js";
8
+ import { type ListCalendar, type ListCrumb, type ListEmpty, type ListGantt, type ListGroup, type ListPivot, type ListPresent, type ListReport, type ListSheet, type ListTimeline, type RowOf } from "./types.js";
8
9
  import type { ReactNode } from "react";
9
10
  type BarProps = {
10
11
  children?: ReactNode;
@@ -41,12 +42,17 @@ type TableProps<T extends object> = {
41
42
  /** Lo que se le hace a UNA fila sin entrar. Se dibujan escondidas y aparecen
42
43
  * sobre la fila que se está apuntando. */
43
44
  rowActions?: (item: T) => readonly RowAction[];
45
+ /** Escribir un registro nuevo como una fila más de la tabla (la lista editable de
46
+ * Odoo). Ver la cabecera de `DataTable`. */
47
+ create?: DataTableCreate<T>;
44
48
  };
45
49
  /** El tablero. Sin `card`, la tarjeta sale de lo que ya dice `List.Rows`: el
46
50
  * nombre, el `meta` y las fichas. Escribirla igual es declarar que ESTA lista
47
51
  * se puede ver como tablero. */
48
52
  type BoardProps<T> = {
49
53
  card?: (item: T) => ReactNode;
54
+ /** One column per stage, with drag (and Alt + ← / →) to move a card between them. */
55
+ stages?: BoardStages<T>;
50
56
  };
51
57
  /** La planilla. Sin columnas propias, sale de las que declaró `List.Table` — la
52
58
  * de datos CRUDOS sí las trae, porque ahí salen del archivo y el código no sabe
@@ -55,6 +61,9 @@ type SheetProps<T> = Partial<ListSheet<T>>;
55
61
  export type ListProps<T extends object> = {
56
62
  items: readonly T[];
57
63
  keyOf: (item: T) => string;
64
+ /** The list is a FIELD of a record — a one2many / many2many — and not a screen: a
65
+ * one-line bar that does not stick. See «A list INSIDE a record» above. */
66
+ embedded?: boolean;
58
67
  /** En qué superficie se apoya la BARRA. `canvas` (el default) es una barra
59
68
  * de control clara; `chrome` la convierte en una banda oscura, una opción
60
69
  * explícita: la barra de la aplicación es la única banda oscura de la
@@ -69,6 +78,11 @@ export type ListProps<T extends object> = {
69
78
  count: number;
70
79
  clear: () => void;
71
80
  actions?: ReactNode;
81
+ /** Every record that matches, not just the page — see `ListViewProps`. */
82
+ matching?: {
83
+ total: number;
84
+ onSelect: () => void;
85
+ };
72
86
  };
73
87
  /** El orden, que también es del conjunto. */
74
88
  sort?: SortState | null;
@@ -82,6 +96,18 @@ export type ListProps<T extends object> = {
82
96
  onOpen?: (item: T) => void;
83
97
  onPeek?: (item: T) => void;
84
98
  isPeeked?: (item: T) => boolean;
99
+ /** What a press on the row's body does when both ways in exist. `peek` (the default) looks
100
+ * without leaving the list; `open` goes to the record — a workspace whose rule is «a row
101
+ * opens the page» (the composer's) says `open`, and 👁 stays for the look. */
102
+ rowClick?: "peek" | "open";
103
+ /** The list CHOOSES instead of opening — a «Buscar más…», a chooser a workspace asked for:
104
+ * a press on a row (its body, its name, a card) calls this, and the row offers no 👁 / ↗.
105
+ * For several at once, give `selection` too and toggle it here. */
106
+ onChoose?: (item: T) => void;
107
+ /** Nothing in it can be acted on: no opening, peeking, selecting, creating nor row
108
+ * actions — a list to read, like the lines of a record in view mode. One switch, so whoever
109
+ * passes the handlers generically does not have to strip them one by one. */
110
+ readOnly?: boolean;
85
111
  /** Los tres estados, en un solo lugar. */
86
112
  state?: {
87
113
  loading?: boolean;
@@ -93,7 +119,7 @@ export type ListProps<T extends object> = {
93
119
  empty?: ListEmpty;
94
120
  children?: ReactNode;
95
121
  };
96
- export declare function List<T extends object>({ items, keyOf, surface, group, selection, sort, onSortChange, sortable, present, onPresentChange, onOpen, onPeek, isPeeked, state, filtered, empty, children, }: ListProps<T>): import("react").JSX.Element;
122
+ export declare function List<T extends object>({ items, keyOf, embedded, surface, group, selection: selectWith, sort, onSortChange, sortable, present, onPresentChange, onOpen: openWith, onPeek: peekWith, isPeeked, readOnly, rowClick, onChoose, state, filtered, empty, children, }: ListProps<T>): import("react").JSX.Element;
97
123
  export declare namespace List {
98
124
  var Bar: ((props: BarProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"bar">;
99
125
  var Crumbs: ((props: CrumbsProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"crumbs">;
@@ -109,5 +135,8 @@ export declare namespace List {
109
135
  var Sheet: <T>(props: SheetProps<T>) => ReactNode;
110
136
  var Report: ((props: ListReport) => ReactNode) & import("../../foundations/slots.js").Slotted<"report">;
111
137
  var Pivot: <T>(props: ListPivot<T>) => ReactNode;
138
+ var Calendar: <T>(props: ListCalendar<T>) => ReactNode;
139
+ var Gantt: <T>(props: ListGantt<T>) => ReactNode;
140
+ var Timeline: <T>(props: ListTimeline<T>) => ReactNode;
112
141
  }
113
142
  export {};
@@ -22,6 +22,9 @@ const Views = slot("views", (_props) => null);
22
22
  * arriba de las filas, porque son del conjunto y no del marco. */
23
23
  const Metrics = slot("metrics", (_props) => null);
24
24
  const Rows = slot("rows", (_props) => null);
25
+ /* Through `unknown`: `create` both receives and hands back a `T` (`onChange`, `draft`),
26
+ so `TableProps` is invariant in `T` and `TableProps<never>` no longer narrows to it. The
27
+ slot only carries the props; `List` reads them back typed against the list's `T`. */
25
28
  const TableSlot = slot("table", (_props) => null);
26
29
  const Board = slot("board", (_props) => null);
27
30
  const SheetSlot = slot("sheet", (_props) => null);
@@ -37,10 +40,32 @@ const PRESENT_OF = {
37
40
  sheet: "sheet",
38
41
  report: "report",
39
42
  pivot: "pivot",
43
+ calendar: "calendar",
44
+ gantt: "gantt",
45
+ timeline: "timeline",
40
46
  };
41
- const FACES = ["rows", "table", "board", "sheet", "report", "pivot"];
42
- export function List({ items, keyOf, surface, group, selection, sort, onSortChange, sortable, present, onPresentChange, onOpen, onPeek, isPeeked, state, filtered, empty, children, }) {
47
+ const FACES = [
48
+ "rows",
49
+ "table",
50
+ "board",
51
+ "sheet",
52
+ "report",
53
+ "pivot",
54
+ "calendar",
55
+ "gantt",
56
+ "timeline",
57
+ ];
58
+ /** Records grouped in rows across time, N per row — a planning (`RecordTimeline`). */
59
+ const TimelineSlot = slot("timeline", (_props) => null);
60
+ /** When each record happens, on a month, a week, a day or an agenda (`RecordCalendar`). */
61
+ const CalendarSlot = slot("calendar", (_props) => null);
62
+ /** Each record as a span on a timeline, with its dependencies (`RecordGantt`). */
63
+ const GanttSlot = slot("gantt", (_props) => null);
64
+ export function List({ items, keyOf, embedded, surface, group, selection: selectWith, sort, onSortChange, sortable, present, onPresentChange, onOpen: openWith, onPeek: peekWith, isPeeked, readOnly = false, rowClick, onChoose, state, filtered, empty, children, }) {
43
65
  const top = flatten(children);
66
+ const onOpen = readOnly ? undefined : openWith;
67
+ const onPeek = readOnly ? undefined : peekWith;
68
+ const selection = readOnly ? undefined : selectWith;
44
69
  const bar = flatten(propsOf(pick(top, "bar"))?.children);
45
70
  const crumbs = propsOf(pick(bar, "crumbs"))?.crumbs;
46
71
  const identity = propsOf(pick(bar, "identity"));
@@ -55,6 +80,9 @@ export function List({ items, keyOf, surface, group, selection, sort, onSortChan
55
80
  const sheet = propsOf(pick(top, "sheet"));
56
81
  const report = propsOf(pick(top, "report"));
57
82
  const pivot = propsOf(pick(top, "pivot"));
83
+ const calendar = propsOf(pick(top, "calendar"));
84
+ const gantt = propsOf(pick(top, "gantt"));
85
+ const timeline = propsOf(pick(top, "timeline"));
58
86
  /* Las caras que hay son las que están escritas, EN ESE ORDEN. El conmutador
59
87
  no puede ofrecer una que no se pueda dibujar, porque la que se dibuja y la
60
88
  que se ofrece son el mismo nodo. */
@@ -65,21 +93,34 @@ export function List({ items, keyOf, surface, group, selection, sort, onSortChan
65
93
  /* La escotilla: lo que el arquetipo no previó se dibuja igual, debajo. Que
66
94
  aparezca algo acá es la señal de que al arquetipo le falta una pieza. */
67
95
  const rest = loose(top, ["bar", "metrics", ...FACES]);
68
- return (_jsx(ListView, { items: items, keyOf: keyOf, row: rows?.row ?? (() => ({ name: "" })), ...(rows?.header === undefined ? {} : { rowHeader: rows.header }), ...(identity === undefined ? {} : { identity }), ...(crumbs === undefined ? {} : { crumbs }), ...(surface === undefined ? {} : { surface }), ...(search === undefined ? {} : { search }), ...(actions === undefined ? {} : { actions }), ...(pagination === undefined ? {} : { pagination }), ...(views === undefined ? {} : { views }), ...(metrics === undefined ? {} : { metrics }), ...(table === undefined
96
+ return (_jsx(ListView, { items: items, keyOf: keyOf, row: rows === undefined
97
+ ? () => ({ name: "" })
98
+ : readOnly
99
+ ? (item) => {
100
+ const { actions: _actions, ...rest } = rows.row(item);
101
+ return rest;
102
+ }
103
+ : rows.row, ...(rows?.header === undefined ? {} : { rowHeader: rows.header }), ...(identity === undefined ? {} : { identity }), ...(crumbs === undefined ? {} : { crumbs }), ...(embedded === undefined ? {} : { embedded }), ...(surface === undefined ? {} : { surface }), ...(search === undefined ? {} : { search }), ...(actions === undefined ? {} : { actions }), ...(pagination === undefined ? {} : { pagination }), ...(views === undefined ? {} : { views }), ...(metrics === undefined ? {} : { metrics }), ...(table === undefined
69
104
  ? {}
70
105
  : {
71
- columns: table.columns,
106
+ /* Read-only: a cell that opens an editor is an action too. */
107
+ columns: readOnly
108
+ ? table.columns.map(({ edit: _edit, ...c }) => c)
109
+ : table.columns,
72
110
  ...(table.totals === undefined ? {} : { totals: table.totals }),
73
- ...(table.rowActions === undefined ? {} : { rowActions: table.rowActions }),
111
+ ...(table.rowActions === undefined || readOnly
112
+ ? {}
113
+ : { rowActions: table.rowActions }),
114
+ ...(table.create === undefined || readOnly ? {} : { create: table.create }),
74
115
  ...(table.visibleColumnIds === undefined
75
116
  ? {}
76
117
  : { visibleColumnIds: table.visibleColumnIds }),
77
118
  ...(table.onVisibleColumnIdsChange === undefined
78
119
  ? {}
79
120
  : { onVisibleColumnIdsChange: table.onVisibleColumnIdsChange }),
80
- }), ...(board?.card === undefined ? {} : { card: board.card }), ...(sheet?.columns === undefined || sheet.cell === undefined
121
+ }), ...(board?.card === undefined ? {} : { card: board.card }), ...(board?.stages === undefined ? {} : { boardStages: board.stages }), ...(sheet?.columns === undefined || sheet.cell === undefined
81
122
  ? {}
82
- : { sheet: sheet }), ...(report === undefined ? {} : { report }), ...(pivot === undefined ? {} : { pivot }), ...(presents.length === 0 ? {} : { presents }), ...(present === undefined ? {} : { present }), ...(onPresentChange === undefined ? {} : { onPresentChange }), ...(group === undefined ? {} : { group }), ...(selection === undefined ? {} : { selection }), ...(sort === undefined ? {} : { sort }), ...(onSortChange === undefined ? {} : { onSortChange }), ...(sortable === undefined ? {} : { sortable }), ...(onOpen === undefined ? {} : { onOpen }), ...(onPeek === undefined ? {} : { onPeek }), ...(isPeeked === undefined ? {} : { isPeeked }), ...(state === undefined ? {} : { state }), ...(filtered === undefined ? {} : { filtered }), ...(empty === undefined ? {} : { empty }), children: rest.length === 0 ? undefined : rest }));
123
+ : { sheet: sheet }), ...(report === undefined ? {} : { report }), ...(pivot === undefined ? {} : { pivot }), ...(calendar === undefined ? {} : { calendar }), ...(gantt === undefined ? {} : { gantt }), ...(timeline === undefined ? {} : { timeline }), ...(presents.length === 0 ? {} : { presents }), ...(present === undefined ? {} : { present }), ...(onPresentChange === undefined ? {} : { onPresentChange }), ...(group === undefined ? {} : { group }), ...(selection === undefined ? {} : { selection }), ...(sort === undefined ? {} : { sort }), ...(onSortChange === undefined ? {} : { onSortChange }), ...(sortable === undefined ? {} : { sortable }), ...(onOpen === undefined ? {} : { onOpen }), ...(onPeek === undefined ? {} : { onPeek }), ...(isPeeked === undefined ? {} : { isPeeked }), ...(rowClick === undefined ? {} : { rowClick }), ...(onChoose === undefined || readOnly ? {} : { onChoose }), ...(state === undefined ? {} : { state }), ...(filtered === undefined ? {} : { filtered }), ...(empty === undefined ? {} : { empty }), children: rest.length === 0 ? undefined : rest }));
83
124
  }
84
125
  List.Bar = Bar;
85
126
  List.Crumbs = Crumbs;
@@ -95,3 +136,6 @@ List.Board = Board;
95
136
  List.Sheet = SheetSlot;
96
137
  List.Report = Report;
97
138
  List.Pivot = Pivot;
139
+ List.Calendar = CalendarSlot;
140
+ List.Gantt = GanttSlot;
141
+ List.Timeline = TimelineSlot;
@@ -1,5 +1,5 @@
1
1
  import type { ListViewProps } from "./types.js";
2
- export declare function ListView<T extends object>({ items, keyOf, identity, crumbs, surface, metrics, search, pagination, views, actions, row, rowHeader, columns, visibleColumnIds, onVisibleColumnIdsChange, card, sheet, report, pivot, present: requested, presents, onPresentChange, sort, onSortChange, sortable, totals, rowActions, group, selection, onOpen, onPeek, state, filtered, empty, children, }: ListViewProps<T>): import("react").JSX.Element;
2
+ export declare function ListView<T extends object>({ items, keyOf, identity, crumbs, embedded, surface: requestedSurface, metrics, search, pagination, views, actions, row, rowHeader, columns, visibleColumnIds, onVisibleColumnIdsChange, card, boardStages, calendar, gantt, timeline, sheet, report, pivot, present: requested, presents, onPresentChange, sort, onSortChange, sortable, totals, rowActions, create, group, selection, onOpen, onPeek, rowClick, onChoose, state, filtered, empty, children, }: ListViewProps<T>): import("react").JSX.Element;
3
3
  /** What a list offers when it fails and can be retried. */
4
4
  export declare function RetryAction({ onRetry }: {
5
5
  onRetry: () => void;
@@ -1,14 +1,19 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { RecordCalendar } from "../../components/calendar/RecordCalendar.js";
4
+ import { RecordGantt } from "../../components/gantt/RecordGantt.js";
5
+ import { BoardColumns } from "../../components/listing/BoardColumns.js";
3
6
  import { useColumnControl, visibleColumns } from "../../components/listing/columns.js";
4
7
  import { ColumnsMenu, specsFromColumns, } from "../../components/listing/ColumnsMenu.js";
5
8
  import { Listing } from "../../components/listing/Listing.js";
6
9
  import { ListingBar } from "../../components/listing/ListingBar.js";
10
+ import { PivotExplorer } from "../../components/listing/PivotExplorer.js";
7
11
  import { RecordCard } from "../../components/listing/RecordCard.js";
8
12
  import { GroupHeader, Row, RowHeader } from "../../components/listing/Row.js";
9
13
  import { sortItems, SortMenu, } from "../../components/listing/SortMenu.js";
10
14
  import { ViewSwitcher } from "../../components/listing/ViewSwitcher.js";
11
15
  import { Breadcrumb } from "../../components/record/Breadcrumb.js";
16
+ import { RecordTimeline } from "../../components/timeline/RecordTimeline.js";
12
17
  import { useUiCopy } from "../../foundations/copy.js";
13
18
  import { useDensity } from "../../foundations/density.js";
14
19
  import { pathKey, pileIsOpen, pilesOf, } from "../../foundations/grouping.js";
@@ -18,10 +23,10 @@ import { Empty, Failed, Skeleton } from "../../primitives/notice/Notice.js";
18
23
  import { PivotTable } from "../../primitives/table/PivotTable.js";
19
24
  import { ReportTable } from "../../primitives/table/ReportTable.js";
20
25
  import { groupedColumnIds } from "./columns.js";
21
- import { groupLevelsOf, reportRowsOf, sheetFromColumns, sheetRowsOf, } from "./derive.js";
26
+ import { groupLevelsOf, reportColumnsOf, reportRowsOf, sheetFromColumns, sheetRowsOf, } from "./derive.js";
22
27
  import { SheetPresentation, TablePresentation, } from "./presentations.js";
23
28
  import { useNarrowList } from "./useNarrowList.js";
24
- import { Grid3x3, LayoutGrid, List, Sheet, Sigma, Table2 } from "lucide-react";
29
+ import { CalendarDays, CalendarRange, ChartGantt, Grid3x3, LayoutGrid, List, Sheet, Sigma, Table2, } from "lucide-react";
25
30
  import { Fragment, useCallback, useMemo } from "react";
26
31
  /**
27
32
  * EL DIBUJO de una lista. **La puerta es `List`**, que se escribe como árbol y
@@ -57,9 +62,15 @@ const PRESENT_ICONS = [
57
62
  { id: "report", icon: Sigma },
58
63
  /* La cuadrícula: dos ejes, y lo que importa está adentro. */
59
64
  { id: "pivot", icon: Grid3x3 },
65
+ { id: "calendar", icon: CalendarDays },
66
+ { id: "gantt", icon: ChartGantt },
67
+ { id: "timeline", icon: CalendarRange },
60
68
  ];
61
- export function ListView({ items, keyOf, identity, crumbs, surface, metrics, search, pagination, views, actions, row, rowHeader, columns, visibleColumnIds, onVisibleColumnIdsChange, card, sheet, report, pivot, present: requested = "rows", presents, onPresentChange, sort, onSortChange, sortable, totals = false, rowActions, group, selection, onOpen, onPeek, state, filtered = false, empty, children, }) {
69
+ export function ListView({ items, keyOf, identity, crumbs, embedded = false, surface: requestedSurface, metrics, search, pagination, views, actions, row, rowHeader, columns, visibleColumnIds, onVisibleColumnIdsChange, card, boardStages, calendar, gantt, timeline, sheet, report, pivot, present: requested = "rows", presents, onPresentChange, sort, onSortChange, sortable, totals = false, rowActions, create, group, selection, onOpen, onPeek, rowClick = "peek", onChoose, state, filtered = false, empty, children, }) {
62
70
  const { frame, offered, present } = useNarrowList(presents, requested);
71
+ /* Embedded, the list sits on the record's paper: there is no dark band to inherit, and a
72
+ navy switcher inside a white sheet would be a second chrome on the screen. */
73
+ const surface = embedded ? undefined : requestedSurface;
63
74
  const density = useDensity();
64
75
  const copy = useUiCopy();
65
76
  const loading = state?.loading === true;
@@ -128,7 +139,16 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
128
139
  present === "pivot" ? null : (_jsx(SortMenu, { fields: sortable, sort: sort ?? null, onSortChange: (next) => onSortChange?.(next), ...(surface === undefined ? {} : { surface }) }));
129
140
  const rightViews = views ?? switcher;
130
141
  const viewsSlot = columnsMenu === null && sortMenu === null && rightViews === undefined ? undefined : (_jsxs(_Fragment, { children: [sortMenu, columnsMenu, rightViews] }));
131
- const bar = (_jsx(ListingBar, { surface: surface, ...(crumbs === undefined || crumbs.length === 0 || density === "compact"
142
+ /* Embedded, the line is drawn only if it has something to OFFER: a count alone repeats
143
+ what the rows (and the footer) already say. */
144
+ const barHasControls = !embedded ||
145
+ identity?.title !== undefined ||
146
+ search !== undefined ||
147
+ viewsSlot !== undefined ||
148
+ actions !== undefined ||
149
+ pagination !== undefined ||
150
+ (selection?.count ?? 0) > 0;
151
+ const bar = !barHasControls ? undefined : (_jsx(ListingBar, { surface: surface, ...(embedded ? { inline: true, count: items.length } : {}), ...(embedded || crumbs === undefined || crumbs.length === 0 || density === "compact"
132
152
  ? {}
133
153
  : {
134
154
  crumbs: (_jsx(Breadcrumb, { crumbs: crumbs, ...(surface === undefined ? {} : { surface }) })),
@@ -139,12 +159,26 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
139
159
  count: selection.count,
140
160
  onClear: selection.clear,
141
161
  ...(selection.actions === undefined ? {} : { actions: selection.actions }),
162
+ ...(selection.matching === undefined ? {} : { matching: selection.matching }),
142
163
  },
143
164
  }), ...(search === undefined ? {} : { search }), ...(pagination === undefined ? {} : { pagination }), ...(viewsSlot === undefined ? {} : { views: viewsSlot }) }));
165
+ /* The body of a row, a card: the look first unless the list says the page. */
166
+ /* Choosing, the press chooses and the ways in (👁 ↗) are not offered: a chooser's row answers. */
167
+ const choosing = onChoose !== undefined;
168
+ const press = choosing
169
+ ? onChoose
170
+ : rowClick === "open"
171
+ ? (onOpen ?? onPeek)
172
+ : (onPeek ?? onOpen);
144
173
  const renderRow = (item) => {
145
174
  const d = row(item);
146
175
  const id = keyOf(item);
147
- return (_jsx(Row, { ...(d.icon === undefined ? {} : { icon: d.icon }), name: d.name, ...(d.tags === undefined ? {} : { tags: d.tags }), ...(d.meta === undefined ? {} : { meta: d.meta }), ...(d.actions === undefined ? {} : { actions: d.actions }), ...(onOpen === undefined ? {} : { onOpen: () => onOpen(item) }), ...(onPeek === undefined ? {} : { onPeek: () => onPeek(item) }), ...(selection === undefined
176
+ return (_jsx(Row, { ...(d.icon === undefined ? {} : { icon: d.icon }), ...(d.avatar === undefined ? {} : { avatar: d.avatar }), name: d.name, ...(d.tags === undefined ? {} : { tags: d.tags }), ...(d.meta === undefined ? {} : { meta: d.meta }), ...(d.actions === undefined ? {} : { actions: d.actions }), ...(choosing
177
+ ? { onOpen: () => onChoose(item), openers: false }
178
+ : {
179
+ ...(onOpen === undefined ? {} : { onOpen: () => onOpen(item) }),
180
+ ...(onPeek === undefined ? {} : { onPeek: () => onPeek(item) }),
181
+ }), click: rowClick, ...(selection === undefined
148
182
  ? {}
149
183
  : { selected: selection.has(id), onSelectedChange: () => selection.toggle(id) }), children: d.children }, id));
150
184
  };
@@ -183,7 +217,10 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
183
217
  error === null &&
184
218
  (present === "pivot" && pivotShown !== undefined
185
219
  ? pivotIsEmpty(pivotShown)
186
- : items.length === 0);
220
+ : items.length === 0) &&
221
+ /* The first record of an empty list is written in the table, not under a sentence
222
+ saying there is nothing. */
223
+ !(present === "table" && create?.draft != null);
187
224
  /* La única línea que hace agrupable a una vista: el árbol que le pasaron, o el
188
225
  que se arma con las filas en la mano. */
189
226
  const piles = useMemo(() => pilesOf(ordered, group), [group, ordered]);
@@ -221,11 +258,11 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
221
258
  return _jsx(Fragment, { children: card(item) }, keyOf(item));
222
259
  }
223
260
  const d = row(item);
224
- return (_jsx(RecordCard, { title: d.name, ...(d.tags === undefined ? {} : { tags: d.tags }), ...(d.meta === undefined ? {} : { meta: d.meta }), ...(onOpen === undefined ? {} : { onOpen: () => onOpen(item) }) }, keyOf(item)));
261
+ return (_jsx(RecordCard, { title: d.name, ...(d.tags === undefined ? {} : { tags: d.tags }), ...(d.meta === undefined ? {} : { meta: d.meta }), ...(press === undefined ? {} : { onOpen: () => press(item) }) }, keyOf(item)));
225
262
  };
226
263
  const body = present === "table" && shownColumns !== undefined ? (
227
264
  /* The table receives EVERYTHING that belongs to the set: see `TablePresentation`. */
228
- _jsx(TablePresentation, { items: items, columns: shownColumns, keyOf: keyOf, levels: groupLevels, ...(sort === undefined ? {} : { sort }), ...(onSortChange === undefined ? {} : { onSortChange }), ...(group === undefined ? {} : { group }), ...(selection === undefined ? {} : { selection }), totals: totals, ...(rowActions === undefined ? {} : { rowActions }), ...(onPeek === undefined ? {} : { onPeek }), ...(onOpen === undefined ? {} : { onOpen }) })) : present === "sheet" && sheetSpec !== undefined ? (
265
+ _jsx(TablePresentation, { items: items, columns: shownColumns, keyOf: keyOf, levels: groupLevels, ...(sort === undefined ? {} : { sort }), ...(onSortChange === undefined ? {} : { onSortChange }), ...(group === undefined ? {} : { group }), ...(selection === undefined ? {} : { selection }), totals: totals, ...(rowActions === undefined ? {} : { rowActions }), ...(create === undefined ? {} : { create }), ...(onPeek === undefined || choosing ? {} : { onPeek }), ...(onOpen === undefined || choosing ? {} : { onOpen }), ...(choosing ? { onChoose } : {}), rowClick: rowClick })) : present === "sheet" && sheetSpec !== undefined ? (
229
266
  /* La planilla recibe el conjunto por ÍNDICE y no una matriz ya armada: ver `SheetPresentation`. */
230
267
  _jsx(SheetPresentation, { spec: sheetSpec, entries: sheetRows, ordered: ordered, ...(group === undefined ? {} : { group }) })) : present === "report" && report !== undefined ? (
231
268
  /* El informe declarado por el dominio no se ORDENA desde la barra: el
@@ -233,28 +270,48 @@ export function ListView({ items, keyOf, identity, crumbs, surface, metrics, sea
233
270
  Activos» en el medio. Pero sí se AGRUPA cuando las líneas no vienen
234
271
  declaradas: ahí el informe es este conjunto contado por los niveles que
235
272
  estén puestos. */
236
- _jsx(ReportTable, { columns: report.columns, rows: report.rows ?? reportRows, ...(report.defaultOpen === undefined ? {} : { defaultOpen: report.defaultOpen }), ...(report.height === undefined ? {} : { height: report.height }) })) : present === "pivot" && pivotShown !== undefined ? (
273
+ _jsx(ReportTable
274
+ /* Built from the piles, the numbers are the list's columns' — and so is how they
275
+ are written. Declared by the domain, the columns are the domain's. */
276
+ , {
277
+ /* Built from the piles, the numbers are the list's columns' — and so is how they
278
+ are written. Declared by the domain, the columns are the domain's. */
279
+ columns: report.rows === undefined
280
+ ? reportColumnsOf(report.columns, columns)
281
+ : report.columns, rows: report.rows ?? reportRows, ...(report.defaultOpen === undefined ? {} : { defaultOpen: report.defaultOpen }), ...(report.height === undefined ? {} : { height: report.height }) })) : present === "pivot" && pivot?.spec !== undefined && pivot.fields !== undefined ? (
282
+ /* Explored: the person moves the axes. Its rows follow the search bar's grouping. */
283
+ _jsx("div", { className: "p-3", children: _jsx(PivotExplorer, { items: ordered, of: pivot.spec.of, fields: pivot.fields, measures: pivot.spec.measures, ...(pivot.defaultMeasures === undefined
284
+ ? {}
285
+ : { defaultMeasures: pivot.defaultMeasures }), ...((group?.levels.length ?? 0) > 0 ? { rows: group.levels } : {}), defaultRows: pivot.spec.rows, defaultColumns: pivot.spec.columns, ...(pivot.onCell === undefined ? {} : { onCell: pivot.onCell }), ...(pivot.height === undefined ? {} : { height: pivot.height }) }) })) : present === "pivot" && pivotShown !== undefined ? (
237
286
  /* El pivote recibe la grilla y nada más: no hay selección que pasarle
238
287
  —una casilla por montón elegiría cosas que no son registros—, no hay
239
288
  orden —cada eje viene en el orden de sus claves— y no hay menú de
240
289
  columnas, porque las columnas son un eje y no una lista de campos que
241
290
  se puedan esconder de a uno. */
242
- _jsx(PivotTable, { pivot: pivotShown, ...(pivot?.onCell === undefined ? {} : { onCell: pivot.onCell }), ...(pivot?.height === undefined ? {} : { height: pivot.height }) })) : present === "board" ? (_jsx("div", { className: "flex flex-col", children: wrapGroups((inside) => (_jsx("div", { className: "grid gap-3 p-3 @2xl:grid-cols-2 @5xl:grid-cols-3", children: inside.map(renderCard) }))) })) : (
291
+ _jsx(PivotTable, { pivot: pivotShown, ...(pivot?.onCell === undefined ? {} : { onCell: pivot.onCell }), ...(pivot?.height === undefined ? {} : { height: pivot.height }) })) : present === "calendar" && calendar !== undefined ? (_jsx("div", { className: "p-3", children: _jsx(RecordCalendar, { ...calendar, items: ordered, keyOf: keyOf, ...(press === undefined ? {} : { onOpen: press }) }) })) : present === "gantt" && gantt !== undefined ? (_jsx("div", { className: "p-3", children: _jsx(RecordGantt, { ...gantt, items: ordered, keyOf: keyOf, ...(press === undefined ? {} : { onOpen: press }) }) })) : present === "timeline" && timeline !== undefined ? (_jsx("div", { className: "p-3", children: _jsx(RecordTimeline, { ...timeline, items: ordered, keyOf: keyOf, ...(press === undefined ? {} : { onOpen: press }) }) })) : present === "board" && boardStages !== undefined ? (_jsx(BoardColumns, { items: ordered, keyOf: keyOf, stages: boardStages, card: renderCard })) : present === "board" ? (_jsx("div", { className: "flex flex-col", children: wrapGroups((inside) => (_jsx("div", { className: "grid gap-3 p-3 @2xl:grid-cols-2 @5xl:grid-cols-3", children: inside.map(renderCard) }))) })) : (
243
292
  /* El encabezado va ANTES de los montones y no adentro de cada uno:
244
293
  rotula la fila, y la fila es la misma en los cinco montones. */
245
294
  _jsxs(_Fragment, { children: [rowHeader === undefined || items.length === 0 ? null : (_jsx(RowHeader, { name: rowHeader.name, ...(rowHeader.meta === undefined ? {} : { meta: rowHeader.meta }), ...(rowHeader.tags === undefined ? {} : { tags: rowHeader.tags }), withSelection: selection !== undefined,
246
295
  /* Los espaciadores se preguntan por la PRIMERA fila: el ícono y las
247
296
  acciones son de la lista entera, no de una fila — si la primera no
248
297
  tiene ícono, ninguna lo tiene. */
249
- withIcon: firstRow?.icon !== undefined, withActions: onOpen !== undefined || firstRow?.actions !== undefined })), wrapGroups((inside) => inside.map(renderRow))] }));
298
+ withIcon: firstRow?.icon !== undefined, withAvatar: firstRow?.avatar !== undefined, trail: {
299
+ ...(firstRow?.actions === undefined ? {} : { actions: firstRow.actions }),
300
+ peek: onPeek !== undefined && !choosing,
301
+ open: onOpen !== undefined && !choosing,
302
+ } })), wrapGroups((inside) => inside.map(renderRow))] }));
250
303
  return (
251
304
  /* The list is its own container (`@container`: the presentations adapt to the width
252
305
  the LIST has, not the window) and fills the space it is given. Before, every page
253
306
  wrapped it in a `div` to get both. */
254
307
  _jsxs("div", { ref: frame, className: "@container flex min-w-0 flex-1 flex-col bg-surface", children: [_jsxs(Listing, { bar: bar,
255
308
  /* La barra se queda arriba: con 187 filas, scrollear para ver la última
256
- dejaba sin buscador, sin paginador y sin el conmutador. */
257
- sticky: true, ...(surface === undefined ? {} : { surface }), ...(metrics === undefined
309
+ dejaba sin buscador, sin paginador y sin el conmutador.
310
+ Embedded it does NOT: the record has its own sticky parts (its rail, its tabs),
311
+ and a second band stuck under them — or carried up into the frame's header slot,
312
+ which is what a sticky list bar does under a sidebar — would cover the record
313
+ for a collection of a dozen rows. */
314
+ sticky: !embedded, ...(surface === undefined ? {} : { surface }), ...(metrics === undefined
258
315
  ? {}
259
316
  : {
260
317
  metrics: _jsx("div", { className: "px-3.5", children: metrics }),
@@ -16,7 +16,14 @@ export function groupedColumnIds(group, columns) {
16
16
  .filter((id) => id !== undefined);
17
17
  }
18
18
  /** The presentations that need width: a grid of columns. */
19
- export const WIDE_PRESENTS = ["table", "sheet", "report", "pivot"];
19
+ export const WIDE_PRESENTS = [
20
+ "table",
21
+ "sheet",
22
+ "report",
23
+ "pivot",
24
+ "gantt",
25
+ "timeline",
26
+ ];
20
27
  /** The id of the column that answers for a model field, when there is one. */
21
28
  function columnAnswering(field, columns) {
22
29
  if (columns === undefined)