@sincpro/web-ui 0.2.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.
- 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 +4 -1
- package/dist/components/search/SearchBar.js +15 -7
- 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 +106 -14
- package/dist/foundations/copy.es.js +83 -9
- package/dist/foundations/copy.js +83 -9
- 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 +29 -7
- package/dist/primitives/picker/Picker.js +88 -23
- 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 +10 -0
- package/dist/primitives/tag/TagsField.js +34 -9
- package/dist/primitives/tag/choices.d.ts +2 -0
- 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 +39 -8
- 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,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
|
};
|
|
@@ -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
|
}
|
|
@@ -3,8 +3,10 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { Activity } from "../../components/activity/Activity.js";
|
|
4
4
|
import { Sheet, } from "../../components/form/Sheet.js";
|
|
5
5
|
import { Pager as PagerControl } from "../../components/record/Pager.js";
|
|
6
|
+
import { RecordAudit } from "../../components/record/RecordAudit.js";
|
|
6
7
|
import { RecordHeader } from "../../components/record/RecordHeader.js";
|
|
7
8
|
import { RecordBlocking, RecordNoticeIndicator, splitNotices, } from "../../components/record/RecordNotices.js";
|
|
9
|
+
import { RecordRibbon } from "../../components/record/RecordRibbon.js";
|
|
8
10
|
import { RecordSkeleton } from "../../components/record/RecordSkeleton.js";
|
|
9
11
|
import { SmartButton, SmartButtons } from "../../components/record/SmartButtons.js";
|
|
10
12
|
import { StatusBar } from "../../components/record/StatusBar.js";
|
|
@@ -51,6 +53,11 @@ const Status = slot("status", (_props) => null);
|
|
|
51
53
|
* notice that was already true when the record opened interrupts nothing (`Callout`
|
|
52
54
|
* is `role="status"`). */
|
|
53
55
|
const NoticeSlot = slot("notice", (_props) => null);
|
|
56
|
+
/** What the record IS now, across the paper's corner: «PAGADA», «ARCHIVADA». A word with its
|
|
57
|
+
* tone, written as a child of `Record` or `Record.Paper`; see `RecordRibbon`. */
|
|
58
|
+
const RibbonSlot = slot("ribbon", (_props) => null);
|
|
59
|
+
/** Who created the record and who changed it last, on the paper's last line. */
|
|
60
|
+
const AuditSlot = slot("audit", (_props) => null);
|
|
54
61
|
/** El papel: el registro. El `<sheet>` blanco sobre el piso. */
|
|
55
62
|
const Paper = slot("paper", (_props) => null);
|
|
56
63
|
const ActivitySlot = slot("activity", (_props) => null);
|
|
@@ -89,10 +96,12 @@ export function Record({ identity, error = null, dirty, children }) {
|
|
|
89
96
|
/* Apiladas en un bloque: arriba dónde está, abajo qué pasó para llegar.
|
|
90
97
|
Lado a lado competían por el ancho con los botones y quedaban las dos
|
|
91
98
|
truncadas; una debajo de la otra, cada una tiene su línea entera. */
|
|
92
|
-
_jsxs("div", { className: "flex min-w-0 flex-col items-end gap-1.5", children: [status.stages === undefined ? null : (_jsx(StatusBar, { stages: status.stages, current: status.current ?? status.stages[0]?.id ?? "", failed: status.failed ?? false, surface: surface })), status.children] }));
|
|
99
|
+
_jsxs("div", { className: "flex min-w-0 flex-col items-end gap-1.5", children: [status.stages === undefined ? null : (_jsx(StatusBar, { stages: status.stages, current: status.current ?? status.stages[0]?.id ?? "", failed: status.failed ?? false, surface: surface, ...(status.onSelect === undefined ? {} : { onSelect: status.onSelect }) })), status.children] }));
|
|
93
100
|
const paper = propsOf(paperNode);
|
|
94
101
|
const inPaper = [...flatten(paper?.children), ...loose];
|
|
95
102
|
const fields = propsOf(pick(inPaper, "facts"))?.fields;
|
|
103
|
+
const ribbon = propsOf(pick(inPaper, "ribbon"));
|
|
104
|
+
const audit = propsOf(pick(inPaper, "audit"));
|
|
96
105
|
/* Written at the top of the record or inside the paper: both are «on the record». */
|
|
97
106
|
const notices = pickAll(inPaper, "notice")
|
|
98
107
|
.map((node) => propsOf(node))
|
|
@@ -157,7 +166,11 @@ export function Record({ identity, error = null, dirty, children }) {
|
|
|
157
166
|
shorter than the screen. The extra height has no paint of its own: on a desk
|
|
158
167
|
nothing changes. */
|
|
159
168
|
/* The record knows the notes of its fields and rows: their cards register here. */
|
|
160
|
-
_jsx(NoteRegistryContext, { value: registry, children: _jsx("article", { className: "flex min-w-0 flex-1 flex-col", children: _jsx(RecordHeader, { railSurface: surface, ...(
|
|
169
|
+
_jsx(NoteRegistryContext, { value: registry, children: _jsx("article", { className: "flex min-w-0 flex-1 flex-col", children: _jsx(RecordHeader, { railSurface: surface, ...(ribbon === undefined
|
|
170
|
+
? {}
|
|
171
|
+
: {
|
|
172
|
+
ribbon: (_jsx(RecordRibbon, { label: ribbon.label, ...(ribbon.tone === undefined ? {} : { tone: ribbon.tone }) })),
|
|
173
|
+
}), ...(paper?.width === undefined ? {} : { width: paper.width }), ...(dirty === undefined ? {} : { dirty }), ...(crumbs === undefined ? {} : { crumbs }), ...(pager === undefined
|
|
161
174
|
? {}
|
|
162
175
|
: {
|
|
163
176
|
pager: (_jsx(PagerControl, { index: pager.index, total: pager.total, surface: surface, ...(pager.onPrev === undefined ? {} : { onPrev: pager.onPrev }), ...(pager.onNext === undefined ? {} : { onNext: pager.onNext }) })),
|
|
@@ -190,11 +203,11 @@ export function Record({ identity, error = null, dirty, children }) {
|
|
|
190
203
|
},
|
|
191
204
|
},
|
|
192
205
|
}) })),
|
|
193
|
-
}), children:
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
206
|
+
}), children: _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: `flex min-w-0 flex-col ${byDensity(d, "gap-(--spacing-block) px-4 pt-(--spacing-block) pb-4", "gap-2.5 pt-2.5 pb-2")}`, children: _jsxs(Sheet, { layout: paper?.layout ?? "stacked", columns: paper?.columns ?? 2, ...(hasFacts ? { fields } : {}), ...(above.length > 0 || (globalSheet && aspectsNode !== undefined)
|
|
207
|
+
? {
|
|
208
|
+
head: (_jsxs(_Fragment, { children: [above, globalSheet && aspectsNode !== undefined ? (_jsx("div", { className: `border-b border-line-strong ${byDensity(d, "-mx-4 px-4", "")}`, children: aspectsNode })) : null] })),
|
|
209
|
+
}
|
|
210
|
+
: {}), children: [pageBody, below] }) }), audit === undefined ? null : _jsx(RecordAudit, { ...audit })] }) }) }) }));
|
|
198
211
|
}
|
|
199
212
|
Record.Skeleton = RecordSkeleton;
|
|
200
213
|
Record.Rail = Rail;
|
|
@@ -209,3 +222,5 @@ Record.Facts = Facts;
|
|
|
209
222
|
Record.Pages = Pages;
|
|
210
223
|
Record.Page = Page;
|
|
211
224
|
Record.Notice = NoticeSlot;
|
|
225
|
+
Record.Ribbon = RibbonSlot;
|
|
226
|
+
Record.Audit = AuditSlot;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sincpro/web-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "An enterprise web design system for business software — ERPs, back offices, management tools — in React and Tailwind: tokens, primitives, components and the two views every management screen is made of.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -53,19 +53,24 @@
|
|
|
53
53
|
"@codemirror/lint": "^6.9.7",
|
|
54
54
|
"@fontsource/ibm-plex-mono": "^5.3.0",
|
|
55
55
|
"@fontsource/ibm-plex-sans": "^5.3.0",
|
|
56
|
+
"@fullcalendar/react": "7.1.3",
|
|
56
57
|
"@internationalized/date": "^3.12.4",
|
|
58
|
+
"@svar-ui/react-gantt": "2.8.2",
|
|
57
59
|
"@uiw/react-codemirror": "^4.25.11",
|
|
58
60
|
"lucide-react": "^1.45.0",
|
|
59
61
|
"react-aria": "^3.53.1",
|
|
60
62
|
"react-aria-components": "^1.22.1",
|
|
61
63
|
"tailwind-variants": "^4.0.0",
|
|
62
|
-
"tailwindcss-react-aria-components": "^2.2.0"
|
|
64
|
+
"tailwindcss-react-aria-components": "^2.2.0",
|
|
65
|
+
"temporal-polyfill": "^1.0.6"
|
|
63
66
|
},
|
|
64
67
|
"devDependencies": {
|
|
65
68
|
"@eslint/js": "^10.0.1",
|
|
69
|
+
"@sincpro/web-composer": "^1.4.0",
|
|
66
70
|
"@storybook/addon-a11y": "10.6.0",
|
|
67
71
|
"@storybook/addon-docs": "10.6.0",
|
|
68
72
|
"@storybook/addon-mcp": "10.6.0",
|
|
73
|
+
"@storybook/addon-vitest": "10.6.0",
|
|
69
74
|
"@storybook/react-vite": "10.6.0",
|
|
70
75
|
"@tailwindcss/vite": "4.3.3",
|
|
71
76
|
"@tanstack/react-table": "^9.2.4",
|
|
@@ -74,12 +79,13 @@
|
|
|
74
79
|
"@types/react": "^19.3.0",
|
|
75
80
|
"@types/react-dom": "^19.3.0",
|
|
76
81
|
"@vitejs/plugin-react": "6.1.1",
|
|
82
|
+
"@vitest/browser-playwright": "5.0.5",
|
|
77
83
|
"eslint": "^10.11.0",
|
|
78
84
|
"eslint-config-prettier": "^10.1.8",
|
|
79
85
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
80
86
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
87
|
+
"playwright": "1.64.1",
|
|
81
88
|
"prettier": "^3.9.8",
|
|
82
|
-
"puppeteer-core": "^23.11.1",
|
|
83
89
|
"react": "19.3.0",
|
|
84
90
|
"react-dom": "19.3.0",
|
|
85
91
|
"storybook": "10.6.0",
|
|
@@ -88,7 +94,8 @@
|
|
|
88
94
|
"tsx": "^4.23.13",
|
|
89
95
|
"typescript": "~5.9.3",
|
|
90
96
|
"typescript-eslint": "^8.70.0",
|
|
91
|
-
"vite": "8.3.0"
|
|
97
|
+
"vite": "8.3.0",
|
|
98
|
+
"vitest": "5.0.5"
|
|
92
99
|
},
|
|
93
100
|
"keywords": [
|
|
94
101
|
"design-system",
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { type PaletteCommand } from "./commands.js";
|
|
2
|
-
/**
|
|
3
|
-
* The search box and the list, without the modal around them.
|
|
4
|
-
*
|
|
5
|
-
* Context: split out so the filtering can be rendered on its own — in a test, with a query
|
|
6
|
-
* already typed — since the modal is a portal that only exists in a browser.
|
|
7
|
-
*/
|
|
8
|
-
export declare function CommandSearch({ commands, onRun, query, onQueryChange, placeholder, }: {
|
|
9
|
-
commands: readonly PaletteCommand[];
|
|
10
|
-
onRun: (id: string) => void;
|
|
11
|
-
query?: string;
|
|
12
|
-
onQueryChange?: (next: string) => void;
|
|
13
|
-
placeholder?: string;
|
|
14
|
-
}): import("react").JSX.Element;
|
|
15
|
-
/**
|
|
16
|
-
* A modal search over everything the app can do — go to a screen, create a record — opened
|
|
17
|
-
* with ⌘K (Ctrl+K) and run with Enter.
|
|
18
|
-
*
|
|
19
|
-
* Context: it is controlled (`open`, `onOpenChange`) and does not bind the shortcut itself,
|
|
20
|
-
* because an app has ONE palette and many screens: the key is bound once, where the palette
|
|
21
|
-
* lives, with `useCommandPaletteShortcut`. Running a command closes it — the palette is a
|
|
22
|
-
* path, not a place to stay.
|
|
23
|
-
*/
|
|
24
|
-
export declare function CommandPalette({ open, onOpenChange, commands, onRun, placeholder, }: {
|
|
25
|
-
open: boolean;
|
|
26
|
-
onOpenChange: (open: boolean) => void;
|
|
27
|
-
commands: readonly PaletteCommand[];
|
|
28
|
-
/** A command was chosen. The palette closes on its own. */
|
|
29
|
-
onRun: (id: string) => void;
|
|
30
|
-
/** The search box's prompt and its accessible name. By default, the copy table's. */
|
|
31
|
-
placeholder?: string;
|
|
32
|
-
}): import("react").JSX.Element;
|
|
33
|
-
/**
|
|
34
|
-
* Binds ⌘K (Ctrl+K on Windows and Linux) to open the palette, anywhere in the page.
|
|
35
|
-
*
|
|
36
|
-
* Context: both modifiers are accepted on every platform. Telling them apart by the user
|
|
37
|
-
* agent guesses wrong on a Mac with a PC keyboard, and the cost of accepting both is that
|
|
38
|
-
* Ctrl+K on a Mac — «delete to the end of the line» in a text box — opens the palette.
|
|
39
|
-
*/
|
|
40
|
-
export declare function useCommandPaletteShortcut(setOpen: (open: boolean) => void): void;
|