@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,181 @@
|
|
|
1
|
+
import { levelKey, pathKey, pileItems, pileLabel, } from "../../foundations/grouping.js";
|
|
2
|
+
import { aggregateOf } from "../../primitives/table/DataTable.js";
|
|
3
|
+
/**
|
|
4
|
+
* Context: what a list DERIVES from its set, as pure functions — the table's group levels,
|
|
5
|
+
* the sheet drawn from the declared columns, the sheet's flattened band/row sequence and
|
|
6
|
+
* the report built from the piles. `ListView` calls them inside `useMemo` with the same
|
|
7
|
+
* dependencies they always had; nothing here knows React.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Los niveles, como los necesita la tabla: uno por NIVEL y no uno por campo.
|
|
11
|
+
*
|
|
12
|
+
* La clave sale de `group.of` —la misma que arman las otras cinco
|
|
13
|
+
* presentaciones— así que los montones de la tabla son los mismos y lo plegado
|
|
14
|
+
* vale para todas. El rótulo sale de la misma regla que el árbol: un nivel no
|
|
15
|
+
* repite lo que el de arriba, del mismo campo, ya dijo.
|
|
16
|
+
*/
|
|
17
|
+
export function groupLevelsOf(group, columns) {
|
|
18
|
+
if (group === undefined || group.levels.length === 0)
|
|
19
|
+
return [];
|
|
20
|
+
const above = [];
|
|
21
|
+
const label = group.label ?? pileLabel;
|
|
22
|
+
return group.levels.map((level) => {
|
|
23
|
+
const within = above.flatMap((one) => one.field === level.field && one.grain !== undefined ? [one.grain] : []);
|
|
24
|
+
above.push(level);
|
|
25
|
+
/* Cómo se llama el corte: como lo llama la columna que contesta por ese
|
|
26
|
+
campo, que es el único rótulo humano que esta capa tiene. */
|
|
27
|
+
const column = columns?.find((one) => one.id === level.field || one.field === level.field);
|
|
28
|
+
return {
|
|
29
|
+
id: levelKey(level),
|
|
30
|
+
label: column?.header ?? level.field,
|
|
31
|
+
keyOf: (item) => group.of(item, level),
|
|
32
|
+
labelOf: (key) => label(key, level, within),
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* La planilla de un conjunto de REGISTROS sale de las columnas que la lista
|
|
38
|
+
* ya declaró. Sin esto, ofrecer `sheet` obligaba a escribir las columnas dos
|
|
39
|
+
* veces —una como `DataColumn` y otra como `SheetColumn`— y entonces nadie la
|
|
40
|
+
* ofrecía: la presentación existía en el arquetipo y no estaba en ninguna
|
|
41
|
+
* pantalla.
|
|
42
|
+
*
|
|
43
|
+
* `sheet` explícita sigue siendo para los DATOS CRUDOS, donde las columnas
|
|
44
|
+
* salen del archivo y el código no sabe cuáles son.
|
|
45
|
+
*/
|
|
46
|
+
export function sheetFromColumns(shownColumns) {
|
|
47
|
+
if (shownColumns === undefined || shownColumns.length === 0)
|
|
48
|
+
return undefined;
|
|
49
|
+
const byId = new Map(shownColumns.map((column) => [column.id, column]));
|
|
50
|
+
return {
|
|
51
|
+
columns: shownColumns.map((column) => ({
|
|
52
|
+
id: column.id,
|
|
53
|
+
header: column.header,
|
|
54
|
+
...(column.numeric === true ? { numeric: true } : {}),
|
|
55
|
+
...(column.width === undefined ? {} : { width: column.width }),
|
|
56
|
+
})),
|
|
57
|
+
cell: (item, column) => {
|
|
58
|
+
const declared = byId.get(column.id);
|
|
59
|
+
/* `text` primero: `value` es la clave de ORDEN, no lo que se lee. Sin
|
|
60
|
+
esto la planilla mostraba `computed` donde la tabla muestra
|
|
61
|
+
«calculado». */
|
|
62
|
+
if (declared?.text !== undefined)
|
|
63
|
+
return declared.text(item);
|
|
64
|
+
const raw = declared?.value(item);
|
|
65
|
+
/* Un booleano no es una celda: la cuadrícula dibuja texto o número, y
|
|
66
|
+
`false` se vería como una celda vacía, que significa otra cosa. */
|
|
67
|
+
return typeof raw === "boolean" ? String(raw) : (raw ?? null);
|
|
68
|
+
},
|
|
69
|
+
tone: (item, column) => byId.get(column.id)?.tone?.(item),
|
|
70
|
+
note: (item, column) => byId.get(column.id)?.note?.(item),
|
|
71
|
+
/* Crece con la página en vez de abrir una caja con scroll adentro de otra
|
|
72
|
+
que ya scrollea. La lista ya vino paginada, así que son ochenta filas y
|
|
73
|
+
no ciento ochenta y siete — el caso de un millón es el de los datos
|
|
74
|
+
crudos, y ése declara su propio alto. */
|
|
75
|
+
height: "auto",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* La planilla, cuando hay montones: una SECUENCIA donde cada índice es una
|
|
80
|
+
* fila o la cabecera del montón que empieza ahí.
|
|
81
|
+
*
|
|
82
|
+
* **Acá decía que la planilla no agrupa y estaba mal razonado.** El argumento
|
|
83
|
+
* —una banda corre la cuadrícula y mirar datos crudos es seguir una fila a lo
|
|
84
|
+
* ancho— vale para los datos crudos, donde las columnas salen del archivo y
|
|
85
|
+
* no hay ningún eje del dominio por el que agrupar. Pero la planilla DERIVADA
|
|
86
|
+
* es el mismo conjunto de registros que la tabla, con los mismos ejes: que
|
|
87
|
+
* agrupe la tabla y no ella es exactamente el drift que este arquetipo existe
|
|
88
|
+
* para impedir — cambiar de presentación perdía la agrupación.
|
|
89
|
+
*
|
|
90
|
+
* La regla queda en manos de la vista, como corresponde: si declara `group`,
|
|
91
|
+
* agrupa; si no, no hay montones que dibujar.
|
|
92
|
+
*/
|
|
93
|
+
export function sheetRowsOf(piles, isOpen, columns) {
|
|
94
|
+
if (piles === null)
|
|
95
|
+
return null;
|
|
96
|
+
const out = [];
|
|
97
|
+
let n = 0;
|
|
98
|
+
/* Aplanar es recursivo porque el árbol lo es. La planilla no puede recibir
|
|
99
|
+
el árbol: está virtualizada por índice, y un índice es una posición en una
|
|
100
|
+
secuencia — así que la jerarquía se expresa en la sangría de cada banda. */
|
|
101
|
+
const walk = (list) => {
|
|
102
|
+
for (const pile of list) {
|
|
103
|
+
const open = isOpen(pile);
|
|
104
|
+
const inside = pile.piles.length === 0 ? pile.items : pileItems(pile.piles);
|
|
105
|
+
out.push({
|
|
106
|
+
band: {
|
|
107
|
+
field: pile.field,
|
|
108
|
+
label: pile.label,
|
|
109
|
+
count: pile.count,
|
|
110
|
+
open,
|
|
111
|
+
key: pathKey(pile.path),
|
|
112
|
+
depth: pile.depth,
|
|
113
|
+
/* El total de cada columna, en la posición de esa columna. Sale de
|
|
114
|
+
las `columns` de la lista —las mismas que suma el pie de la
|
|
115
|
+
tabla— para que la planilla y la tabla no digan números distintos
|
|
116
|
+
del mismo montón. La planilla declara SUS columnas aparte, así que
|
|
117
|
+
se busca por id. */
|
|
118
|
+
total: (column) => {
|
|
119
|
+
const declared = columns?.find((one) => one.id === column.id);
|
|
120
|
+
return declared === undefined ? null : aggregateOf(declared, inside);
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
if (!open)
|
|
125
|
+
continue;
|
|
126
|
+
if (pile.piles.length > 0) {
|
|
127
|
+
walk(pile.piles);
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
for (const one of pile.items) {
|
|
131
|
+
n += 1;
|
|
132
|
+
out.push({ item: one, n });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
walk(piles);
|
|
137
|
+
return out;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* El informe armado a partir de los montones, cuando el dominio no declaró
|
|
141
|
+
* líneas.
|
|
142
|
+
*
|
|
143
|
+
* Cada montón es una línea con los agregados de las columnas que declararon
|
|
144
|
+
* uno, y el nivel más profundo cuelga sus filas. Las columnas del informe las
|
|
145
|
+
* pone `report.columns`; los números salen de las `columns` de la lista, que
|
|
146
|
+
* son las mismas que suma el pie de la tabla — para que el informe y la tabla
|
|
147
|
+
* no digan totales distintos del mismo conjunto.
|
|
148
|
+
*/
|
|
149
|
+
export function reportRowsOf(piles, columns, row) {
|
|
150
|
+
if (piles === null || columns === undefined)
|
|
151
|
+
return [];
|
|
152
|
+
const numeric = columns.filter((column) => column.aggregate !== undefined);
|
|
153
|
+
/* A pile is named by the HEADER of the column that answers for its field — the word the
|
|
154
|
+
person reads in the table — not by the field's id: «stage · pedido» showed the
|
|
155
|
+
model's name for it. */
|
|
156
|
+
const fieldName = (field) => columns.find((column) => column.id === field || column.field === field)?.header ?? field;
|
|
157
|
+
const line = (pile) => {
|
|
158
|
+
const inside = pile.piles.length === 0 ? pile.items : pileItems(pile.piles);
|
|
159
|
+
const children = pile.piles.length > 0
|
|
160
|
+
? pile.piles.map(line)
|
|
161
|
+
: pile.items.map((item, index) => ({
|
|
162
|
+
id: `${pathKey(pile.path)}\u0000${index}`,
|
|
163
|
+
label: row(item).name,
|
|
164
|
+
kind: "line",
|
|
165
|
+
level: pile.depth + 1,
|
|
166
|
+
values: numeric.map((column) => {
|
|
167
|
+
const value = column.value(item);
|
|
168
|
+
return typeof value === "number" ? value : null;
|
|
169
|
+
}),
|
|
170
|
+
}));
|
|
171
|
+
return {
|
|
172
|
+
id: pathKey(pile.path),
|
|
173
|
+
label: `${fieldName(pile.field)} · ${pile.label}`,
|
|
174
|
+
kind: pile.depth === 0 ? "section" : "group",
|
|
175
|
+
level: pile.depth,
|
|
176
|
+
values: numeric.map((column) => aggregateOf(column, inside)),
|
|
177
|
+
children,
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
return piles.map(line);
|
|
181
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type DataColumn, DataTable } from "../../primitives/table/DataTable.js";
|
|
2
|
+
import type { SheetEntry } from "./derive.js";
|
|
3
|
+
import type { ListGroup, ListSheet, ListViewProps } from "./types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Context: the two presentations whose body is an adapter between the list's vocabulary
|
|
6
|
+
* (one selection that speaks one id at a time, a grouping tree, an index-addressed sequence)
|
|
7
|
+
* and a primitive's (a Set, levels, indexes). They draw nothing of their own — they are
|
|
8
|
+
* `ListView`'s `table` and `sheet` branches, moved out so that file reads as the decision of
|
|
9
|
+
* WHICH presentation, and these as HOW the two grids are fed.
|
|
10
|
+
*/
|
|
11
|
+
type TableProps<T extends object> = {
|
|
12
|
+
items: readonly T[];
|
|
13
|
+
columns: readonly DataColumn<T>[];
|
|
14
|
+
keyOf: (item: T) => string;
|
|
15
|
+
levels: NonNullable<Parameters<typeof DataTable<T>>[0]["levels"]>;
|
|
16
|
+
group?: ListGroup<T>;
|
|
17
|
+
totals: boolean;
|
|
18
|
+
} & Pick<ListViewProps<T>, "sort" | "onSortChange" | "selection" | "rowActions" | "onPeek" | "onOpen">;
|
|
19
|
+
/** The table receives EVERYTHING that belongs to the set —sorting, selection, grouping,
|
|
20
|
+
* which columns are on screen— and not a trimmed down version: that was exactly the trap
|
|
21
|
+
* 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, onPeek, onOpen, }: TableProps<T>): import("react").JSX.Element;
|
|
23
|
+
type SheetProps<T extends object> = {
|
|
24
|
+
spec: ListSheet<T>;
|
|
25
|
+
/** The flattened bands and rows when the set is grouped; `null` when it is not. */
|
|
26
|
+
entries: readonly SheetEntry<T>[] | null;
|
|
27
|
+
ordered: readonly T[];
|
|
28
|
+
group?: ListGroup<T>;
|
|
29
|
+
};
|
|
30
|
+
/** The sheet receives the set by INDEX and not a ready-made matrix: `SheetTable` is
|
|
31
|
+
* virtualized precisely so as not to materialize a million rows, and handing it copies
|
|
32
|
+
* would throw that away.
|
|
33
|
+
*
|
|
34
|
+
* It groups like the other three —grouping belongs to the SET, not to the presentation—
|
|
35
|
+
* and the piles enter as rows of another kind, so virtualization is not lost. What it still
|
|
36
|
+
* lacks is selection: a checkbox per row in a forty-column grid competes with the frozen
|
|
37
|
+
* row-number column, the one that lets you know which row you are on. */
|
|
38
|
+
export declare function SheetPresentation<T extends object>({ spec, entries, ordered, group, }: SheetProps<T>): import("react").JSX.Element;
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useUiCopy } from "../../foundations/copy.js";
|
|
4
|
+
import { DataTable } from "../../primitives/table/DataTable.js";
|
|
5
|
+
import { SheetTable } from "../../primitives/table/SheetTable.js";
|
|
6
|
+
/** The table receives EVERYTHING that belongs to the set —sorting, selection, grouping,
|
|
7
|
+
* which columns are on screen— and not a trimmed down version: that was exactly the trap
|
|
8
|
+
* this archetype exists to avoid, that switching presentation loses capabilities. */
|
|
9
|
+
export function TablePresentation({ items, columns, keyOf, levels, group, totals, sort, onSortChange, selection, rowActions, onPeek, onOpen, }) {
|
|
10
|
+
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
|
+
? {}
|
|
12
|
+
: {
|
|
13
|
+
selected: new Set(items.filter((i) => selection.has(keyOf(i))).map(keyOf)),
|
|
14
|
+
onSelectedChange: (next) => {
|
|
15
|
+
/* `DataTable` sends the whole set; the view's selection speaks
|
|
16
|
+
one at a time. It is translated here and not there because the
|
|
17
|
+
one who knows what changed is the one holding the before and
|
|
18
|
+
the after. */
|
|
19
|
+
for (const item of items) {
|
|
20
|
+
const id = keyOf(item);
|
|
21
|
+
if (next.has(id) !== selection.has(id))
|
|
22
|
+
selection.toggle(id);
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
}), ...(totals ? { totals: true } : {}), ...(rowActions === undefined ? {} : { rowActions }), ...((onPeek ?? onOpen) === undefined ? {} : { onRowClick: onPeek ?? onOpen }) }));
|
|
26
|
+
}
|
|
27
|
+
/** The sheet receives the set by INDEX and not a ready-made matrix: `SheetTable` is
|
|
28
|
+
* virtualized precisely so as not to materialize a million rows, and handing it copies
|
|
29
|
+
* would throw that away.
|
|
30
|
+
*
|
|
31
|
+
* It groups like the other three —grouping belongs to the SET, not to the presentation—
|
|
32
|
+
* and the piles enter as rows of another kind, so virtualization is not lost. What it still
|
|
33
|
+
* lacks is selection: a checkbox per row in a forty-column grid competes with the frozen
|
|
34
|
+
* row-number column, the one that lets you know which row you are on. */
|
|
35
|
+
export function SheetPresentation({ spec, entries, ordered, group, }) {
|
|
36
|
+
const copy = useUiCopy();
|
|
37
|
+
/** Qué ítem hay en este índice de la planilla. Con montones, un índice puede
|
|
38
|
+
* ser una banda y no una fila — y entonces no hay ítem. Una sola vez, porque
|
|
39
|
+
* las seis funciones que la planilla recibe hacen la misma pregunta. */
|
|
40
|
+
const sheetItemAt = (index) => entries === null ? ordered[index] : entries[index]?.item;
|
|
41
|
+
return (_jsx(SheetTable, { columns: spec.columns, rows: entries === null ? ordered.length : entries.length, value: (index, column, columnIndex) => {
|
|
42
|
+
const item = sheetItemAt(index);
|
|
43
|
+
return item === undefined ? null : spec.cell(item, column, columnIndex);
|
|
44
|
+
}, ...(entries === null
|
|
45
|
+
? {}
|
|
46
|
+
: {
|
|
47
|
+
band: (index) => entries[index]?.band,
|
|
48
|
+
...(group?.onToggle === undefined
|
|
49
|
+
? {}
|
|
50
|
+
: {
|
|
51
|
+
onBandToggle: (index) => {
|
|
52
|
+
const key = entries[index]?.band?.key;
|
|
53
|
+
if (key !== undefined)
|
|
54
|
+
group.onToggle?.(key);
|
|
55
|
+
},
|
|
56
|
+
}),
|
|
57
|
+
}), ...(spec.tone === undefined
|
|
58
|
+
? {}
|
|
59
|
+
: {
|
|
60
|
+
tone: (index, column, columnIndex) => {
|
|
61
|
+
const item = sheetItemAt(index);
|
|
62
|
+
return item === undefined ? undefined : spec.tone?.(item, column, columnIndex);
|
|
63
|
+
},
|
|
64
|
+
}), ...(spec.note === undefined
|
|
65
|
+
? {}
|
|
66
|
+
: {
|
|
67
|
+
note: (index, column, columnIndex) => {
|
|
68
|
+
const item = sheetItemAt(index);
|
|
69
|
+
return item === undefined ? undefined : spec.note?.(item, column, columnIndex);
|
|
70
|
+
},
|
|
71
|
+
}), ...(spec.height === undefined ? {} : { height: spec.height }), ...(spec.rowLabel !== undefined
|
|
72
|
+
? {
|
|
73
|
+
rowLabel: (index) => {
|
|
74
|
+
const item = sheetItemAt(index);
|
|
75
|
+
return item === undefined ? "" : (spec.rowLabel?.(item, index) ?? "");
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
: entries === null
|
|
79
|
+
? {}
|
|
80
|
+
: {
|
|
81
|
+
rowLabel: (index) => {
|
|
82
|
+
const n = entries[index]?.n;
|
|
83
|
+
return n === undefined ? "" : n.toLocaleString(copy.locale);
|
|
84
|
+
},
|
|
85
|
+
}), ...(spec.onCommit === undefined
|
|
86
|
+
? {}
|
|
87
|
+
: {
|
|
88
|
+
onCommit: (index, column, columnIndex, next) => {
|
|
89
|
+
const item = sheetItemAt(index);
|
|
90
|
+
if (item !== undefined)
|
|
91
|
+
spec.onCommit?.(item, column, columnIndex, next);
|
|
92
|
+
},
|
|
93
|
+
}), ...(spec.editable === undefined
|
|
94
|
+
? {}
|
|
95
|
+
: {
|
|
96
|
+
editable: (index, column, columnIndex) => {
|
|
97
|
+
const item = sheetItemAt(index);
|
|
98
|
+
return item === undefined
|
|
99
|
+
? false
|
|
100
|
+
: (spec.editable?.(item, column, columnIndex) ?? false);
|
|
101
|
+
},
|
|
102
|
+
}) }));
|
|
103
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import type { ListIdentity } from "../../components/listing/ListingBar.js";
|
|
2
|
+
import type { ListPresent } from "../../components/listing/set.js";
|
|
3
|
+
import type { SortField } from "../../components/listing/SortMenu.js";
|
|
4
|
+
import type { Grouping, GroupLevel } from "../../foundations/grouping.js";
|
|
5
|
+
import type { Pivot, PivotKey, PivotSpec } from "../../foundations/pivot.js";
|
|
6
|
+
import type { Surface, Tone } from "../../foundations/types.js";
|
|
7
|
+
import type { DataColumn } from "../../primitives/table/DataTable.js";
|
|
8
|
+
import type { ReportColumn, ReportRow } from "../../primitives/table/ReportTable.js";
|
|
9
|
+
import type { RowAction } from "../../primitives/table/RowActions.js";
|
|
10
|
+
import type { SheetColumn } from "../../primitives/table/SheetTable.js";
|
|
11
|
+
import type { SortState } from "../../primitives/table/Table.js";
|
|
12
|
+
import type { LucideIcon } from "lucide-react";
|
|
13
|
+
import type { ReactNode } from "react";
|
|
14
|
+
/**
|
|
15
|
+
* Las seis maneras de mirar un conjunto. La unión vive en `list/set.ts`, que
|
|
16
|
+
* es donde ya vivía su copia.
|
|
17
|
+
*
|
|
18
|
+
* Las seis son el MISMO conjunto mirado distinto, y por eso comparten barra.
|
|
19
|
+
* Lo que cambia entre ellas es qué sabe el código de las columnas — y, en el
|
|
20
|
+
* pivote, que las filas de la pantalla ya no son las filas del conjunto.
|
|
21
|
+
*/
|
|
22
|
+
export type { ListPresent };
|
|
23
|
+
/** What an item looks like when the presentation is rows. */
|
|
24
|
+
export type RowOf = {
|
|
25
|
+
icon?: LucideIcon;
|
|
26
|
+
name: string;
|
|
27
|
+
tags?: ReactNode;
|
|
28
|
+
meta?: string;
|
|
29
|
+
/** What this resource produced, hanging underneath. */
|
|
30
|
+
children?: ReactNode;
|
|
31
|
+
actions?: ReactNode;
|
|
32
|
+
};
|
|
33
|
+
/** A crumb of the list: where I came from. */
|
|
34
|
+
export type ListCrumb = {
|
|
35
|
+
label: string;
|
|
36
|
+
onClick?: () => void;
|
|
37
|
+
};
|
|
38
|
+
export type ListEmpty = {
|
|
39
|
+
sentence: string;
|
|
40
|
+
action?: ReactNode;
|
|
41
|
+
/** Empty because of a filter is ANOTHER situation: content is not missing,
|
|
42
|
+
* there is an extra narrowing. Telling «importá tus facturas» to someone who has 180
|
|
43
|
+
* and mistyped a name is answering a different question. */
|
|
44
|
+
whenFiltered?: {
|
|
45
|
+
sentence: string;
|
|
46
|
+
action?: ReactNode;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* El agrupado de una lista: el CONTRATO de `system/grouping.ts` más lo único que
|
|
51
|
+
* una tabla necesita de más.
|
|
52
|
+
*
|
|
53
|
+
* Las seis presentaciones parten el conjunto con el MISMO árbol y las mismas
|
|
54
|
+
* claves — la tabla incluida. Lo único que una tabla agrega es una pregunta de
|
|
55
|
+
* VISIBILIDAD: agrupar por un campo cuya columna está escondida dejaría montones
|
|
56
|
+
* cuyo criterio no se ve en ninguna parte, así que esa columna se repone, y para
|
|
57
|
+
* reponerla hay que saber cuál es. Eso es `columnOf`, y nada más: la clave del
|
|
58
|
+
* montón no sale de una columna.
|
|
59
|
+
*
|
|
60
|
+
* Va acá y no en el contrato para que una vista que no tenga columnas no tenga
|
|
61
|
+
* que fingir que las tiene.
|
|
62
|
+
*/
|
|
63
|
+
export type ListGroup<T extends object> = Grouping<T> & {
|
|
64
|
+
/** Qué COLUMNA contesta por cada nivel, para reponerla cuando está escondida.
|
|
65
|
+
* Sin esto, la que se llame como el campo o la que lo declare en `field`. */
|
|
66
|
+
columnOf?: (level: GroupLevel) => string | undefined;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* La PLANILLA de una lista: las columnas y cómo se lee cada celda.
|
|
70
|
+
*
|
|
71
|
+
* Se declara cuando las columnas salen del ARCHIVO y el código no sabe cuáles
|
|
72
|
+
* son. Para un conjunto de registros no hace falta: sale de las `columns` que
|
|
73
|
+
* la lista ya declaró.
|
|
74
|
+
*/
|
|
75
|
+
export type ListSheet<T> = {
|
|
76
|
+
columns: readonly SheetColumn[];
|
|
77
|
+
/** El valor de una celda. `null` es un faltante y `""` una celda vacía: son
|
|
78
|
+
* cosas distintas y el motor las distingue, así que la interfaz también. */
|
|
79
|
+
cell: (item: T, column: SheetColumn, columnIndex: number) => string | number | null;
|
|
80
|
+
/** Qué celdas se pintan y con qué: un valor imputado, uno fuera de rango. */
|
|
81
|
+
tone?: (item: T, column: SheetColumn, columnIndex: number) => Tone | undefined;
|
|
82
|
+
note?: (item: T, column: SheetColumn, columnIndex: number) => string | undefined;
|
|
83
|
+
height?: number | "auto";
|
|
84
|
+
/** Qué dice la columna fija de la izquierda. Sin esto, el número de fila. */
|
|
85
|
+
rowLabel?: (item: T, index: number) => string;
|
|
86
|
+
/** Presente = la planilla se edita como planilla. Recibe el texto crudo;
|
|
87
|
+
* convertirlo al tipo de la columna es del dominio, no de acá. */
|
|
88
|
+
onCommit?: (item: T, column: SheetColumn, columnIndex: number, next: string) => void;
|
|
89
|
+
editable?: (item: T, column: SheetColumn, columnIndex: number) => boolean;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* El INFORME de una lista: los períodos y la jerarquía que termina en totales.
|
|
93
|
+
*
|
|
94
|
+
* Las filas van aparte de `items` y es deliberado: un balance no es la lista
|
|
95
|
+
* plana agrupada —«Total Activos» es una suma de sumas— y el arquetipo no puede
|
|
96
|
+
* inventar esa jerarquía. La arma quien conoce el dominio, como ya arma
|
|
97
|
+
* `columns`.
|
|
98
|
+
*/
|
|
99
|
+
export type ListReport = {
|
|
100
|
+
columns: readonly ReportColumn[];
|
|
101
|
+
/**
|
|
102
|
+
* Las líneas del informe, cuando el DOMINIO las arma.
|
|
103
|
+
*
|
|
104
|
+
* Un balance no es la lista plana agrupada —«Total Activos» es una suma de
|
|
105
|
+
* sumas— y el arquetipo no puede inventar esa jerarquía.
|
|
106
|
+
*
|
|
107
|
+
* **Sin esto, el informe se arma con los montones**: cada nivel es una línea
|
|
108
|
+
* con sus agregados, y el más profundo son las filas. Es el mismo conjunto que
|
|
109
|
+
* ven las otras cuatro presentaciones, contado — que es lo que alguien espera
|
|
110
|
+
* al pasar de «tabla» a «informe» con dos niveles puestos.
|
|
111
|
+
*/
|
|
112
|
+
rows?: readonly ReportRow[];
|
|
113
|
+
/** Qué grupos arrancan abiertos. Por omisión ninguno: un balance con todo
|
|
114
|
+
* desplegado son cuatrocientas líneas y lo que se mira primero son los
|
|
115
|
+
* totales. */
|
|
116
|
+
defaultOpen?: readonly string[];
|
|
117
|
+
height?: number;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* El PIVOTE de una lista: los dos ejes y las medidas que se pliegan en el cruce.
|
|
121
|
+
*
|
|
122
|
+
* Es la presentación en la que **las filas de la pantalla dejan de ser los
|
|
123
|
+
* registros**: una fila es un montón y una columna también, así que no hay nada
|
|
124
|
+
* que elegir ni que abrir, y lo que se lee está en el cruce.
|
|
125
|
+
*
|
|
126
|
+
* De ahí que venga plegado de dos lugares posibles, igual que los montones de
|
|
127
|
+
* una lista agrupada:
|
|
128
|
+
*
|
|
129
|
+
* folded lo plegó quien guarda los datos. Es el caso normal en cuanto el
|
|
130
|
+
* conjunto no entra en el navegador: contar 187 vencidas no lo
|
|
131
|
+
* pueden contestar las doce filas que se bajaron.
|
|
132
|
+
* spec lo pliega esta máquina, con las filas en la mano.
|
|
133
|
+
*
|
|
134
|
+
* Los dos dibujan igual porque dibujan el mismo tipo.
|
|
135
|
+
*/
|
|
136
|
+
export type ListPivot<T> = {
|
|
137
|
+
/** El pivote ya plegado. Gana sobre `spec` cuando están los dos: si alguien
|
|
138
|
+
* con el conjunto entero ya contestó, no hay nada que recalcular. */
|
|
139
|
+
folded?: Pivot;
|
|
140
|
+
/**
|
|
141
|
+
* Cómo armarlo acá adentro. Las medidas traen su pliegue, y los márgenes salen
|
|
142
|
+
* de la BASE y no de sumar celdas — ver `system/pivot.ts`.
|
|
143
|
+
*
|
|
144
|
+
* Quien además necesite la grilla EN LA MANO —para bajarla como planilla con
|
|
145
|
+
* `downloadPivot`, por ejemplo— la pliega él con `pivotFrom` y la pasa por
|
|
146
|
+
* `folded`: así la que se dibuja y la que se baja son el mismo objeto, y no
|
|
147
|
+
* dos que podrían no coincidir.
|
|
148
|
+
*/
|
|
149
|
+
spec?: PivotSpec<T>;
|
|
150
|
+
/** Qué pasa al tocar un cruce: abrir las filas que cayeron ahí. */
|
|
151
|
+
onCell?: (row: PivotKey, column: PivotKey) => void;
|
|
152
|
+
height?: number | string;
|
|
153
|
+
};
|
|
154
|
+
export type ListViewProps<T extends object> = {
|
|
155
|
+
items: readonly T[];
|
|
156
|
+
keyOf: (item: T) => string;
|
|
157
|
+
identity?: ListIdentity;
|
|
158
|
+
/** Where I came from. It goes above the bar, and only when the bar is chrome. */
|
|
159
|
+
crumbs?: readonly ListCrumb[];
|
|
160
|
+
/** The metrics that also NARROW: «2 por confirmar», «41 por entregar». They go
|
|
161
|
+
* below the bar and above the rows — they belong to the set, not to the frame. */
|
|
162
|
+
metrics?: ReactNode;
|
|
163
|
+
/** Which surface the BAR sits on. `canvas` (the default) is a light control bar;
|
|
164
|
+
* `chrome` turns it into a dark band — an explicit choice, since the application bar
|
|
165
|
+
* is the screen's one dark band (same as the rail of a record). */
|
|
166
|
+
surface?: Surface;
|
|
167
|
+
/** The search field, already assembled. It is assembled by whoever holds the
|
|
168
|
+
* domain declarations — the system does not know what a provenance is. */
|
|
169
|
+
search?: ReactNode;
|
|
170
|
+
pagination?: ReactNode;
|
|
171
|
+
/** Escape hatch: a custom switcher. Prefer `presents` so the archetype draws it. */
|
|
172
|
+
views?: ReactNode;
|
|
173
|
+
actions?: ReactNode;
|
|
174
|
+
row: (item: T) => RowOf;
|
|
175
|
+
/**
|
|
176
|
+
* Los rótulos de las zonas de una fila, arriba de todas. Sin esto no hay
|
|
177
|
+
* encabezado: `rows` no es una tabla y no toda lista tiene algo que rotular
|
|
178
|
+
* —una lista de cinco archivos con el nombre y nada más no lo necesita.
|
|
179
|
+
*
|
|
180
|
+
* No son columnas. Una fila tiene un nombre, UNA cadena de `meta` ya
|
|
181
|
+
* formateada y una columna de fichas: eso es lo que se rotula, y por eso los
|
|
182
|
+
* rótulos los escribe la vista y no salen de `columns`.
|
|
183
|
+
*/
|
|
184
|
+
rowHeader?: {
|
|
185
|
+
name: string;
|
|
186
|
+
meta?: string;
|
|
187
|
+
tags?: string;
|
|
188
|
+
};
|
|
189
|
+
/** The table presentation. With this, `present="table"` draws a `DataTable`. */
|
|
190
|
+
columns?: readonly DataColumn<T>[];
|
|
191
|
+
/** Which table columns are on screen. Required columns stay even if omitted.
|
|
192
|
+
* Owned by the view when omitted — catalog filters must not reset them. */
|
|
193
|
+
visibleColumnIds?: readonly string[];
|
|
194
|
+
onVisibleColumnIdsChange?: (ids: readonly string[]) => void;
|
|
195
|
+
/** The sorting of the table. It lives outside because it belongs to the SET
|
|
196
|
+
* and not to the presentation: switching from table to rows has no reason to
|
|
197
|
+
* lose it. */
|
|
198
|
+
sort?: SortState | null;
|
|
199
|
+
onSortChange?: (next: SortState | null) => void;
|
|
200
|
+
/**
|
|
201
|
+
* Por qué campos se puede ordenar, para las presentaciones que NO tienen
|
|
202
|
+
* encabezados donde hacer clic.
|
|
203
|
+
*
|
|
204
|
+
* Sin esto, ordenar existe sólo en la tabla — y entonces pasar a filas o a
|
|
205
|
+
* tablero no pierde el orden puesto, pierde la posibilidad de ordenar. La
|
|
206
|
+
* tabla sigue teniendo además sus encabezados: son dos puertas a lo mismo, y
|
|
207
|
+
* la de los encabezados es más rápida donde existe.
|
|
208
|
+
*/
|
|
209
|
+
sortable?: readonly SortField<T>[];
|
|
210
|
+
/** The footer with the aggregates of the columns that declared one. */
|
|
211
|
+
totals?: boolean;
|
|
212
|
+
/** What gets done to ONE row without entering it, in the table presentation.
|
|
213
|
+
* They are drawn hidden and appear on the row being pointed at. */
|
|
214
|
+
rowActions?: (item: T) => readonly RowAction[];
|
|
215
|
+
/** The board presentation. */
|
|
216
|
+
card?: (item: T) => ReactNode;
|
|
217
|
+
/**
|
|
218
|
+
* La PLANILLA. Con esto, `present="sheet"` dibuja un `SheetTable`.
|
|
219
|
+
*
|
|
220
|
+
* Es la presentación de los datos crudos, y por eso no recibe `DataColumn`:
|
|
221
|
+
* las columnas salen del archivo y el código no sabe cuáles son ni cuántas.
|
|
222
|
+
* El valor se pide por índice —igual que `SheetTable`, que está virtualizado—
|
|
223
|
+
* así que un millón de filas no se copia a ninguna otra estructura.
|
|
224
|
+
*/
|
|
225
|
+
sheet?: ListSheet<T>;
|
|
226
|
+
/**
|
|
227
|
+
* El INFORME: una jerarquía que termina en totales. Con esto,
|
|
228
|
+
* `present="report"` dibuja un `ReportTable`.
|
|
229
|
+
*
|
|
230
|
+
* Las filas van aparte de `items` y es deliberado: un balance NO es la lista
|
|
231
|
+
* plana agrupada —«Total Activos» no es un montón de cuentas, es una suma de
|
|
232
|
+
* sumas— y el arquetipo no puede inventar esa jerarquía a partir del
|
|
233
|
+
* conjunto. La arma quien conoce el dominio, como ya arma `columns`.
|
|
234
|
+
*/
|
|
235
|
+
report?: ListReport;
|
|
236
|
+
/**
|
|
237
|
+
* El PIVOTE: dos ejes a la vez. Con esto, `present="pivot"` dibuja un
|
|
238
|
+
* `PivotTable`.
|
|
239
|
+
*
|
|
240
|
+
* Va aparte de `group` y no derivado de él aunque los dos partan el conjunto,
|
|
241
|
+
* porque un pivote parte por DOS ejes y una lista agrupada por uno: los
|
|
242
|
+
* niveles de `group` no dicen cuál de ellos sería el eje horizontal, y
|
|
243
|
+
* elegirlo acá sería adivinar la pregunta.
|
|
244
|
+
*/
|
|
245
|
+
pivot?: ListPivot<T>;
|
|
246
|
+
present?: ListPresent;
|
|
247
|
+
/** Which presentations this list offers. Two or more draw the switcher. */
|
|
248
|
+
presents?: readonly ListPresent[];
|
|
249
|
+
onPresentChange?: (next: ListPresent) => void;
|
|
250
|
+
group?: ListGroup<T>;
|
|
251
|
+
selection?: {
|
|
252
|
+
has: (id: string) => boolean;
|
|
253
|
+
toggle: (id: string) => void;
|
|
254
|
+
count: number;
|
|
255
|
+
clear: () => void;
|
|
256
|
+
actions?: ReactNode;
|
|
257
|
+
};
|
|
258
|
+
onOpen?: (item: T) => void;
|
|
259
|
+
onPeek?: (item: T) => void;
|
|
260
|
+
isPeeked?: (item: T) => boolean;
|
|
261
|
+
/** The three states, in a single place. Every view used to re-assemble them
|
|
262
|
+
* with three nested ternaries, and that is why «empty because of a filter»
|
|
263
|
+
* and «really empty» got mixed up in half the screens. */
|
|
264
|
+
state?: {
|
|
265
|
+
loading?: boolean;
|
|
266
|
+
error?: string | null;
|
|
267
|
+
};
|
|
268
|
+
filtered?: boolean;
|
|
269
|
+
empty?: ListEmpty;
|
|
270
|
+
/** The escape hatch. */
|
|
271
|
+
children?: ReactNode;
|
|
272
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ListPresent } from "../../components/listing/set.js";
|
|
2
|
+
import { type RefObject } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* A narrow LIST — a phone, a drawer, a panel among four — does not offer the presentations
|
|
5
|
+
* that need width: a seven-column table at 360 px is a horizontal scroll nobody reads.
|
|
6
|
+
* There the list shows rows (a stacked card per record: the name, its state, its facts)
|
|
7
|
+
* and the board, and the switcher offers only those. It is the width of the list's own
|
|
8
|
+
* container that decides, not the window's: a narrow panel on a wide screen is narrow.
|
|
9
|
+
*
|
|
10
|
+
* Attach `frame` to the list's root element; `offered` is what the switcher may show and
|
|
11
|
+
* `present` what to draw (a wide one that was asked for falls back to rows).
|
|
12
|
+
*/
|
|
13
|
+
export declare function useNarrowList(presents: readonly ListPresent[] | undefined, requested: ListPresent): {
|
|
14
|
+
frame: RefObject<HTMLDivElement | null>;
|
|
15
|
+
offered: readonly ListPresent[];
|
|
16
|
+
present: ListPresent;
|
|
17
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { HANDHELD_WIDTH } from "../../foundations/layout.js";
|
|
3
|
+
import { WIDE_PRESENTS } from "./columns.js";
|
|
4
|
+
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
/**
|
|
6
|
+
* A narrow LIST — a phone, a drawer, a panel among four — does not offer the presentations
|
|
7
|
+
* that need width: a seven-column table at 360 px is a horizontal scroll nobody reads.
|
|
8
|
+
* There the list shows rows (a stacked card per record: the name, its state, its facts)
|
|
9
|
+
* and the board, and the switcher offers only those. It is the width of the list's own
|
|
10
|
+
* container that decides, not the window's: a narrow panel on a wide screen is narrow.
|
|
11
|
+
*
|
|
12
|
+
* Attach `frame` to the list's root element; `offered` is what the switcher may show and
|
|
13
|
+
* `present` what to draw (a wide one that was asked for falls back to rows).
|
|
14
|
+
*/
|
|
15
|
+
export function useNarrowList(presents, requested) {
|
|
16
|
+
const frame = useRef(null);
|
|
17
|
+
const [narrow, setNarrow] = useState(false);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const el = frame.current;
|
|
20
|
+
if (el === null || typeof ResizeObserver === "undefined")
|
|
21
|
+
return;
|
|
22
|
+
const watch = new ResizeObserver(([entry]) => {
|
|
23
|
+
if (entry !== undefined)
|
|
24
|
+
setNarrow(entry.contentRect.width < HANDHELD_WIDTH);
|
|
25
|
+
});
|
|
26
|
+
watch.observe(el);
|
|
27
|
+
return () => watch.disconnect();
|
|
28
|
+
}, []);
|
|
29
|
+
const offered = (presents ?? []).filter((p) => !narrow || !WIDE_PRESENTS.includes(p));
|
|
30
|
+
const present = narrow && WIDE_PRESENTS.includes(requested) ? "rows" : requested;
|
|
31
|
+
return { frame, offered, present };
|
|
32
|
+
}
|