@sincpro/web-ui 0.0.0-stage → 0.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/LICENSE +21 -0
- package/README.md +157 -2
- package/dist/components/assist/AssistMark.d.ts +64 -0
- package/dist/components/assist/AssistMark.js +108 -0
- package/dist/components/assist/AssistPending.d.ts +24 -0
- package/dist/components/assist/AssistPending.js +26 -0
- package/dist/components/assist/AssistSuggestion.d.ts +42 -0
- package/dist/components/assist/AssistSuggestion.js +55 -0
- package/dist/components/assist/ChangeReview.d.ts +56 -0
- package/dist/components/assist/ChangeReview.js +121 -0
- package/dist/components/assist/assist.d.ts +77 -0
- package/dist/components/assist/assist.js +87 -0
- package/dist/components/assist/index.d.ts +5 -0
- package/dist/components/assist/index.js +7 -0
- package/dist/components/chatter/Chatter.d.ts +161 -0
- package/dist/components/chatter/Chatter.js +232 -0
- package/dist/components/chatter/Messages.d.ts +66 -0
- package/dist/components/chatter/Messages.js +43 -0
- package/dist/components/chatter/Timeline.d.ts +21 -0
- package/dist/components/chatter/Timeline.js +30 -0
- package/dist/components/chatter/index.d.ts +3 -0
- package/dist/components/chatter/index.js +4 -0
- package/dist/components/form/Form.d.ts +52 -0
- package/dist/components/form/Form.js +49 -0
- package/dist/components/form/Sheet.d.ts +43 -0
- package/dist/components/form/Sheet.js +83 -0
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.js +4 -0
- package/dist/components/frame/Chart.d.ts +13 -0
- package/dist/components/frame/Chart.js +24 -0
- package/dist/components/frame/FrameView.d.ts +33 -0
- package/dist/components/frame/FrameView.js +125 -0
- package/dist/components/frame/index.d.ts +3 -0
- package/dist/components/frame/index.js +3 -0
- package/dist/components/frame/model.d.ts +43 -0
- package/dist/components/frame/model.js +101 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.js +11 -0
- package/dist/components/lines/LineItems.d.ts +74 -0
- package/dist/components/lines/LineItems.js +229 -0
- package/dist/components/lines/index.d.ts +1 -0
- package/dist/components/lines/index.js +2 -0
- package/dist/components/lines/lines.d.ts +23 -0
- package/dist/components/lines/lines.js +26 -0
- package/dist/components/listing/ColumnsMenu.d.ts +20 -0
- package/dist/components/listing/ColumnsMenu.js +46 -0
- package/dist/components/listing/GroupingLevels.d.ts +35 -0
- package/dist/components/listing/GroupingLevels.js +45 -0
- package/dist/components/listing/Listing.d.ts +27 -0
- package/dist/components/listing/Listing.js +86 -0
- package/dist/components/listing/ListingBar.d.ts +73 -0
- package/dist/components/listing/ListingBar.js +105 -0
- package/dist/components/listing/Pagination.d.ts +36 -0
- package/dist/components/listing/Pagination.js +69 -0
- package/dist/components/listing/RecordCard.d.ts +33 -0
- package/dist/components/listing/RecordCard.js +22 -0
- package/dist/components/listing/RecordLink.d.ts +7 -0
- package/dist/components/listing/RecordLink.js +22 -0
- package/dist/components/listing/Row.d.ts +106 -0
- package/dist/components/listing/Row.js +122 -0
- package/dist/components/listing/SortMenu.d.ts +62 -0
- package/dist/components/listing/SortMenu.js +105 -0
- package/dist/components/listing/ViewSwitcher.d.ts +24 -0
- package/dist/components/listing/ViewSwitcher.js +16 -0
- package/dist/components/listing/columns.d.ts +47 -0
- package/dist/components/listing/columns.js +72 -0
- package/dist/components/listing/grouping.d.ts +35 -0
- package/dist/components/listing/grouping.js +47 -0
- package/dist/components/listing/index.d.ts +17 -0
- package/dist/components/listing/index.js +20 -0
- package/dist/components/listing/modelColumns.d.ts +50 -0
- package/dist/components/listing/modelColumns.js +103 -0
- package/dist/components/listing/pages.d.ts +78 -0
- package/dist/components/listing/pages.js +135 -0
- package/dist/components/listing/selection.d.ts +21 -0
- package/dist/components/listing/selection.js +43 -0
- package/dist/components/listing/set.d.ts +48 -0
- package/dist/components/listing/set.js +24 -0
- package/dist/components/listing/tree.d.ts +28 -0
- package/dist/components/listing/tree.js +23 -0
- package/dist/components/navigation/CommandPalette.d.ts +40 -0
- package/dist/components/navigation/CommandPalette.js +114 -0
- package/dist/components/navigation/NavigationTree.d.ts +31 -0
- package/dist/components/navigation/NavigationTree.js +79 -0
- package/dist/components/navigation/commands.d.ts +30 -0
- package/dist/components/navigation/commands.js +30 -0
- package/dist/components/navigation/index.d.ts +3 -0
- package/dist/components/navigation/index.js +4 -0
- package/dist/components/record/Breadcrumb.d.ts +42 -0
- package/dist/components/record/Breadcrumb.js +98 -0
- package/dist/components/record/DirtyActions.d.ts +26 -0
- package/dist/components/record/DirtyActions.js +22 -0
- package/dist/components/record/Pager.d.ts +18 -0
- package/dist/components/record/Pager.js +33 -0
- package/dist/components/record/RecordHeader.d.ts +71 -0
- package/dist/components/record/RecordHeader.js +72 -0
- package/dist/components/record/RecordSkeleton.d.ts +3 -0
- package/dist/components/record/RecordSkeleton.js +21 -0
- package/dist/components/record/SmartButtons.d.ts +35 -0
- package/dist/components/record/SmartButtons.js +38 -0
- package/dist/components/record/StatusBar.d.ts +51 -0
- package/dist/components/record/StatusBar.js +168 -0
- package/dist/components/record/ThumbBar.d.ts +36 -0
- package/dist/components/record/ThumbBar.js +86 -0
- package/dist/components/record/index.d.ts +6 -0
- package/dist/components/record/index.js +8 -0
- package/dist/components/search/CriteriaBuilder.d.ts +58 -0
- package/dist/components/search/CriteriaBuilder.js +278 -0
- package/dist/components/search/CriteriaChips.d.ts +22 -0
- package/dist/components/search/CriteriaChips.js +64 -0
- package/dist/components/search/FilterBuilder.d.ts +142 -0
- package/dist/components/search/FilterBuilder.js +421 -0
- package/dist/components/search/JoinWord.d.ts +8 -0
- package/dist/components/search/JoinWord.js +47 -0
- package/dist/components/search/JsonEditor.d.ts +19 -0
- package/dist/components/search/JsonEditor.js +78 -0
- package/dist/components/search/SearchBar.d.ts +78 -0
- package/dist/components/search/SearchBar.js +387 -0
- package/dist/components/search/criteria.d.ts +116 -0
- package/dist/components/search/criteria.js +159 -0
- package/dist/components/search/datePeriods.d.ts +122 -0
- package/dist/components/search/datePeriods.js +134 -0
- package/dist/components/search/dateRanges.d.ts +54 -0
- package/dist/components/search/dateRanges.js +89 -0
- package/dist/components/search/index.d.ts +9 -0
- package/dist/components/search/index.js +20 -0
- package/dist/components/search/model.d.ts +54 -0
- package/dist/components/search/model.js +37 -0
- package/dist/components/search/query.d.ts +166 -0
- package/dist/components/search/query.js +436 -0
- package/dist/components/shell/AppFrame.d.ts +14 -0
- package/dist/components/shell/AppFrame.js +80 -0
- package/dist/components/shell/TopBar.d.ts +73 -0
- package/dist/components/shell/TopBar.js +54 -0
- package/dist/components/shell/headerSlot.d.ts +17 -0
- package/dist/components/shell/headerSlot.js +21 -0
- package/dist/components/shell/index.d.ts +2 -0
- package/dist/components/shell/index.js +3 -0
- package/dist/foundations/aria.d.ts +14 -0
- package/dist/foundations/aria.js +18 -0
- package/dist/foundations/calendar.d.ts +28 -0
- package/dist/foundations/calendar.js +183 -0
- package/dist/foundations/copy.d.ts +619 -0
- package/dist/foundations/copy.es.d.ts +2 -0
- package/dist/foundations/copy.es.js +377 -0
- package/dist/foundations/copy.js +472 -0
- package/dist/foundations/density.d.ts +16 -0
- package/dist/foundations/density.js +21 -0
- package/dist/foundations/grouping.d.ts +281 -0
- package/dist/foundations/grouping.js +0 -0
- package/dist/foundations/index.d.ts +14 -0
- package/dist/foundations/index.js +30 -0
- package/dist/foundations/layout.d.ts +9 -0
- package/dist/foundations/layout.js +9 -0
- package/dist/foundations/pivot.d.ts +222 -0
- package/dist/foundations/pivot.js +198 -0
- package/dist/foundations/pivotSheet.d.ts +61 -0
- package/dist/foundations/pivotSheet.js +0 -0
- package/dist/foundations/slots.d.ts +67 -0
- package/dist/foundations/slots.js +63 -0
- package/dist/foundations/sticky.d.ts +53 -0
- package/dist/foundations/sticky.js +150 -0
- package/dist/foundations/surface.d.ts +9 -0
- package/dist/foundations/surface.js +26 -0
- package/dist/foundations/theme.d.ts +42 -0
- package/dist/foundations/theme.js +88 -0
- package/dist/foundations/types.d.ts +23 -0
- package/dist/foundations/types.js +50 -0
- package/dist/foundations/variants.d.ts +19 -0
- package/dist/foundations/variants.js +48 -0
- package/dist/foundations/xlsx.d.ts +86 -0
- package/dist/foundations/xlsx.js +0 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/primitives/block/Block.d.ts +66 -0
- package/dist/primitives/block/Block.js +34 -0
- package/dist/primitives/block/Line.d.ts +18 -0
- package/dist/primitives/block/Line.js +45 -0
- package/dist/primitives/block/SectionGrid.d.ts +30 -0
- package/dist/primitives/block/SectionGrid.js +29 -0
- package/dist/primitives/block/Steps.d.ts +76 -0
- package/dist/primitives/block/Steps.js +43 -0
- package/dist/primitives/block/index.d.ts +4 -0
- package/dist/primitives/block/index.js +7 -0
- package/dist/primitives/button/Button.d.ts +137 -0
- package/dist/primitives/button/Button.js +234 -0
- package/dist/primitives/button/index.d.ts +1 -0
- package/dist/primitives/button/index.js +2 -0
- package/dist/primitives/cell/CellStack.d.ts +47 -0
- package/dist/primitives/cell/CellStack.js +33 -0
- package/dist/primitives/cell/index.d.ts +1 -0
- package/dist/primitives/cell/index.js +3 -0
- package/dist/primitives/choice/ComboBox.d.ts +16 -0
- package/dist/primitives/choice/ComboBox.js +52 -0
- package/dist/primitives/choice/RadioGroup.d.ts +18 -0
- package/dist/primitives/choice/RadioGroup.js +50 -0
- package/dist/primitives/choice/SearchField.d.ts +24 -0
- package/dist/primitives/choice/SearchField.js +27 -0
- package/dist/primitives/choice/SegmentedControl.d.ts +16 -0
- package/dist/primitives/choice/SegmentedControl.js +69 -0
- package/dist/primitives/choice/Slider.d.ts +16 -0
- package/dist/primitives/choice/Slider.js +46 -0
- package/dist/primitives/choice/Switch.d.ts +14 -0
- package/dist/primitives/choice/Switch.js +65 -0
- package/dist/primitives/choice/ThemeSwitcher.d.ts +10 -0
- package/dist/primitives/choice/ThemeSwitcher.js +25 -0
- package/dist/primitives/choice/index.d.ts +8 -0
- package/dist/primitives/choice/index.js +11 -0
- package/dist/primitives/choice/options.d.ts +28 -0
- package/dist/primitives/choice/options.js +6 -0
- package/dist/primitives/date/Calendar.d.ts +31 -0
- package/dist/primitives/date/Calendar.js +110 -0
- package/dist/primitives/date/DatePicker.d.ts +34 -0
- package/dist/primitives/date/DatePicker.js +99 -0
- package/dist/primitives/date/index.d.ts +2 -0
- package/dist/primitives/date/index.js +4 -0
- package/dist/primitives/dialog/Dialog.d.ts +118 -0
- package/dist/primitives/dialog/Dialog.js +121 -0
- package/dist/primitives/dialog/index.d.ts +1 -0
- package/dist/primitives/dialog/index.js +2 -0
- package/dist/primitives/disclosure/Disclosure.d.ts +36 -0
- package/dist/primitives/disclosure/Disclosure.js +52 -0
- package/dist/primitives/disclosure/index.d.ts +1 -0
- package/dist/primitives/disclosure/index.js +2 -0
- package/dist/primitives/field/Checks.d.ts +57 -0
- package/dist/primitives/field/Checks.js +72 -0
- package/dist/primitives/field/DropZone.d.ts +42 -0
- package/dist/primitives/field/DropZone.js +52 -0
- package/dist/primitives/field/Field.d.ts +166 -0
- package/dist/primitives/field/Field.js +240 -0
- package/dist/primitives/field/Suggest.d.ts +48 -0
- package/dist/primitives/field/Suggest.js +43 -0
- package/dist/primitives/field/aria.d.ts +61 -0
- package/dist/primitives/field/aria.js +70 -0
- package/dist/primitives/field/context.d.ts +30 -0
- package/dist/primitives/field/context.js +13 -0
- package/dist/primitives/field/index.d.ts +7 -0
- package/dist/primitives/field/index.js +11 -0
- package/dist/primitives/field/layout.d.ts +24 -0
- package/dist/primitives/field/layout.js +10 -0
- package/dist/primitives/field/mode.d.ts +9 -0
- package/dist/primitives/field/mode.js +13 -0
- package/dist/primitives/hint/Hint.d.ts +31 -0
- package/dist/primitives/hint/Hint.js +42 -0
- package/dist/primitives/hint/index.d.ts +1 -0
- package/dist/primitives/hint/index.js +1 -0
- package/dist/primitives/index.d.ts +19 -0
- package/dist/primitives/index.js +20 -0
- package/dist/primitives/menu/Menu.d.ts +100 -0
- package/dist/primitives/menu/Menu.js +59 -0
- package/dist/primitives/menu/index.d.ts +1 -0
- package/dist/primitives/menu/index.js +2 -0
- package/dist/primitives/notice/Callout.d.ts +49 -0
- package/dist/primitives/notice/Callout.js +28 -0
- package/dist/primitives/notice/Notice.d.ts +45 -0
- package/dist/primitives/notice/Notice.js +61 -0
- package/dist/primitives/notice/Toast.d.ts +55 -0
- package/dist/primitives/notice/Toast.js +142 -0
- package/dist/primitives/notice/index.d.ts +3 -0
- package/dist/primitives/notice/index.js +5 -0
- package/dist/primitives/overlay/Popover.d.ts +6 -0
- package/dist/primitives/overlay/Popover.js +27 -0
- package/dist/primitives/overlay/PopoverTrigger.d.ts +55 -0
- package/dist/primitives/overlay/PopoverTrigger.js +38 -0
- package/dist/primitives/overlay/Tooltip.d.ts +38 -0
- package/dist/primitives/overlay/Tooltip.js +74 -0
- package/dist/primitives/overlay/index.d.ts +3 -0
- package/dist/primitives/overlay/index.js +5 -0
- package/dist/primitives/picker/Picker.d.ts +48 -0
- package/dist/primitives/picker/Picker.js +150 -0
- package/dist/primitives/picker/index.d.ts +1 -0
- package/dist/primitives/picker/index.js +2 -0
- package/dist/primitives/surface/Peek.d.ts +60 -0
- package/dist/primitives/surface/Peek.js +71 -0
- package/dist/primitives/surface/index.d.ts +1 -0
- package/dist/primitives/surface/index.js +2 -0
- package/dist/primitives/table/CellEditor.d.ts +14 -0
- package/dist/primitives/table/CellEditor.js +83 -0
- package/dist/primitives/table/DataTable.d.ts +135 -0
- package/dist/primitives/table/DataTable.js +244 -0
- package/dist/primitives/table/EditableTable.d.ts +96 -0
- package/dist/primitives/table/EditableTable.js +423 -0
- package/dist/primitives/table/PivotTable.d.ts +14 -0
- package/dist/primitives/table/PivotTable.js +0 -0
- package/dist/primitives/table/ReportTable.d.ts +38 -0
- package/dist/primitives/table/ReportTable.js +106 -0
- package/dist/primitives/table/RowActions.d.ts +16 -0
- package/dist/primitives/table/RowActions.js +28 -0
- package/dist/primitives/table/SheetTable.d.ts +70 -0
- package/dist/primitives/table/SheetTable.js +197 -0
- package/dist/primitives/table/Table.d.ts +106 -0
- package/dist/primitives/table/Table.js +116 -0
- package/dist/primitives/table/index.d.ts +7 -0
- package/dist/primitives/table/index.js +9 -0
- package/dist/primitives/tabs/Tabs.d.ts +91 -0
- package/dist/primitives/tabs/Tabs.js +56 -0
- package/dist/primitives/tabs/index.d.ts +1 -0
- package/dist/primitives/tabs/index.js +1 -0
- package/dist/primitives/tag/Tag.d.ts +36 -0
- package/dist/primitives/tag/Tag.js +68 -0
- package/dist/primitives/tag/Tags.d.ts +57 -0
- package/dist/primitives/tag/Tags.js +104 -0
- package/dist/primitives/tag/TagsField.d.ts +32 -0
- package/dist/primitives/tag/TagsField.js +182 -0
- package/dist/primitives/tag/choices.d.ts +38 -0
- package/dist/primitives/tag/choices.js +26 -0
- package/dist/primitives/tag/index.d.ts +4 -0
- package/dist/primitives/tag/index.js +8 -0
- package/dist/primitives/toolbar/Toolbar.d.ts +13 -0
- package/dist/primitives/toolbar/Toolbar.js +36 -0
- package/dist/primitives/toolbar/index.d.ts +1 -0
- package/dist/primitives/toolbar/index.js +2 -0
- package/dist/primitives/value/Copyable.d.ts +37 -0
- package/dist/primitives/value/Copyable.js +50 -0
- package/dist/primitives/value/Fecha.d.ts +12 -0
- package/dist/primitives/value/Fecha.js +80 -0
- package/dist/primitives/value/Meter.d.ts +29 -0
- package/dist/primitives/value/Meter.js +46 -0
- package/dist/primitives/value/Metric.d.ts +42 -0
- package/dist/primitives/value/Metric.js +38 -0
- package/dist/primitives/value/Person.d.ts +90 -0
- package/dist/primitives/value/Person.js +208 -0
- package/dist/primitives/value/Relation.d.ts +69 -0
- package/dist/primitives/value/Relation.js +57 -0
- package/dist/primitives/value/Totals.d.ts +26 -0
- package/dist/primitives/value/Totals.js +4 -0
- package/dist/primitives/value/Value.d.ts +77 -0
- package/dist/primitives/value/Value.js +119 -0
- package/dist/primitives/value/index.d.ts +8 -0
- package/dist/primitives/value/index.js +9 -0
- package/dist/tokens.css +1025 -0
- package/dist/views/index.d.ts +2 -0
- package/dist/views/index.js +3 -0
- package/dist/views/list/List.d.ts +113 -0
- package/dist/views/list/List.js +97 -0
- package/dist/views/list/ListView.d.ts +6 -0
- package/dist/views/list/ListView.js +269 -0
- package/dist/views/list/columns.d.ts +18 -0
- package/dist/views/list/columns.js +27 -0
- package/dist/views/list/derive.d.ts +76 -0
- package/dist/views/list/derive.js +181 -0
- package/dist/views/list/index.d.ts +2 -0
- package/dist/views/list/index.js +4 -0
- package/dist/views/list/presentations.d.ts +39 -0
- package/dist/views/list/presentations.js +103 -0
- package/dist/views/list/types.d.ts +272 -0
- package/dist/views/list/types.js +1 -0
- package/dist/views/list/useNarrowList.d.ts +17 -0
- package/dist/views/list/useNarrowList.js +32 -0
- package/dist/views/record/Record.d.ts +126 -0
- package/dist/views/record/Record.js +141 -0
- package/dist/views/record/index.d.ts +1 -0
- package/dist/views/record/index.js +2 -0
- package/package.json +109 -4
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { Tone } from "../../foundations/types.js";
|
|
2
|
+
import { type RowAction } from "./RowActions.js";
|
|
3
|
+
import { type SortState } from "./Table.js";
|
|
4
|
+
import { type ReactNode } from "react";
|
|
5
|
+
/** How a column is aggregated when its rows are pulled together into a group.
|
|
6
|
+
*
|
|
7
|
+
* **`count` is the default and it is the right one almost always**: summing
|
|
8
|
+
* identifiers or averaging file sizes produces numbers that look like data and
|
|
9
|
+
* mean nothing. An aggregate has to be chosen on purpose. */
|
|
10
|
+
export type Aggregate = "count" | "sum" | "mean" | "min" | "max" | "unique";
|
|
11
|
+
export interface DataColumn<T> {
|
|
12
|
+
id: string;
|
|
13
|
+
header: string;
|
|
14
|
+
/** Qué campo del MODELO muestra esta columna, cuando no se llama igual que
|
|
15
|
+
* él: «filas» muestra `row_count`. Lo lee `columnsFromModel` para no volver
|
|
16
|
+
* a agregar, en crudo, un campo que esta columna ya contesta. */
|
|
17
|
+
field?: string;
|
|
18
|
+
/** The raw value: this is what sorting and grouping run on. */
|
|
19
|
+
value: (row: T) => string | number | boolean | null;
|
|
20
|
+
/** How it is drawn. Without this the raw value is drawn. */
|
|
21
|
+
cell?: (row: T) => ReactNode;
|
|
22
|
+
/** A column of CODES — a SKU, a hash, a key — set in the monospaced face. Numbers do not
|
|
23
|
+
* need it: they align with tabular figures in the same face as the rest of the row. */
|
|
24
|
+
mono?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Cómo se LEE la columna donde no se puede dibujar: la planilla, que es una
|
|
27
|
+
* cuadrícula de texto y números y no puede recibir un nodo.
|
|
28
|
+
*
|
|
29
|
+
* Context: sin esto, la planilla caía a `value`, que es la clave de ORDEN y
|
|
30
|
+
* no lo que una persona lee. La columna «estado» de Facturas mostraba
|
|
31
|
+
* `posted` y `paid` —los valores del contrato— en vez de «publicada» y
|
|
32
|
+
* «pagada», que es lo que dice la ficha de al lado en la tabla. Dos
|
|
33
|
+
* presentaciones del mismo conjunto diciendo cosas distintas.
|
|
34
|
+
*/
|
|
35
|
+
text?: (row: T) => string;
|
|
36
|
+
/** Paints the BACKGROUND of the cell, row by row. It is what turns a table
|
|
37
|
+
* into something you look at: «this amount is out of range», «this value was
|
|
38
|
+
* imputed by the engine». Returning `undefined` leaves the cell unpainted,
|
|
39
|
+
* which is the normal case — a table with everything colored highlights
|
|
40
|
+
* nothing. */
|
|
41
|
+
tone?: (row: T) => Tone | undefined;
|
|
42
|
+
/** Why it is painted, on hover. */
|
|
43
|
+
note?: (row: T) => string | undefined;
|
|
44
|
+
numeric?: boolean;
|
|
45
|
+
width?: number;
|
|
46
|
+
aggregate?: Aggregate;
|
|
47
|
+
sortable?: boolean;
|
|
48
|
+
/** Odoo: fields without `optional` cannot leave the view. When true, the
|
|
49
|
+
* column appears in the columns submenu and can be hidden. */
|
|
50
|
+
optional?: boolean;
|
|
51
|
+
/** Odoo `optional="hide"`: start off. Only with `optional`. */
|
|
52
|
+
defaultHidden?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Edit this cell IN the list, through a small popover anchored to it: the salesperson of
|
|
55
|
+
* an invoice, its due date, its stage. The cell becomes a button (with a pencil on hover
|
|
56
|
+
* and focus) and `render` draws the editor inside the popover.
|
|
57
|
+
*
|
|
58
|
+
* Context: one function and not a kind of editor, because the editor is the system's own
|
|
59
|
+
* control — a `Picker`, a `Calendar`, a `Select` — written by whoever knows the field.
|
|
60
|
+
* The editor commits with its own gesture and then calls `done`, which closes the popover
|
|
61
|
+
* and returns the focus to the cell. Escape and a click outside close without committing.
|
|
62
|
+
* Do not draw links in an editable column: the cell is already a button.
|
|
63
|
+
*/
|
|
64
|
+
edit?: {
|
|
65
|
+
render: (row: T, done: () => void) => ReactNode;
|
|
66
|
+
/** Which rows can be changed — a paid invoice cannot. All of them by default. */
|
|
67
|
+
when?: (row: T) => boolean;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* El agregado de una columna sobre un conjunto de filas.
|
|
72
|
+
*
|
|
73
|
+
* Vive afuera del componente porque lo preguntan tres lugares: el pie de la
|
|
74
|
+
* tabla, la banda de un montón en la planilla, y la línea de un informe armado
|
|
75
|
+
* a partir de los montones. Tres implementaciones de «cuánto suma esto» es que
|
|
76
|
+
* el pie diga un número y el montón otro.
|
|
77
|
+
*
|
|
78
|
+
* `null` cuando la columna no declaró agregado, no es numérica, o no hay ningún
|
|
79
|
+
* número que sumar — que es distinto de sumar cero.
|
|
80
|
+
*/
|
|
81
|
+
export declare function aggregateOf<T extends object>(column: DataColumn<T>, rows: readonly T[]): number | null;
|
|
82
|
+
/**
|
|
83
|
+
* UN NIVEL, como lo necesita una tabla.
|
|
84
|
+
*
|
|
85
|
+
* La identidad es la del NIVEL y no la del campo: un mismo `registered_at`
|
|
86
|
+
* puesto por mes y por día son dos cortes, y con el nombre del campo serían el
|
|
87
|
+
* mismo repetido. `keyOf` da la clave del motor —la misma que arman las otras
|
|
88
|
+
* presentaciones, así que lo plegado vale para todas— y `labelOf` la lee en el
|
|
89
|
+
* idioma activo.
|
|
90
|
+
*/
|
|
91
|
+
export type GroupColumn<T> = {
|
|
92
|
+
/** La identidad del nivel: `levelKey(level)`. */
|
|
93
|
+
id: string;
|
|
94
|
+
/** Cómo se llama el corte, a la izquierda del valor. Sin él, «Septiembre» no
|
|
95
|
+
* dice si es cuándo se registró o cuándo se corrió. */
|
|
96
|
+
label: string;
|
|
97
|
+
/** A qué montón va cada fila en este nivel. */
|
|
98
|
+
keyOf: (item: T) => string;
|
|
99
|
+
/** Cómo se lee esa clave. */
|
|
100
|
+
labelOf: (key: string) => string;
|
|
101
|
+
};
|
|
102
|
+
export declare function DataTable<T extends object>({ data, columns, getRowId, sort, onSortChange, levels, selected, onSelectedChange, height, onRowClick, rowActions, totals, collapsed, onGroupToggle, }: {
|
|
103
|
+
data: readonly T[];
|
|
104
|
+
columns: readonly DataColumn<T>[];
|
|
105
|
+
getRowId: (row: T) => string;
|
|
106
|
+
sort?: SortState | null;
|
|
107
|
+
onSortChange?: (next: SortState | null) => void;
|
|
108
|
+
/**
|
|
109
|
+
* Los NIVELES por los que se parte, de afuera hacia adentro. Vacío o ausente
|
|
110
|
+
* es «sin agrupar».
|
|
111
|
+
*
|
|
112
|
+
* Varios, porque agrupar no es una pregunta: «por cliente y después por
|
|
113
|
+
* mes» es un árbol y no una lista de montones. El orden ES la jerarquía.
|
|
114
|
+
*/
|
|
115
|
+
levels?: readonly GroupColumn<T>[];
|
|
116
|
+
selected?: ReadonlySet<string>;
|
|
117
|
+
onSelectedChange?: (next: Set<string>) => void;
|
|
118
|
+
height?: number;
|
|
119
|
+
onRowClick?: (row: T) => void;
|
|
120
|
+
/** What you do to ONE row without going into it. They are drawn hidden and
|
|
121
|
+
* show up on the hovered row — see `RowActions.tsx`. */
|
|
122
|
+
rowActions?: (row: T) => readonly RowAction[];
|
|
123
|
+
/** The footer with the aggregate of every column that declared one.
|
|
124
|
+
*
|
|
125
|
+
* Context: up to now the aggregate only existed INSIDE a group, so an
|
|
126
|
+
* ungrouped table had no way to say how much it adds up to — and «how much
|
|
127
|
+
* does it add up to» is the question that makes a table of amounts a report
|
|
128
|
+
* and not a list. It stays pinned at the bottom: in a four-hundred-row table,
|
|
129
|
+
* a total you have to scroll to is a total nobody sees. */
|
|
130
|
+
totals?: boolean;
|
|
131
|
+
/** Group keys that are closed. Same keys as `ListGroup.collapsed`, so the
|
|
132
|
+
* table does not reopen what rows just folded. */
|
|
133
|
+
collapsed?: readonly string[];
|
|
134
|
+
onGroupToggle?: (key: string) => void;
|
|
135
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { fillCopy, useUiCopy } from "../../foundations/copy.js";
|
|
4
|
+
import { pathKey } from "../../foundations/grouping.js";
|
|
5
|
+
import { CellEditor } from "./CellEditor.js";
|
|
6
|
+
import { RowActions } from "./RowActions.js";
|
|
7
|
+
import { SelectBox, Table, Td, Th, THead, Tr, } from "./Table.js";
|
|
8
|
+
import { aggregationFns, columnGroupingFeature, createColumnHelper, createExpandedRowModel, createGroupedRowModel, createSortedRowModel, rowAggregationFeature, rowExpandingFeature, rowSortingFeature, sortFns, tableFeatures, useTable, } from "@tanstack/react-table";
|
|
9
|
+
import { ChevronRight } from "lucide-react";
|
|
10
|
+
import { useMemo } from "react";
|
|
11
|
+
function aggregateLabel(copy, kind, v) {
|
|
12
|
+
const n = v.toLocaleString(copy.locale);
|
|
13
|
+
switch (kind) {
|
|
14
|
+
case "sum":
|
|
15
|
+
return n;
|
|
16
|
+
case "count":
|
|
17
|
+
return `${n} ${v === 1 ? copy.table.row : copy.table.rows}`;
|
|
18
|
+
case "mean":
|
|
19
|
+
return fillCopy(copy.table.mean, {
|
|
20
|
+
value: v.toLocaleString(copy.locale, { maximumFractionDigits: 2 }),
|
|
21
|
+
});
|
|
22
|
+
case "min":
|
|
23
|
+
return fillCopy(copy.table.min, { value: n });
|
|
24
|
+
case "max":
|
|
25
|
+
return fillCopy(copy.table.max, { value: n });
|
|
26
|
+
case "unique":
|
|
27
|
+
return fillCopy(copy.table.distinct, { value: n });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function raw(copy, v) {
|
|
31
|
+
if (v === null)
|
|
32
|
+
return "—";
|
|
33
|
+
if (typeof v === "number")
|
|
34
|
+
return v.toLocaleString(copy.locale);
|
|
35
|
+
if (typeof v === "boolean")
|
|
36
|
+
return v ? copy.table.yes : copy.table.no;
|
|
37
|
+
return v;
|
|
38
|
+
}
|
|
39
|
+
/** What a cell draws: its `cell`, or the raw value. */
|
|
40
|
+
function drawCell(copy, column, row) {
|
|
41
|
+
return column.cell === undefined ? raw(copy, column.value(row)) : column.cell(row);
|
|
42
|
+
}
|
|
43
|
+
/* The features are declared once. In v9 they are opt-in: asking only for what
|
|
44
|
+
is used is what keeps the bundle down to what is actually needed. */
|
|
45
|
+
const FEATURES = tableFeatures({
|
|
46
|
+
rowSortingFeature,
|
|
47
|
+
columnGroupingFeature,
|
|
48
|
+
rowExpandingFeature,
|
|
49
|
+
// `rowAggregationFeature` is what enables `aggregationFn` on a column.
|
|
50
|
+
// Without it, grouping pulls rows together but each column does not know what
|
|
51
|
+
// to show in the group row — which is half of the reason for grouping.
|
|
52
|
+
rowAggregationFeature,
|
|
53
|
+
sortedRowModel: createSortedRowModel(),
|
|
54
|
+
groupedRowModel: createGroupedRowModel(),
|
|
55
|
+
expandedRowModel: createExpandedRowModel(),
|
|
56
|
+
sortFns,
|
|
57
|
+
aggregationFns,
|
|
58
|
+
});
|
|
59
|
+
/**
|
|
60
|
+
* El agregado de una columna sobre un conjunto de filas.
|
|
61
|
+
*
|
|
62
|
+
* Vive afuera del componente porque lo preguntan tres lugares: el pie de la
|
|
63
|
+
* tabla, la banda de un montón en la planilla, y la línea de un informe armado
|
|
64
|
+
* a partir de los montones. Tres implementaciones de «cuánto suma esto» es que
|
|
65
|
+
* el pie diga un número y el montón otro.
|
|
66
|
+
*
|
|
67
|
+
* `null` cuando la columna no declaró agregado, no es numérica, o no hay ningún
|
|
68
|
+
* número que sumar — que es distinto de sumar cero.
|
|
69
|
+
*/
|
|
70
|
+
export function aggregateOf(column, rows) {
|
|
71
|
+
if (column.aggregate === undefined || column.numeric !== true)
|
|
72
|
+
return null;
|
|
73
|
+
const numbers = rows
|
|
74
|
+
.map((row) => column.value(row))
|
|
75
|
+
.filter((value) => typeof value === "number");
|
|
76
|
+
if (numbers.length === 0)
|
|
77
|
+
return null;
|
|
78
|
+
switch (column.aggregate) {
|
|
79
|
+
case "sum":
|
|
80
|
+
return numbers.reduce((a, b) => a + b, 0);
|
|
81
|
+
case "mean":
|
|
82
|
+
return numbers.reduce((a, b) => a + b, 0) / numbers.length;
|
|
83
|
+
case "min":
|
|
84
|
+
return Math.min(...numbers);
|
|
85
|
+
case "max":
|
|
86
|
+
return Math.max(...numbers);
|
|
87
|
+
case "unique":
|
|
88
|
+
return new Set(numbers).size;
|
|
89
|
+
default:
|
|
90
|
+
return numbers.length;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export function DataTable({ data, columns, getRowId, sort, onSortChange, levels, selected, onSelectedChange, height, onRowClick, rowActions, totals = false, collapsed, onGroupToggle, }) {
|
|
94
|
+
const copy = useUiCopy();
|
|
95
|
+
// In v9 the helper is bound to the features AND to the data type: that is what
|
|
96
|
+
// makes the compiler know about `enableGrouping` only if we ask for the
|
|
97
|
+
// feature.
|
|
98
|
+
const helper = useMemo(() => createColumnHelper(), []);
|
|
99
|
+
const defs = useMemo(() => [
|
|
100
|
+
...columns.map((c) => helper.accessor((row) => c.value(row), {
|
|
101
|
+
id: c.id,
|
|
102
|
+
header: c.header,
|
|
103
|
+
enableSorting: c.sortable !== false,
|
|
104
|
+
/* Ninguna columna agrupa. Lo que parte el conjunto son los NIVELES, y
|
|
105
|
+
cada uno entra como una columna suya —abajo— que existe sólo para
|
|
106
|
+
eso: nadie la dibuja y nadie la ordena. */
|
|
107
|
+
enableGrouping: false,
|
|
108
|
+
aggregationFn: c.aggregate ?? "count",
|
|
109
|
+
})),
|
|
110
|
+
/* Una columna por NIVEL, no por campo. Es la única forma de que «por mes y
|
|
111
|
+
adentro por día» sea dos cortes: con el id de la columna, los dos niveles
|
|
112
|
+
de `registered_at` eran el mismo id repetido, y un mismo id no se puede
|
|
113
|
+
agrupar dos veces.
|
|
114
|
+
|
|
115
|
+
Y la clave la pone el nivel, no la columna: `value` es la clave de
|
|
116
|
+
ORDEN —para una fecha, los milisegundos— y agrupar por ella deja un
|
|
117
|
+
montón por instante, con «1789294903895» de rótulo. Acá entra la misma
|
|
118
|
+
clave que arman las otras cinco presentaciones, así que los montones de
|
|
119
|
+
la tabla son LOS MISMOS y lo plegado vale para todas. */
|
|
120
|
+
...(levels ?? []).map((level) => helper.accessor((row) => level.keyOf(row), {
|
|
121
|
+
id: level.id,
|
|
122
|
+
header: level.label,
|
|
123
|
+
enableSorting: false,
|
|
124
|
+
enableGrouping: true,
|
|
125
|
+
})),
|
|
126
|
+
], [columns, helper, levels]);
|
|
127
|
+
/* The assertion, and why it is safe: `ColumnDef` is INVARIANT in its value
|
|
128
|
+
type (`in out TValue`), so an array of columns with concrete values is not
|
|
129
|
+
assignable to the one of `unknown` even though each one on its own is. The
|
|
130
|
+
library solves this with `helper.columns([...])`, which needs a literal
|
|
131
|
+
tuple — and here the columns come out of a `map` over what the consumer
|
|
132
|
+
declared, which is not literal.
|
|
133
|
+
|
|
134
|
+
It is safe because that value is only used by the library to sort and
|
|
135
|
+
group; it is never read with its type from our side. What gets drawn comes
|
|
136
|
+
from `c.cell(row)` or from `c.value(row)`, both typed against `T`. */
|
|
137
|
+
const table = useTable({
|
|
138
|
+
features: FEATURES,
|
|
139
|
+
data,
|
|
140
|
+
columns: defs,
|
|
141
|
+
getRowId: (row) => getRowId(row),
|
|
142
|
+
state: {
|
|
143
|
+
sorting: sort === null || sort === undefined
|
|
144
|
+
? []
|
|
145
|
+
: [{ id: sort.key, desc: sort.direction === "desc" }],
|
|
146
|
+
grouping: (levels ?? []).map((level) => level.id),
|
|
147
|
+
},
|
|
148
|
+
// Groups start open: grouping and then having to open each group is two
|
|
149
|
+
// gestures for what the user asked for in one.
|
|
150
|
+
initialState: { expanded: true },
|
|
151
|
+
});
|
|
152
|
+
const rows = table.getRowModel().rows;
|
|
153
|
+
/**
|
|
154
|
+
* The PATH of a row: the values of every group it hangs from, outermost
|
|
155
|
+
* first.
|
|
156
|
+
*
|
|
157
|
+
* The identity of a pile is its route and not its value, because with several
|
|
158
|
+
* levels two piles can be called the same — «2026-09» inside «Ferretería Sur» and
|
|
159
|
+
* «2026-09» inside «Distribuidora Andina» are different piles, and folding one must not
|
|
160
|
+
* fold the other. It walks up because that is where TanStack keeps the
|
|
161
|
+
* ancestry; a leaf contributes nothing, since `groupingColumnId` is only set
|
|
162
|
+
* on grouped rows.
|
|
163
|
+
*/
|
|
164
|
+
function pathOf(row) {
|
|
165
|
+
const path = [];
|
|
166
|
+
let node = row;
|
|
167
|
+
while (node !== undefined) {
|
|
168
|
+
const columnId = node.groupingColumnId;
|
|
169
|
+
if (columnId !== undefined && columnId !== null) {
|
|
170
|
+
path.unshift(String(node.getGroupingValue(columnId) ?? "—"));
|
|
171
|
+
}
|
|
172
|
+
node = node.getParentRow();
|
|
173
|
+
}
|
|
174
|
+
return path;
|
|
175
|
+
}
|
|
176
|
+
/* TanStack keeps every leaf in the model when `expanded: true`. Folding is
|
|
177
|
+
ours: the same path keys as the rows presentation, so switching present
|
|
178
|
+
does not reopen a group.
|
|
179
|
+
|
|
180
|
+
It checks EVERY ancestor and not just the parent. With one level they were
|
|
181
|
+
the same thing; with several, folding the outer pile has to take the inner
|
|
182
|
+
ones with it — otherwise closing «Ferretería Sur» leaves its months on screen,
|
|
183
|
+
orphaned under a header that says it is closed. */
|
|
184
|
+
const drawn = collapsed === undefined
|
|
185
|
+
? rows
|
|
186
|
+
: rows.filter((row) => {
|
|
187
|
+
let node = row.getParentRow();
|
|
188
|
+
while (node !== undefined) {
|
|
189
|
+
if (collapsed.includes(pathKey(pathOf(node))))
|
|
190
|
+
return false;
|
|
191
|
+
node = node.getParentRow();
|
|
192
|
+
}
|
|
193
|
+
return true;
|
|
194
|
+
});
|
|
195
|
+
const selecting = onSelectedChange !== undefined;
|
|
196
|
+
const leaves = useMemo(() => rows.filter((r) => r.subRows.length === 0), [rows]);
|
|
197
|
+
/* The aggregates are computed by the component and not by TanStack: its
|
|
198
|
+
`aggregationFn` lives in the GROUP model, and a footer exists precisely when
|
|
199
|
+
there are no groups. */
|
|
200
|
+
const footer = useMemo(() => (totals ? columns.map((c) => aggregateOf(c, data)) : null), [totals, columns, data]);
|
|
201
|
+
const all = leaves.length > 0 && leaves.every((r) => selected?.has(r.id) === true);
|
|
202
|
+
const some = leaves.some((r) => selected?.has(r.id) === true);
|
|
203
|
+
function toggle(id, on) {
|
|
204
|
+
const n = new Set(selected ?? []);
|
|
205
|
+
if (on)
|
|
206
|
+
n.add(id);
|
|
207
|
+
else
|
|
208
|
+
n.delete(id);
|
|
209
|
+
onSelectedChange?.(n);
|
|
210
|
+
}
|
|
211
|
+
return (_jsxs(Table, { height: height, children: [_jsxs(THead, { children: [selecting ? (_jsx(Th, { width: 36, children: _jsx(SelectBox, { label: copy.selection.all, checked: all, indeterminate: some, onChange: (v) => onSelectedChange?.(v ? new Set(leaves.map((r) => r.id)) : new Set()) }) })) : null, columns.map((c) => (_jsx(Th, { sortKey: c.sortable === false ? undefined : c.id, sort: sort, onSort: onSortChange, numeric: c.numeric, width: c.width, children: c.header }, c.id))), rowActions === undefined ? null : _jsx(Th, { width: 1, children: "" })] }), _jsx("tbody", { children: drawn.map((row) => {
|
|
212
|
+
const isGroup = row.subRows.length > 0;
|
|
213
|
+
if (isGroup) {
|
|
214
|
+
const path = pathOf(row);
|
|
215
|
+
const key = pathKey(path);
|
|
216
|
+
const depth = path.length - 1;
|
|
217
|
+
const id = row.groupingColumnId ?? undefined;
|
|
218
|
+
const level = levels?.find((one) => one.id === id);
|
|
219
|
+
/* La CLAVE del montón —la del motor— y el rótulo, que lo pone el
|
|
220
|
+
nivel: son dos textos y sólo el segundo cambia con el idioma. */
|
|
221
|
+
const value = path[depth] ?? key;
|
|
222
|
+
const open = collapsed === undefined
|
|
223
|
+
? row.getIsExpanded()
|
|
224
|
+
: collapsed.includes(key) !== true;
|
|
225
|
+
return (
|
|
226
|
+
/* The group row: it spans the whole width, says which value it
|
|
227
|
+
groups by and how many are inside. The counter is what makes it
|
|
228
|
+
useful without opening it. */
|
|
229
|
+
_jsx("tr", { className: "border-b border-line bg-ground", children: _jsx("td", { colSpan: columns.length + (selecting ? 1 : 0) + (rowActions === undefined ? 0 : 1), className: "px-3 py-1.5", style: depth === 0
|
|
230
|
+
? undefined
|
|
231
|
+
: { paddingLeft: `calc(var(--spacing-indent) * ${depth} + 0.75rem)` }, children: _jsxs("button", { type: "button", onClick: () => onGroupToggle === undefined ? row.toggleExpanded() : onGroupToggle(key), "aria-expanded": open, className: "inline-flex items-center gap-1.5 rounded-control text-meta font-medium text-ink transition-colors duration-(--duration-instant) hover:text-primary focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary", children: [_jsx(ChevronRight, { className: `size-(--size-icon-sm) text-muted transition-transform duration-(--duration-instant) ${open ? "rotate-90" : ""}`, strokeWidth: 2, "aria-hidden": true }), level === undefined ? null : (_jsx("span", { className: "text-micro font-normal text-muted/70", children: level.label })), _jsx("span", { children: level === undefined ? value : level.labelOf(value) }), _jsx("span", { className: "inline-flex shrink-0 items-center rounded-pill border border-line-strong bg-ink/5 px-2 py-0.5 text-micro tabular-nums text-muted", children: row.getLeafRows().length.toLocaleString(copy.locale) })] }) }) }, row.id));
|
|
232
|
+
}
|
|
233
|
+
const original = row.original;
|
|
234
|
+
return (_jsxs(Tr, { selected: selected?.has(row.id) === true, onClick: onRowClick === undefined ? undefined : () => onRowClick(original), children: [selecting ? (_jsx(Td, { children: _jsx(SelectBox, { label: copy.selection.row, checked: selected?.has(row.id) === true, onChange: (v) => toggle(row.id, v) }) })) : null, columns.map((c) => {
|
|
235
|
+
const t = c.tone?.(original);
|
|
236
|
+
const n = c.note?.(original);
|
|
237
|
+
const edit = c.edit?.when?.(original) === false ? undefined : c.edit;
|
|
238
|
+
return (_jsx(Td, { numeric: c.numeric, mono: c.mono === true, ...(t === undefined ? {} : { tone: t }), ...(n === undefined ? {} : { title: n }), children: edit !== undefined ? (_jsx(CellEditor, { label: fillCopy(copy.inlineEdit.editCell, { column: c.header }), numeric: c.numeric === true, editor: (done) => edit.render(original, done), children: drawCell(copy, c, original) })) : (drawCell(copy, c, original)) }, c.id));
|
|
239
|
+
}), rowActions === undefined ? null : (_jsx(RowActions, { actions: rowActions(original) }))] }, row.id));
|
|
240
|
+
}) }), footer === null ? null : (_jsx("tfoot", { className: "sticky bottom-0 z-10 bg-ground", children: _jsxs("tr", { className: "border-t border-line-strong", children: [selecting ? _jsx("td", {}) : null, columns.map((c, i) => {
|
|
241
|
+
const value = footer[i] ?? null;
|
|
242
|
+
return (_jsx("td", { className: `px-3 py-1.5 text-meta font-semibold text-ink ${c.numeric === true ? "text-right tabular-nums" : ""}`, children: i === 0 && value === null ? (_jsxs("span", { className: "text-muted", children: [data.length.toLocaleString(copy.locale), " ", data.length === 1 ? copy.table.row : copy.table.rows] })) : value === null ? null : (aggregateLabel(copy, c.aggregate ?? "count", value)) }, c.id));
|
|
243
|
+
}), rowActions === undefined ? null : _jsx("td", {})] }) }))] }));
|
|
244
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { type Grouping } from "../../foundations/grouping.js";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
/** Where a column goes when the row is drawn as a stacked card (a narrow box).
|
|
4
|
+
* `trail` sits at the top right, beside the title — a state, the row's buttons. */
|
|
5
|
+
export type StackPlace = "title" | "detail" | "amount" | "trail" | "hidden";
|
|
6
|
+
/** How the rows are drawn: `auto` measures the box and stacks them below
|
|
7
|
+
* `--container-handheld`; `table` and `stacked` force one — a side panel that is
|
|
8
|
+
* always narrow, a test that has no box to measure. */
|
|
9
|
+
export type EditLayout = "auto" | "table" | "stacked";
|
|
10
|
+
export interface EditColumn<T> {
|
|
11
|
+
id: string;
|
|
12
|
+
header: string;
|
|
13
|
+
width?: number;
|
|
14
|
+
numeric?: boolean;
|
|
15
|
+
/** How the cell reads when the row is NOT being edited. */
|
|
16
|
+
render: (row: T) => ReactNode;
|
|
17
|
+
/** The control, when the row is being edited. Without this the column is read
|
|
18
|
+
* only: an identifier or a computed value. Give the control an `aria-label`
|
|
19
|
+
* (the column's name, or a longer one): a cell has no `Field` to name it, and
|
|
20
|
+
* `Tab` through the row reads each control by that name. */
|
|
21
|
+
edit?: (row: T, set: (next: T) => void) => ReactNode;
|
|
22
|
+
/** Where the column goes in the stacked card, below `--container-handheld`. By
|
|
23
|
+
* default the first column is the `title`, the last numeric one the `amount` and
|
|
24
|
+
* the rest `detail`. `hidden` for what another column's `brief` already says. */
|
|
25
|
+
stack?: StackPlace;
|
|
26
|
+
/** How the column reads inside the card's detail line, when «header value» is not
|
|
27
|
+
* the way to say it: «12 × Bs. 120,00» for a quantity, «desc. 10 %» for a
|
|
28
|
+
* discount. `null` or `""` leaves it out of that row's line — a zero discount is
|
|
29
|
+
* not news. Without it the line reads «<header> <cell>». */
|
|
30
|
+
brief?: (row: T) => ReactNode;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Where each column goes in the stacked card, defaults resolved: a declared `stack`
|
|
34
|
+
* wins; otherwise the first undeclared column is the title (unless one was declared),
|
|
35
|
+
* the last undeclared numeric one is the amount (unless one was declared), and the rest
|
|
36
|
+
* are detail.
|
|
37
|
+
*/
|
|
38
|
+
export declare function stackPlaces(columns: readonly {
|
|
39
|
+
stack?: StackPlace;
|
|
40
|
+
numeric?: boolean;
|
|
41
|
+
}[]): StackPlace[];
|
|
42
|
+
/**
|
|
43
|
+
* A table whose rows are edited in place, one row at a time.
|
|
44
|
+
*
|
|
45
|
+
* Mouse: touching a row opens it. Keyboard: the rows are one tab stop, the arrows walk
|
|
46
|
+
* them, `Enter` / `F2` open one, `Tab` walks its controls, `Enter` commits and `Escape`
|
|
47
|
+
* puts the row back as it was; both return the focus to the row. Leaving the row
|
|
48
|
+
* (click outside, tabbing past its last control) commits.
|
|
49
|
+
*
|
|
50
|
+
* Narrower than `--container-handheld`, each row is a stacked card (see the file's
|
|
51
|
+
* header): the same keys, and `onOpen` —when given— instead of editing in place.
|
|
52
|
+
*/
|
|
53
|
+
export declare function EditableTable<T>({ rows, columns, getRowId, onChange, onRemove, onAdd, addLabel,
|
|
54
|
+
/** The footer with the aggregates, already formatted. `null` on the columns
|
|
55
|
+
* that do not add up. Whoever composes computes it, because whoever composes
|
|
56
|
+
* knows what adding up means in their domain —a percentage is not summed, it
|
|
57
|
+
* is averaged. */
|
|
58
|
+
totals, empty, grouping,
|
|
59
|
+
/** El total de una columna DENTRO de un montón, ya formateado. Lo calcula quien
|
|
60
|
+
* compone, por el mismo motivo que `totals`: sumar significa cosas distintas
|
|
61
|
+
* según el dominio, y un porcentaje se promedia, no se suma. */
|
|
62
|
+
groupTotal, height, layout, onOpen, }: {
|
|
63
|
+
rows: readonly T[];
|
|
64
|
+
columns: readonly EditColumn<T>[];
|
|
65
|
+
getRowId: (row: T) => string;
|
|
66
|
+
onChange?: (id: string, next: T) => void;
|
|
67
|
+
onRemove?: (id: string) => void;
|
|
68
|
+
onAdd?: () => void;
|
|
69
|
+
addLabel?: string;
|
|
70
|
+
totals?: readonly (ReactNode | null)[];
|
|
71
|
+
empty?: string;
|
|
72
|
+
/**
|
|
73
|
+
* El agrupado, con el mismo contrato que el resto de las vistas de datos.
|
|
74
|
+
*
|
|
75
|
+
* Editar y agrupar no se estorban: la fila que se edita sigue siendo una fila,
|
|
76
|
+
* y el montón sólo decide dónde cae. Lo que no se hereda es el orden — las
|
|
77
|
+
* filas de un montón quedan juntas, así que agregar una fila nueva la pone al
|
|
78
|
+
* final de todo y no adentro del montón que estabas mirando, que es el
|
|
79
|
+
* comportamiento honesto: todavía no tiene valor en el campo por el que se
|
|
80
|
+
* agrupa.
|
|
81
|
+
*/
|
|
82
|
+
grouping?: Grouping<T>;
|
|
83
|
+
groupTotal?: (column: EditColumn<T>, rows: readonly T[]) => ReactNode | null;
|
|
84
|
+
/** A height of its own, as `Table`'s: the lines scroll inside the box and the header
|
|
85
|
+
* sticks to its top. A number means pixels; a string goes through as is (`20rem`).
|
|
86
|
+
* Without it the table grows with its lines and the header sticks to the page. */
|
|
87
|
+
height?: number | string;
|
|
88
|
+
/** `auto` (the default) stacks the rows as cards when the box is narrower than
|
|
89
|
+
* `--container-handheld`; `table` and `stacked` force one. */
|
|
90
|
+
layout?: EditLayout;
|
|
91
|
+
/** Only in the stacked layout: pressing a card (or `Enter` / `F2` on it) calls this
|
|
92
|
+
* instead of editing the row in place — for a consumer that edits the whole row in a
|
|
93
|
+
* form of its own, a dialog that is a bottom sheet on a phone. The table ignores it:
|
|
94
|
+
* there the row is edited where it is read. */
|
|
95
|
+
onOpen?: (id: string) => void;
|
|
96
|
+
}): import("react").JSX.Element;
|