@sincpro/web-ui 1.3.0 → 1.5.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/dist/components/form/Sheet.js +4 -1
- package/dist/components/listing/RecordCard.d.ts +4 -0
- package/dist/components/listing/RecordCard.js +4 -1
- package/dist/components/listing/Row.d.ts +5 -4
- package/dist/components/listing/Row.js +5 -4
- package/dist/components/record/DirtyActions.d.ts +20 -6
- package/dist/components/record/DirtyActions.js +55 -4
- package/dist/components/record/RecordHeader.d.ts +1 -1
- package/dist/components/record/RecordHeader.js +32 -13
- package/dist/components/record/RecordSkeleton.js +1 -1
- package/dist/components/search/SearchBar.js +12 -3
- package/dist/foundations/copy.d.ts +15 -0
- package/dist/foundations/copy.es.js +8 -0
- package/dist/foundations/copy.js +8 -0
- package/dist/foundations/grouping.d.ts +28 -0
- package/dist/foundations/grouping.js +0 -0
- package/dist/foundations/index.d.ts +1 -1
- package/dist/foundations/index.js +1 -1
- package/dist/primitives/button/Button.d.ts +12 -2
- package/dist/primitives/button/Button.js +12 -2
- package/dist/primitives/cell/CellStack.js +1 -1
- package/dist/primitives/field/Field.d.ts +8 -3
- package/dist/primitives/field/Field.js +7 -3
- package/dist/primitives/notice/Notice.js +3 -1
- package/dist/primitives/picker/MatchedText.d.ts +3 -2
- package/dist/primitives/picker/MatchedText.js +3 -2
- package/dist/primitives/picker/Picker.d.ts +18 -1
- package/dist/primitives/picker/Picker.js +5 -5
- package/dist/primitives/picker/TagsField.d.ts +10 -0
- package/dist/primitives/picker/TagsField.js +2 -2
- package/dist/primitives/picker/index.d.ts +2 -1
- package/dist/primitives/picker/index.js +2 -0
- package/dist/primitives/picker/tagChoices.d.ts +7 -0
- package/dist/primitives/picker/tagChoices.js +9 -4
- package/dist/primitives/table/DataTable.js +10 -11
- package/dist/primitives/table/EditableTable.js +5 -1
- package/dist/primitives/table/PileFootLine.d.ts +14 -0
- package/dist/primitives/table/PileFootLine.js +22 -0
- package/dist/primitives/table/index.d.ts +1 -0
- package/dist/primitives/table/index.js +1 -0
- package/dist/tokens.css +42 -0
- package/dist/views/list/List.d.ts +11 -3
- package/dist/views/list/ListView.js +19 -8
- package/dist/views/list/index.d.ts +1 -1
- package/dist/views/list/types.d.ts +24 -2
- package/package.json +1 -1
|
@@ -16,12 +16,13 @@ import { Breadcrumb } from "../../components/record/Breadcrumb.js";
|
|
|
16
16
|
import { RecordTimeline } from "../../components/timeline/RecordTimeline.js";
|
|
17
17
|
import { useUiCopy } from "../../foundations/copy.js";
|
|
18
18
|
import { useDensity } from "../../foundations/density.js";
|
|
19
|
-
import { pathKey, pileIsOpen, pilesOf, } from "../../foundations/grouping.js";
|
|
19
|
+
import { pathKey, pileFoot, pileIsOpen, pilesOf, } from "../../foundations/grouping.js";
|
|
20
20
|
import { pivotFrom, pivotIsEmpty } from "../../foundations/pivot.js";
|
|
21
21
|
import { Button } from "../../primitives/button/Button.js";
|
|
22
22
|
import { Empty, Failed, Skeleton } from "../../primitives/notice/Notice.js";
|
|
23
23
|
import { PivotTable } from "../../primitives/table/PivotTable.js";
|
|
24
24
|
import { ReportTable } from "../../primitives/table/ReportTable.js";
|
|
25
|
+
import { ProgressBar } from "../../primitives/value/ProgressBar.js";
|
|
25
26
|
import { groupedColumnIds, WIDE_PRESENTS } from "./columns.js";
|
|
26
27
|
import { groupLevelsOf, reportColumnsOf, reportRowsOf, sheetFromColumns, sheetRowsOf, } from "./derive.js";
|
|
27
28
|
import { SheetPresentation, TablePresentation, } from "./presentations.js";
|
|
@@ -79,6 +80,7 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
79
80
|
const density = useDensity();
|
|
80
81
|
const copy = useUiCopy();
|
|
81
82
|
const loading = state?.loading === true;
|
|
83
|
+
const refreshing = state?.refreshing === true && !loading;
|
|
82
84
|
const error = state?.error ?? null;
|
|
83
85
|
const columnSpecs = useMemo(() => specsFromColumns(columns ?? [], false), [columns]);
|
|
84
86
|
const columnControl = useColumnControl(columnSpecs, {
|
|
@@ -240,6 +242,11 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
240
242
|
los decide la vista para la lista entera. */
|
|
241
243
|
const first = items[0];
|
|
242
244
|
const firstRow = first === undefined ? undefined : row(first);
|
|
245
|
+
/* The same foot the table draws: «Ver 50 más de 49.950», or «Se muestran 500 de …». */
|
|
246
|
+
function footOf(pile) {
|
|
247
|
+
const foot = pileFoot(pile, group?.onMore !== undefined);
|
|
248
|
+
return foot === null ? null : (_jsx(GroupMore, { foot: foot, depth: pile.depth, onMore: () => group?.onMore?.(pile) }));
|
|
249
|
+
}
|
|
243
250
|
/** Los montones, con sus filas adentro. Se llama a sí misma porque el árbol
|
|
244
251
|
* tiene N niveles: un montón dibuja su encabezado y, adentro, o bien los
|
|
245
252
|
* montones de abajo o bien las filas — nunca las dos cosas. */
|
|
@@ -253,10 +260,7 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
253
260
|
? null
|
|
254
261
|
: pile.piles.length > 0
|
|
255
262
|
? renderPiles(pile.piles, nodes)
|
|
256
|
-
: nodes(pile.items), open
|
|
257
|
-
pile.piles.length === 0 &&
|
|
258
|
-
pile.items.length < pile.count &&
|
|
259
|
-
group?.onMore !== undefined ? (_jsx(GroupMore, { left: pile.count - pile.items.length, depth: pile.depth, onMore: () => group.onMore?.(pile) })) : null] }, key));
|
|
263
|
+
: nodes(pile.items), open ? footOf(pile) : null] }, key));
|
|
260
264
|
});
|
|
261
265
|
}
|
|
262
266
|
const reportRows = useMemo(() => reportRowsOf(piles, columns, row), [piles, columns, row]);
|
|
@@ -265,9 +269,14 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
265
269
|
return nodes(ordered);
|
|
266
270
|
return renderPiles(piles, nodes);
|
|
267
271
|
}
|
|
272
|
+
const waysOf = (item) => ({
|
|
273
|
+
...(press === undefined ? {} : { press: () => press(item) }),
|
|
274
|
+
...(onPeek === undefined || choosing ? {} : { peek: () => onPeek(item) }),
|
|
275
|
+
...(onOpen === undefined || choosing ? {} : { open: () => onOpen(item) }),
|
|
276
|
+
});
|
|
268
277
|
const renderCard = (item) => {
|
|
269
278
|
if (card !== undefined) {
|
|
270
|
-
return _jsx(Fragment, { children: card(item) }, keyOf(item));
|
|
279
|
+
return _jsx(Fragment, { children: card(item, waysOf(item)) }, keyOf(item));
|
|
271
280
|
}
|
|
272
281
|
const d = row(item);
|
|
273
282
|
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)));
|
|
@@ -331,7 +340,7 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
331
340
|
/* The list is its own container (`@container`: the presentations adapt to the width
|
|
332
341
|
the LIST has, not the window) and fills the space it is given. Before, every page
|
|
333
342
|
wrapped it in a `div` to get both. */
|
|
334
|
-
_jsxs("div", { ref: frame, className: "@container flex min-w-0 flex-1 flex-col bg-surface", children: [_jsxs(Listing, { bar: bar,
|
|
343
|
+
_jsxs("div", { ref: frame, "aria-busy": refreshing || loading, className: "@container flex min-w-0 flex-1 flex-col bg-surface", children: [_jsxs(Listing, { bar: bar,
|
|
335
344
|
/* La barra se queda arriba: con 187 filas, scrollear para ver la última
|
|
336
345
|
dejaba sin buscador, sin paginador y sin el conmutador.
|
|
337
346
|
Embedded it does NOT: the record has its own sticky parts (its rail, its tabs),
|
|
@@ -344,7 +353,9 @@ export function ListView({ items, keyOf, identity, crumbs, embedded = false, sur
|
|
|
344
353
|
metrics: _jsx("div", { className: "px-3.5", children: metrics }),
|
|
345
354
|
}), children: [loading ? _jsx(Skeleton, { label: copy.failed.loading }) : null, isEmpty ? (filtered ? (_jsx(Empty, { sentence: empty?.whenFiltered?.sentence ?? copy.list.emptyFiltered, ...(empty?.whenFiltered?.action === undefined
|
|
346
355
|
? {}
|
|
347
|
-
: { action: empty.whenFiltered.action }) })) : (_jsx(Empty, { sentence: empty?.sentence ?? copy.list.empty, ...(empty?.action === undefined ? {} : { action: empty.action }) }))) : null,
|
|
356
|
+
: { action: empty.whenFiltered.action }) })) : (_jsx(Empty, { sentence: empty?.sentence ?? copy.list.empty, ...(empty?.action === undefined ? {} : { action: empty.action }) }))) : null, _jsxs("div", { className: "relative min-w-0", children: [refreshing ? (
|
|
357
|
+
/* Over the top edge of the rows, not above them: the rows do not move. */
|
|
358
|
+
_jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 z-20", children: _jsx(ProgressBar, { label: copy.list.refreshing, width: "w-full" }) })) : null, loading || isEmpty ? null : body] })] }), error === null ? null : (_jsx("div", { className: "px-3 pt-(--spacing-block)", children: _jsx(Failed, { heading: copy.failed.list, sentence: error }) })), children] }));
|
|
348
359
|
}
|
|
349
360
|
/** What a list offers when it fails and can be retried. */
|
|
350
361
|
export function RetryAction({ onRetry }) {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { List, type ListPresentProps, type ListProps } from "./List.js";
|
|
2
|
-
export { type ListCrumb, type ListEmpty, type ListGroup, type ListPivot, type ListPresent, type ListReport, type ListSet, type ListSheet, type RowOf, } from "./types.js";
|
|
2
|
+
export { type CardWays, type ListCrumb, type ListEmpty, type ListGroup, type ListPivot, type ListPresent, type ListReport, type ListSet, type ListSheet, type RowOf, } from "./types.js";
|
|
@@ -41,6 +41,20 @@ export type RowOf = {
|
|
|
41
41
|
children?: ReactNode;
|
|
42
42
|
actions?: ReactNode;
|
|
43
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* What a press on ONE card does in this list, already bound to its record.
|
|
46
|
+
*
|
|
47
|
+
* Context: a board's own card replaced the built-in one and lost, with it, the rule of what
|
|
48
|
+
* a press does — the application had to pass it by hand. It gets the same three ways as a
|
|
49
|
+
* `List.Present` (`ListSet`), so a card written by the domain answers a click like the
|
|
50
|
+
* list's own: `press` is the rule (choose, look or open, as the list decided); `peek` and
|
|
51
|
+
* `open` are the two ways in, absent while the list is choosing.
|
|
52
|
+
*/
|
|
53
|
+
export type CardWays = {
|
|
54
|
+
press?: () => void;
|
|
55
|
+
peek?: () => void;
|
|
56
|
+
open?: () => void;
|
|
57
|
+
};
|
|
44
58
|
/**
|
|
45
59
|
* The set as a presentation receives it — what a `List.Present` draws from.
|
|
46
60
|
*
|
|
@@ -286,8 +300,8 @@ export type ListViewProps<T extends object> = {
|
|
|
286
300
|
* columns that declare `input` get a control in that row. See `DataTable`'s header.
|
|
287
301
|
* While a record is being written the table shows even if the set is empty. */
|
|
288
302
|
create?: DataTableCreate<T>;
|
|
289
|
-
/** The board presentation. */
|
|
290
|
-
card?: (item: T) => ReactNode;
|
|
303
|
+
/** The board presentation: the card of one record, with what a press on it does. */
|
|
304
|
+
card?: (item: T, ways: CardWays) => ReactNode;
|
|
291
305
|
/** The board by stages: one column each, cards moved between them. */
|
|
292
306
|
boardStages?: BoardStages<T>;
|
|
293
307
|
/** The calendar presentation: when each record happens. */
|
|
@@ -359,8 +373,16 @@ export type ListViewProps<T extends object> = {
|
|
|
359
373
|
/** The three states, in a single place. Every view used to re-assemble them
|
|
360
374
|
* with three nested ternaries, and that is why «empty because of a filter»
|
|
361
375
|
* and «really empty» got mixed up in half the screens. */
|
|
376
|
+
/** Los estados del conjunto, en un solo lugar.
|
|
377
|
+
*
|
|
378
|
+
* `loading` es la PRIMERA lectura: no hay filas todavía, y se dibuja el esqueleto.
|
|
379
|
+
* `refreshing` es volver a preguntar con filas en la mano —se tipeó en la búsqueda, se
|
|
380
|
+
* cambió de página—: las filas se quedan y una barra discreta dice que se está
|
|
381
|
+
* preguntando. Cambiarlas por un esqueleto en cada pregunta hacía parpadear la tabla, y un
|
|
382
|
+
* clic podía caer sobre una fila que ya no estaba. */
|
|
362
383
|
state?: {
|
|
363
384
|
loading?: boolean;
|
|
385
|
+
refreshing?: boolean;
|
|
364
386
|
error?: string | null;
|
|
365
387
|
};
|
|
366
388
|
filtered?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sincpro/web-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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",
|