@sincpro/web-ui 0.2.0 → 1.1.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.
- package/README.md +3 -3
- package/dist/components/activity/ActivityBadge.d.ts +16 -0
- package/dist/components/activity/ActivityBadge.js +29 -0
- package/dist/components/activity/index.d.ts +1 -0
- package/dist/components/activity/index.js +1 -0
- package/dist/components/calendar/RecordCalendar.d.ts +48 -0
- package/dist/components/calendar/RecordCalendar.js +101 -0
- package/dist/components/calendar/index.d.ts +1 -0
- package/dist/components/calendar/index.js +1 -0
- package/dist/components/form/Sheet.js +5 -1
- package/dist/components/gantt/RecordGantt.d.ts +44 -0
- package/dist/components/gantt/RecordGantt.js +110 -0
- package/dist/components/gantt/index.d.ts +1 -0
- package/dist/components/gantt/index.js +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.js +3 -0
- package/dist/components/lines/LineItems.d.ts +5 -2
- package/dist/components/lines/LineItems.js +3 -3
- package/dist/components/listing/BoardColumns.d.ts +39 -0
- package/dist/components/listing/BoardColumns.js +138 -0
- package/dist/components/listing/ListingBar.d.ts +5 -0
- package/dist/components/listing/ListingBar.js +5 -2
- package/dist/components/listing/PivotExplorer.d.ts +34 -0
- package/dist/components/listing/PivotExplorer.js +86 -0
- package/dist/components/listing/RecordCard.d.ts +6 -8
- package/dist/components/listing/RecordCard.js +13 -7
- package/dist/components/listing/Row.d.ts +21 -2
- package/dist/components/listing/Row.js +29 -13
- package/dist/components/listing/index.d.ts +2 -0
- package/dist/components/listing/index.js +2 -0
- package/dist/components/listing/set.d.ts +1 -1
- package/dist/components/navigation/index.d.ts +0 -2
- package/dist/components/navigation/index.js +2 -3
- package/dist/components/record/RecordAudit.d.ts +17 -0
- package/dist/components/record/RecordAudit.js +27 -0
- package/dist/components/record/RecordHeader.d.ts +7 -1
- package/dist/components/record/RecordHeader.js +30 -18
- package/dist/components/record/RecordRibbon.d.ts +13 -0
- package/dist/components/record/RecordRibbon.js +22 -0
- package/dist/components/record/index.d.ts +2 -0
- package/dist/components/record/index.js +2 -0
- package/dist/components/search/CriteriaChips.d.ts +6 -1
- package/dist/components/search/CriteriaChips.js +71 -20
- package/dist/components/search/SearchBar.d.ts +15 -1
- package/dist/components/search/SearchBar.js +34 -16
- package/dist/components/search/query.d.ts +14 -11
- package/dist/components/search/query.js +36 -3
- package/dist/components/timeline/RecordTimeline.d.ts +60 -0
- package/dist/components/timeline/RecordTimeline.js +214 -0
- package/dist/components/timeline/TimelineToolbar.d.ts +30 -0
- package/dist/components/timeline/TimelineToolbar.js +31 -0
- package/dist/components/timeline/index.d.ts +3 -0
- package/dist/components/timeline/index.js +3 -0
- package/dist/components/timeline/time.d.ts +43 -0
- package/dist/components/timeline/time.js +135 -0
- package/dist/foundations/copy.d.ts +110 -14
- package/dist/foundations/copy.es.js +85 -9
- package/dist/foundations/copy.js +85 -9
- package/dist/foundations/grouping.d.ts +3 -0
- package/dist/foundations/index.d.ts +1 -1
- package/dist/foundations/match.d.ts +23 -0
- package/dist/foundations/match.js +63 -0
- package/dist/foundations/slots.d.ts +1 -1
- package/dist/foundations/slots.js +8 -3
- package/dist/foundations/types.d.ts +7 -0
- package/dist/foundations/types.js +0 -36
- package/dist/primitives/block/index.d.ts +1 -1
- package/dist/primitives/block/index.js +1 -1
- package/dist/primitives/cell/CellStack.d.ts +7 -1
- package/dist/primitives/cell/CellStack.js +5 -2
- package/dist/primitives/choice/ComboBox.js +8 -4
- package/dist/primitives/choice/KanbanState.d.ts +14 -0
- package/dist/primitives/choice/KanbanState.js +37 -0
- package/dist/primitives/choice/Priority.d.ts +20 -0
- package/dist/primitives/choice/Priority.js +41 -0
- package/dist/primitives/choice/Slider.d.ts +6 -1
- package/dist/primitives/choice/Slider.js +2 -2
- package/dist/primitives/choice/index.d.ts +2 -0
- package/dist/primitives/choice/index.js +2 -0
- package/dist/primitives/dialog/Dialog.d.ts +9 -3
- package/dist/primitives/dialog/Dialog.js +41 -5
- package/dist/primitives/dialog/index.d.ts +0 -1
- package/dist/primitives/dialog/index.js +0 -2
- package/dist/primitives/notice/index.d.ts +1 -1
- package/dist/primitives/notice/index.js +1 -1
- package/dist/primitives/overlay/Preview.js +13 -4
- package/dist/primitives/picker/MatchedText.d.ts +10 -0
- package/dist/primitives/picker/MatchedText.js +12 -0
- package/dist/primitives/picker/Picker.d.ts +42 -7
- package/dist/primitives/picker/Picker.js +107 -29
- package/dist/primitives/table/EditableTable.d.ts +6 -1
- package/dist/primitives/table/EditableTable.js +70 -9
- package/dist/primitives/table/ReportTable.js +2 -1
- package/dist/primitives/table/RowActions.js +7 -2
- package/dist/primitives/table/Table.d.ts +5 -0
- package/dist/primitives/table/Table.js +17 -8
- package/dist/primitives/tag/Tags.d.ts +2 -0
- package/dist/primitives/tag/Tags.js +2 -1
- package/dist/primitives/tag/TagsField.d.ts +17 -0
- package/dist/primitives/tag/TagsField.js +46 -11
- package/dist/primitives/tag/choices.d.ts +6 -1
- package/dist/primitives/tag/choices.js +6 -2
- package/dist/primitives/value/Avatar.d.ts +16 -0
- package/dist/primitives/value/Avatar.js +91 -0
- package/dist/primitives/value/Person.d.ts +6 -51
- package/dist/primitives/value/Person.js +9 -148
- package/dist/primitives/value/Relation.d.ts +21 -9
- package/dist/primitives/value/Relation.js +35 -24
- package/dist/primitives/value/contact.d.ts +30 -0
- package/dist/primitives/value/contact.js +51 -0
- package/dist/primitives/value/index.d.ts +1 -1
- package/dist/primitives/value/index.js +1 -1
- package/dist/tokens.css +136 -1
- package/dist/views/list/List.d.ts +25 -2
- package/dist/views/list/List.js +48 -8
- package/dist/views/list/ListView.d.ts +1 -1
- package/dist/views/list/ListView.js +40 -9
- package/dist/views/list/columns.js +8 -1
- package/dist/views/list/presentations.d.ts +5 -2
- package/dist/views/list/presentations.js +32 -2
- package/dist/views/list/types.d.ts +44 -0
- package/dist/views/list/useNarrowList.d.ts +10 -0
- package/dist/views/list/useNarrowList.js +20 -6
- package/dist/views/record/Record.d.ts +14 -0
- package/dist/views/record/Record.js +22 -7
- package/package.json +11 -4
- package/dist/components/navigation/CommandPalette.d.ts +0 -40
- package/dist/components/navigation/CommandPalette.js +0 -114
- package/dist/components/navigation/commands.d.ts +0 -30
- package/dist/components/navigation/commands.js +0 -30
- package/dist/primitives/dialog/Drill.d.ts +0 -90
- package/dist/primitives/dialog/Drill.js +0 -172
|
@@ -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
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;
|
|
@@ -50,6 +51,8 @@ type TableProps<T extends object> = {
|
|
|
50
51
|
* se puede ver como tablero. */
|
|
51
52
|
type BoardProps<T> = {
|
|
52
53
|
card?: (item: T) => ReactNode;
|
|
54
|
+
/** One column per stage, with drag (and Alt + ← / →) to move a card between them. */
|
|
55
|
+
stages?: BoardStages<T>;
|
|
53
56
|
};
|
|
54
57
|
/** La planilla. Sin columnas propias, sale de las que declaró `List.Table` — la
|
|
55
58
|
* de datos CRUDOS sí las trae, porque ahí salen del archivo y el código no sabe
|
|
@@ -75,6 +78,11 @@ export type ListProps<T extends object> = {
|
|
|
75
78
|
count: number;
|
|
76
79
|
clear: () => void;
|
|
77
80
|
actions?: ReactNode;
|
|
81
|
+
/** Every record that matches, not just the page — see `ListViewProps`. */
|
|
82
|
+
matching?: {
|
|
83
|
+
total: number;
|
|
84
|
+
onSelect: () => void;
|
|
85
|
+
};
|
|
78
86
|
};
|
|
79
87
|
/** El orden, que también es del conjunto. */
|
|
80
88
|
sort?: SortState | null;
|
|
@@ -88,6 +96,18 @@ export type ListProps<T extends object> = {
|
|
|
88
96
|
onOpen?: (item: T) => void;
|
|
89
97
|
onPeek?: (item: T) => void;
|
|
90
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;
|
|
91
111
|
/** Los tres estados, en un solo lugar. */
|
|
92
112
|
state?: {
|
|
93
113
|
loading?: boolean;
|
|
@@ -99,7 +119,7 @@ export type ListProps<T extends object> = {
|
|
|
99
119
|
empty?: ListEmpty;
|
|
100
120
|
children?: ReactNode;
|
|
101
121
|
};
|
|
102
|
-
export declare function List<T extends object>({ items, keyOf, embedded, 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;
|
|
103
123
|
export declare namespace List {
|
|
104
124
|
var Bar: ((props: BarProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"bar">;
|
|
105
125
|
var Crumbs: ((props: CrumbsProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"crumbs">;
|
|
@@ -115,5 +135,8 @@ export declare namespace List {
|
|
|
115
135
|
var Sheet: <T>(props: SheetProps<T>) => ReactNode;
|
|
116
136
|
var Report: ((props: ListReport) => ReactNode) & import("../../foundations/slots.js").Slotted<"report">;
|
|
117
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;
|
|
118
141
|
}
|
|
119
142
|
export {};
|
package/dist/views/list/List.js
CHANGED
|
@@ -40,10 +40,32 @@ const PRESENT_OF = {
|
|
|
40
40
|
sheet: "sheet",
|
|
41
41
|
report: "report",
|
|
42
42
|
pivot: "pivot",
|
|
43
|
+
calendar: "calendar",
|
|
44
|
+
gantt: "gantt",
|
|
45
|
+
timeline: "timeline",
|
|
43
46
|
};
|
|
44
|
-
const FACES = [
|
|
45
|
-
|
|
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, }) {
|
|
46
65
|
const top = flatten(children);
|
|
66
|
+
const onOpen = readOnly ? undefined : openWith;
|
|
67
|
+
const onPeek = readOnly ? undefined : peekWith;
|
|
68
|
+
const selection = readOnly ? undefined : selectWith;
|
|
47
69
|
const bar = flatten(propsOf(pick(top, "bar"))?.children);
|
|
48
70
|
const crumbs = propsOf(pick(bar, "crumbs"))?.crumbs;
|
|
49
71
|
const identity = propsOf(pick(bar, "identity"));
|
|
@@ -58,6 +80,9 @@ export function List({ items, keyOf, embedded, surface, group, selection, sort,
|
|
|
58
80
|
const sheet = propsOf(pick(top, "sheet"));
|
|
59
81
|
const report = propsOf(pick(top, "report"));
|
|
60
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"));
|
|
61
86
|
/* Las caras que hay son las que están escritas, EN ESE ORDEN. El conmutador
|
|
62
87
|
no puede ofrecer una que no se pueda dibujar, porque la que se dibuja y la
|
|
63
88
|
que se ofrece son el mismo nodo. */
|
|
@@ -68,22 +93,34 @@ export function List({ items, keyOf, embedded, surface, group, selection, sort,
|
|
|
68
93
|
/* La escotilla: lo que el arquetipo no previó se dibuja igual, debajo. Que
|
|
69
94
|
aparezca algo acá es la señal de que al arquetipo le falta una pieza. */
|
|
70
95
|
const rest = loose(top, ["bar", "metrics", ...FACES]);
|
|
71
|
-
return (_jsx(ListView, { items: items, keyOf: keyOf, row: rows
|
|
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
|
|
72
104
|
? {}
|
|
73
105
|
: {
|
|
74
|
-
|
|
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,
|
|
75
110
|
...(table.totals === undefined ? {} : { totals: table.totals }),
|
|
76
|
-
...(table.rowActions === undefined
|
|
77
|
-
|
|
111
|
+
...(table.rowActions === undefined || readOnly
|
|
112
|
+
? {}
|
|
113
|
+
: { rowActions: table.rowActions }),
|
|
114
|
+
...(table.create === undefined || readOnly ? {} : { create: table.create }),
|
|
78
115
|
...(table.visibleColumnIds === undefined
|
|
79
116
|
? {}
|
|
80
117
|
: { visibleColumnIds: table.visibleColumnIds }),
|
|
81
118
|
...(table.onVisibleColumnIdsChange === undefined
|
|
82
119
|
? {}
|
|
83
120
|
: { onVisibleColumnIdsChange: table.onVisibleColumnIdsChange }),
|
|
84
|
-
}), ...(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
|
|
85
122
|
? {}
|
|
86
|
-
: { 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 }));
|
|
87
124
|
}
|
|
88
125
|
List.Bar = Bar;
|
|
89
126
|
List.Crumbs = Crumbs;
|
|
@@ -99,3 +136,6 @@ List.Board = Board;
|
|
|
99
136
|
List.Sheet = SheetSlot;
|
|
100
137
|
List.Report = Report;
|
|
101
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, embedded, surface: requestedSurface, metrics, search, pagination, views, actions, row, rowHeader, columns, visibleColumnIds, onVisibleColumnIdsChange, card, sheet, report, pivot, present: requested, presents, onPresentChange, sort, onSortChange, sortable, totals, rowActions, create, 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";
|
|
@@ -21,7 +26,7 @@ import { groupedColumnIds } from "./columns.js";
|
|
|
21
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,8 +62,11 @@ 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, embedded = false, surface: requestedSurface, 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, create, 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);
|
|
63
71
|
/* Embedded, the list sits on the record's paper: there is no dark band to inherit, and a
|
|
64
72
|
navy switcher inside a white sheet would be a second chrome on the screen. */
|
|
@@ -134,6 +142,7 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
134
142
|
/* Embedded, the line is drawn only if it has something to OFFER: a count alone repeats
|
|
135
143
|
what the rows (and the footer) already say. */
|
|
136
144
|
const barHasControls = !embedded ||
|
|
145
|
+
identity?.title !== undefined ||
|
|
137
146
|
search !== undefined ||
|
|
138
147
|
viewsSlot !== undefined ||
|
|
139
148
|
actions !== undefined ||
|
|
@@ -150,12 +159,26 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
150
159
|
count: selection.count,
|
|
151
160
|
onClear: selection.clear,
|
|
152
161
|
...(selection.actions === undefined ? {} : { actions: selection.actions }),
|
|
162
|
+
...(selection.matching === undefined ? {} : { matching: selection.matching }),
|
|
153
163
|
},
|
|
154
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);
|
|
155
173
|
const renderRow = (item) => {
|
|
156
174
|
const d = row(item);
|
|
157
175
|
const id = keyOf(item);
|
|
158
|
-
return (_jsx(Row, { ...(d.icon === undefined ? {} : { icon: d.icon }),
|
|
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
|
|
159
182
|
? {}
|
|
160
183
|
: { selected: selection.has(id), onSelectedChange: () => selection.toggle(id) }), children: d.children }, id));
|
|
161
184
|
};
|
|
@@ -215,7 +238,7 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
215
238
|
return list.map((pile) => {
|
|
216
239
|
const key = pathKey(pile.path);
|
|
217
240
|
const open = isOpen(pile);
|
|
218
|
-
return (_jsxs(Fragment, { children: [_jsx(GroupHeader, { field: pile.field, label: pile.label, count: pile.count, depth: pile.depth, open: open, ...(group?.onToggle === undefined
|
|
241
|
+
return (_jsxs(Fragment, { children: [_jsx(GroupHeader, { field: pile.field, label: pile.label, count: pile.count, ...(pile.detail === undefined ? {} : { detail: pile.detail }), depth: pile.depth, open: open, ...(group?.onToggle === undefined
|
|
219
242
|
? {}
|
|
220
243
|
: { onToggle: () => group.onToggle?.(key) }) }), !open
|
|
221
244
|
? null
|
|
@@ -235,11 +258,11 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
235
258
|
return _jsx(Fragment, { children: card(item) }, keyOf(item));
|
|
236
259
|
}
|
|
237
260
|
const d = row(item);
|
|
238
|
-
return (_jsx(RecordCard, { title: d.name, ...(d.tags === undefined ? {} : { tags: d.tags }), ...(d.meta === undefined ? {} : { meta: d.meta }), ...(
|
|
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)));
|
|
239
262
|
};
|
|
240
263
|
const body = present === "table" && shownColumns !== undefined ? (
|
|
241
264
|
/* The table receives EVERYTHING that belongs to the set: see `TablePresentation`. */
|
|
242
|
-
_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 ? {} : { 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 ? (
|
|
243
266
|
/* La planilla recibe el conjunto por ÍNDICE y no una matriz ya armada: ver `SheetPresentation`. */
|
|
244
267
|
_jsx(SheetPresentation, { spec: sheetSpec, entries: sheetRows, ordered: ordered, ...(group === undefined ? {} : { group }) })) : present === "report" && report !== undefined ? (
|
|
245
268
|
/* El informe declarado por el dominio no se ORDENA desde la barra: el
|
|
@@ -255,20 +278,28 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
255
278
|
are written. Declared by the domain, the columns are the domain's. */
|
|
256
279
|
columns: report.rows === undefined
|
|
257
280
|
? reportColumnsOf(report.columns, columns)
|
|
258
|
-
: report.columns, rows: report.rows ?? reportRows, ...(report.defaultOpen === undefined ? {} : { defaultOpen: report.defaultOpen }), ...(report.height === undefined ? {} : { height: report.height }) })) : present === "pivot" &&
|
|
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 ? (
|
|
259
286
|
/* El pivote recibe la grilla y nada más: no hay selección que pasarle
|
|
260
287
|
—una casilla por montón elegiría cosas que no son registros—, no hay
|
|
261
288
|
orden —cada eje viene en el orden de sus claves— y no hay menú de
|
|
262
289
|
columnas, porque las columnas son un eje y no una lista de campos que
|
|
263
290
|
se puedan esconder de a uno. */
|
|
264
|
-
_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) }))) })) : (
|
|
265
292
|
/* El encabezado va ANTES de los montones y no adentro de cada uno:
|
|
266
293
|
rotula la fila, y la fila es la misma en los cinco montones. */
|
|
267
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,
|
|
268
295
|
/* Los espaciadores se preguntan por la PRIMERA fila: el ícono y las
|
|
269
296
|
acciones son de la lista entera, no de una fila — si la primera no
|
|
270
297
|
tiene ícono, ninguna lo tiene. */
|
|
271
|
-
withIcon: firstRow?.icon !== undefined,
|
|
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))] }));
|
|
272
303
|
return (
|
|
273
304
|
/* The list is its own container (`@container`: the presentations adapt to the width
|
|
274
305
|
the LIST has, not the window) and fills the space it is given. Before, every page
|
|
@@ -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 = [
|
|
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)
|
|
@@ -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" | "create" | "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, create, 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, create, 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 } : {}), ...(
|
|
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
|
|
@@ -1,6 +1,11 @@
|
|
|
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";
|
|
@@ -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;
|
|
@@ -153,6 +163,12 @@ export type ListPivot<T> = {
|
|
|
153
163
|
/** Qué pasa al tocar un cruce: abrir las filas que cayeron ahí. */
|
|
154
164
|
onCell?: (row: PivotKey, column: PivotKey) => void;
|
|
155
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[];
|
|
156
172
|
};
|
|
157
173
|
export type ListViewProps<T extends object> = {
|
|
158
174
|
items: readonly T[];
|
|
@@ -226,6 +242,14 @@ export type ListViewProps<T extends object> = {
|
|
|
226
242
|
create?: DataTableCreate<T>;
|
|
227
243
|
/** The board presentation. */
|
|
228
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>;
|
|
229
253
|
/**
|
|
230
254
|
* La PLANILLA. Con esto, `present="sheet"` dibuja un `SheetTable`.
|
|
231
255
|
*
|
|
@@ -266,10 +290,24 @@ export type ListViewProps<T extends object> = {
|
|
|
266
290
|
count: number;
|
|
267
291
|
clear: () => void;
|
|
268
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
|
+
};
|
|
269
299
|
};
|
|
270
300
|
onOpen?: (item: T) => void;
|
|
271
301
|
onPeek?: (item: T) => void;
|
|
272
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;
|
|
273
311
|
/** The three states, in a single place. Every view used to re-assemble them
|
|
274
312
|
* with three nested ternaries, and that is why «empty because of a filter»
|
|
275
313
|
* and «really empty» got mixed up in half the screens. */
|
|
@@ -282,3 +320,9 @@ export type ListViewProps<T extends object> = {
|
|
|
282
320
|
/** The escape hatch. */
|
|
283
321
|
children?: ReactNode;
|
|
284
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 {
|
|
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
|
-
|
|
21
|
+
useLayoutEffect(() => {
|
|
19
22
|
const el = frame.current;
|
|
20
|
-
if (el === null
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
}
|
|
@@ -2,6 +2,7 @@ import { type ActivityProps } from "../../components/activity/Activity.js";
|
|
|
2
2
|
import { type SheetField, type SheetLayout } from "../../components/form/Sheet.js";
|
|
3
3
|
import type { Crumb } from "../../components/record/Breadcrumb.js";
|
|
4
4
|
import type { DirtyState } from "../../components/record/DirtyActions.js";
|
|
5
|
+
import { type RecordStamp } from "../../components/record/RecordAudit.js";
|
|
5
6
|
import { type RecordNoticeSpec } from "../../components/record/RecordNotices.js";
|
|
6
7
|
import { RecordSkeleton } from "../../components/record/RecordSkeleton.js";
|
|
7
8
|
import { type Stage } from "../../components/record/StatusBar.js";
|
|
@@ -68,6 +69,8 @@ type StatusProps = {
|
|
|
68
69
|
/** Cuál de las etapas es la de ahora. Sin esto, la primera. */
|
|
69
70
|
current?: string;
|
|
70
71
|
failed?: boolean;
|
|
72
|
+
/** The stages can be chosen: pressing one moves the record there (a CRM's pipeline). */
|
|
73
|
+
onSelect?: (id: string) => void;
|
|
71
74
|
/** La escotilla del estado, para lo que no es ninguna de las dos. */
|
|
72
75
|
children?: ReactNode;
|
|
73
76
|
};
|
|
@@ -76,6 +79,9 @@ type PaperProps = {
|
|
|
76
79
|
* valor, que es la forma de Odoo. */
|
|
77
80
|
layout?: SheetLayout;
|
|
78
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";
|
|
79
85
|
children?: ReactNode;
|
|
80
86
|
};
|
|
81
87
|
type FactsProps = {
|
|
@@ -142,4 +148,12 @@ export declare namespace Record {
|
|
|
142
148
|
var Pages: ((props: PagesProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"pages">;
|
|
143
149
|
var Page: ((props: PageProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"page">;
|
|
144
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">;
|
|
145
159
|
}
|