@sincpro/web-ui 0.0.0-stage → 0.2.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 +184 -2
- package/dist/components/activity/Activity.d.ts +69 -0
- package/dist/components/activity/Activity.js +279 -0
- package/dist/components/activity/Messages.d.ts +66 -0
- package/dist/components/activity/Messages.js +46 -0
- package/dist/components/activity/Timeline.d.ts +21 -0
- package/dist/components/activity/Timeline.js +30 -0
- package/dist/components/activity/index.d.ts +3 -0
- package/dist/components/activity/index.js +4 -0
- 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/form/Form.d.ts +52 -0
- package/dist/components/form/Form.js +49 -0
- package/dist/components/form/Sheet.d.ts +47 -0
- package/dist/components/form/Sheet.js +90 -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 +111 -0
- package/dist/components/lines/LineItems.js +317 -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 +112 -0
- package/dist/components/listing/ListingBar.js +144 -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 +110 -0
- package/dist/components/listing/Row.js +123 -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 +75 -0
- package/dist/components/record/RecordHeader.js +104 -0
- package/dist/components/record/RecordNotices.d.ts +37 -0
- package/dist/components/record/RecordNotices.js +75 -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 +703 -0
- package/dist/foundations/copy.es.d.ts +2 -0
- package/dist/foundations/copy.es.js +430 -0
- package/dist/foundations/copy.js +520 -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 +19 -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 +33 -0
- package/dist/primitives/choice/SearchField.js +46 -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 +134 -0
- package/dist/primitives/date/DatePicker.d.ts +42 -0
- package/dist/primitives/date/DatePicker.js +103 -0
- package/dist/primitives/date/DateTime.d.ts +35 -0
- package/dist/primitives/date/DateTime.js +100 -0
- package/dist/primitives/date/index.d.ts +3 -0
- package/dist/primitives/date/index.js +6 -0
- package/dist/primitives/dialog/Dialog.d.ts +118 -0
- package/dist/primitives/dialog/Dialog.js +121 -0
- package/dist/primitives/dialog/Drill.d.ts +90 -0
- package/dist/primitives/dialog/Drill.js +172 -0
- package/dist/primitives/dialog/index.d.ts +2 -0
- package/dist/primitives/dialog/index.js +4 -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 +178 -0
- package/dist/primitives/field/Field.js +251 -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/copy.d.ts +16 -0
- package/dist/primitives/field/copy.js +69 -0
- package/dist/primitives/field/index.d.ts +9 -0
- package/dist/primitives/field/index.js +15 -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/field/note.d.ts +55 -0
- package/dist/primitives/field/note.js +117 -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 +20 -0
- package/dist/primitives/index.js +21 -0
- package/dist/primitives/menu/Menu.d.ts +128 -0
- package/dist/primitives/menu/Menu.js +89 -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/Flag.d.ts +13 -0
- package/dist/primitives/notice/Flag.js +31 -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 +4 -0
- package/dist/primitives/notice/index.js +6 -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/Preview.d.ts +19 -0
- package/dist/primitives/overlay/Preview.js +60 -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 +4 -0
- package/dist/primitives/overlay/index.js +7 -0
- package/dist/primitives/picker/Picker.d.ts +89 -0
- package/dist/primitives/picker/Picker.js +204 -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/CellCopy.d.ts +20 -0
- package/dist/primitives/table/CellCopy.js +23 -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 +189 -0
- package/dist/primitives/table/DataTable.js +330 -0
- package/dist/primitives/table/EditableTable.d.ts +191 -0
- package/dist/primitives/table/EditableTable.js +617 -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 +73 -0
- package/dist/primitives/table/SheetTable.js +230 -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 +35 -0
- package/dist/primitives/tag/TagsField.js +186 -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/tree/Tree.d.ts +56 -0
- package/dist/primitives/tree/Tree.js +74 -0
- package/dist/primitives/tree/index.d.ts +1 -0
- package/dist/primitives/tree/index.js +3 -0
- package/dist/primitives/value/Copyable.d.ts +48 -0
- package/dist/primitives/value/Copyable.js +71 -0
- package/dist/primitives/value/Fecha.d.ts +12 -0
- package/dist/primitives/value/Fecha.js +89 -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/ProgressBar.d.ts +31 -0
- package/dist/primitives/value/ProgressBar.js +53 -0
- package/dist/primitives/value/Relation.d.ts +74 -0
- package/dist/primitives/value/Relation.js +74 -0
- package/dist/primitives/value/Totals.d.ts +121 -0
- package/dist/primitives/value/Totals.js +54 -0
- package/dist/primitives/value/Value.d.ts +40 -0
- package/dist/primitives/value/Value.js +80 -0
- package/dist/primitives/value/index.d.ts +9 -0
- package/dist/primitives/value/index.js +10 -0
- package/dist/tokens.css +1062 -0
- package/dist/views/index.d.ts +2 -0
- package/dist/views/index.js +3 -0
- package/dist/views/list/List.d.ts +119 -0
- package/dist/views/list/List.js +101 -0
- package/dist/views/list/ListView.d.ts +6 -0
- package/dist/views/list/ListView.js +295 -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 +87 -0
- package/dist/views/list/derive.js +207 -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 +105 -0
- package/dist/views/list/types.d.ts +284 -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 +145 -0
- package/dist/views/record/Record.js +211 -0
- package/dist/views/record/index.d.ts +1 -0
- package/dist/views/record/index.js +2 -0
- package/package.json +113 -4
|
@@ -0,0 +1,189 @@
|
|
|
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
|
+
/**
|
|
12
|
+
* How the aggregate of a column is written: its `format` when the aggregate is in the
|
|
13
|
+
* column's units, `undefined` (the bare number) otherwise.
|
|
14
|
+
*
|
|
15
|
+
* Exported for the other places that draw `aggregateOf` — the bands of the sheet, the report
|
|
16
|
+
* built from the piles — so the footer and the band never write the same total two ways.
|
|
17
|
+
* Not public.
|
|
18
|
+
*/
|
|
19
|
+
export declare function aggregateFormat<T>(column: DataColumn<T>): ((value: number) => string) | undefined;
|
|
20
|
+
/** An aggregate of a column, written — or the bare number when nothing says how. */
|
|
21
|
+
export declare function aggregateText<T>(column: DataColumn<T>, value: number): string | number;
|
|
22
|
+
export interface DataColumn<T> {
|
|
23
|
+
id: string;
|
|
24
|
+
header: string;
|
|
25
|
+
/** Qué campo del MODELO muestra esta columna, cuando no se llama igual que
|
|
26
|
+
* él: «filas» muestra `row_count`. Lo lee `columnsFromModel` para no volver
|
|
27
|
+
* a agregar, en crudo, un campo que esta columna ya contesta. */
|
|
28
|
+
field?: string;
|
|
29
|
+
/** The raw value: this is what sorting and grouping run on. */
|
|
30
|
+
value: (row: T) => string | number | boolean | null;
|
|
31
|
+
/** How it is drawn. Without this the raw value is drawn. */
|
|
32
|
+
cell?: (row: T) => ReactNode;
|
|
33
|
+
/** A column of CODES — a SKU, a hash, a key — set in the monospaced face. Numbers do not
|
|
34
|
+
* need it: they align with tabular figures in the same face as the rest of the row. */
|
|
35
|
+
mono?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Cómo se LEE la columna donde no se puede dibujar: la planilla, que es una
|
|
38
|
+
* cuadrícula de texto y números y no puede recibir un nodo.
|
|
39
|
+
*
|
|
40
|
+
* Context: sin esto, la planilla caía a `value`, que es la clave de ORDEN y
|
|
41
|
+
* no lo que una persona lee. La columna «estado» de Facturas mostraba
|
|
42
|
+
* `posted` y `paid` —los valores del contrato— en vez de «publicada» y
|
|
43
|
+
* «pagada», que es lo que dice la ficha de al lado en la tabla. Dos
|
|
44
|
+
* presentaciones del mismo conjunto diciendo cosas distintas.
|
|
45
|
+
*/
|
|
46
|
+
text?: (row: T) => string;
|
|
47
|
+
/** Paints the BACKGROUND of the cell, row by row. It is what turns a table
|
|
48
|
+
* into something you look at: «this amount is out of range», «this value was
|
|
49
|
+
* imputed by the engine». Returning `undefined` leaves the cell unpainted,
|
|
50
|
+
* which is the normal case — a table with everything colored highlights
|
|
51
|
+
* nothing. */
|
|
52
|
+
tone?: (row: T) => Tone | undefined;
|
|
53
|
+
/** Why it is painted, on hover. */
|
|
54
|
+
note?: (row: T) => string | undefined;
|
|
55
|
+
numeric?: boolean;
|
|
56
|
+
width?: number;
|
|
57
|
+
aggregate?: Aggregate;
|
|
58
|
+
/**
|
|
59
|
+
* How a number of this column is WRITTEN where the code draws it instead of a row: the
|
|
60
|
+
* footer's aggregate, the total of a band of the sheet, the lines of a report built
|
|
61
|
+
* from the piles. `(n) => money(n)` makes the footer read «Bs. 78.752,90» like the cells
|
|
62
|
+
* above it, and not «78.752,9».
|
|
63
|
+
*
|
|
64
|
+
* Context: the cells already had their way (`cell`), and that way takes a ROW — the
|
|
65
|
+
* footer has no row, only a number. A string and not a node because two of the three
|
|
66
|
+
* places are grids of text: the sheet and the report. Applies to the aggregates in the
|
|
67
|
+
* column's units (`sum`, `mean`, `min`, `max`); a `count` stays a count. Without it,
|
|
68
|
+
* the number in the copy table's locale, as before.
|
|
69
|
+
*/
|
|
70
|
+
format?: (value: number) => string;
|
|
71
|
+
sortable?: boolean;
|
|
72
|
+
/** Odoo: fields without `optional` cannot leave the view. When true, the
|
|
73
|
+
* column appears in the columns submenu and can be hidden. */
|
|
74
|
+
optional?: boolean;
|
|
75
|
+
/** Odoo `optional="hide"`: start off. Only with `optional`. */
|
|
76
|
+
defaultHidden?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Edit this cell IN the list, through a small popover anchored to it: the salesperson of
|
|
79
|
+
* an invoice, its due date, its stage. The cell becomes a button (with a pencil on hover
|
|
80
|
+
* and focus) and `render` draws the editor inside the popover.
|
|
81
|
+
*
|
|
82
|
+
* Context: one function and not a kind of editor, because the editor is the system's own
|
|
83
|
+
* control — a `Picker`, a `Calendar`, a `Select` — written by whoever knows the field.
|
|
84
|
+
* The editor commits with its own gesture and then calls `done`, which closes the popover
|
|
85
|
+
* and returns the focus to the cell. Escape and a click outside close without committing.
|
|
86
|
+
* Do not draw links in an editable column: the cell is already a button.
|
|
87
|
+
*/
|
|
88
|
+
edit?: {
|
|
89
|
+
render: (row: T, done: () => void) => ReactNode;
|
|
90
|
+
/** Which rows can be changed — a paid invoice cannot. All of them by default. */
|
|
91
|
+
when?: (row: T) => boolean;
|
|
92
|
+
};
|
|
93
|
+
/** The control of this column in the row of a NEW record (`DataTable`'s `create`).
|
|
94
|
+
* The same contract as `EditableTable`'s `edit`: `set` replaces the draft. Give it an
|
|
95
|
+
* `aria-label` — a cell has no `Field` to name it. Without it the column stays
|
|
96
|
+
* empty in that row: a computed amount, a sequence the backend assigns. */
|
|
97
|
+
input?: (draft: T, set: (next: T) => void) => ReactNode;
|
|
98
|
+
/** The copy button of the cells (`CellCopy`): on by default, copying what the cell shows.
|
|
99
|
+
* A function copies what it returns for the row instead; `false` removes it from this
|
|
100
|
+
* column — a column of tags or of buttons has nothing worth pasting. */
|
|
101
|
+
copy?: false | ((row: T) => string);
|
|
102
|
+
}
|
|
103
|
+
/** Writing a new record in the list. See the file's header. */
|
|
104
|
+
export type DataTableCreate<T> = {
|
|
105
|
+
/** The record being written, or `null` when there is none. */
|
|
106
|
+
draft: T | null;
|
|
107
|
+
/** «Add» was asked for: the consumer sets a blank `draft`. */
|
|
108
|
+
onStart: () => void;
|
|
109
|
+
onChange: (next: T) => void;
|
|
110
|
+
/** `Enter`, leaving the row after writing in it, or `Tab` past its last control. The
|
|
111
|
+
* consumer validates and saves, and sets `draft` back to `null` — or keeps it, to
|
|
112
|
+
* say what is missing. */
|
|
113
|
+
onCommit: (draft: T) => void;
|
|
114
|
+
/** `Escape`, or leaving the row without having written anything. */
|
|
115
|
+
onCancel: () => void;
|
|
116
|
+
/** The «add» under the table. Defaults to the copy table's. */
|
|
117
|
+
label?: string;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* El agregado de una columna sobre un conjunto de filas.
|
|
121
|
+
*
|
|
122
|
+
* Vive afuera del componente porque lo preguntan tres lugares: el pie de la
|
|
123
|
+
* tabla, la banda de un montón en la planilla, y la línea de un informe armado
|
|
124
|
+
* a partir de los montones. Tres implementaciones de «cuánto suma esto» es que
|
|
125
|
+
* el pie diga un número y el montón otro.
|
|
126
|
+
*
|
|
127
|
+
* `null` cuando la columna no declaró agregado, no es numérica, o no hay ningún
|
|
128
|
+
* número que sumar — que es distinto de sumar cero.
|
|
129
|
+
*/
|
|
130
|
+
export declare function aggregateOf<T extends object>(column: DataColumn<T>, rows: readonly T[]): number | null;
|
|
131
|
+
/**
|
|
132
|
+
* UN NIVEL, como lo necesita una tabla.
|
|
133
|
+
*
|
|
134
|
+
* La identidad es la del NIVEL y no la del campo: un mismo `registered_at`
|
|
135
|
+
* puesto por mes y por día son dos cortes, y con el nombre del campo serían el
|
|
136
|
+
* mismo repetido. `keyOf` da la clave del motor —la misma que arman las otras
|
|
137
|
+
* presentaciones, así que lo plegado vale para todas— y `labelOf` la lee en el
|
|
138
|
+
* idioma activo.
|
|
139
|
+
*/
|
|
140
|
+
export type GroupColumn<T> = {
|
|
141
|
+
/** La identidad del nivel: `levelKey(level)`. */
|
|
142
|
+
id: string;
|
|
143
|
+
/** Cómo se llama el corte, a la izquierda del valor. Sin él, «Septiembre» no
|
|
144
|
+
* dice si es cuándo se registró o cuándo se corrió. */
|
|
145
|
+
label: string;
|
|
146
|
+
/** A qué montón va cada fila en este nivel. */
|
|
147
|
+
keyOf: (item: T) => string;
|
|
148
|
+
/** Cómo se lee esa clave. */
|
|
149
|
+
labelOf: (key: string) => string;
|
|
150
|
+
};
|
|
151
|
+
export declare function DataTable<T extends object>({ data, columns, getRowId, sort, onSortChange, levels, selected, onSelectedChange, height, onRowClick, rowActions, totals, collapsed, onGroupToggle, create, copyable, }: {
|
|
152
|
+
data: readonly T[];
|
|
153
|
+
columns: readonly DataColumn<T>[];
|
|
154
|
+
getRowId: (row: T) => string;
|
|
155
|
+
sort?: SortState | null;
|
|
156
|
+
onSortChange?: (next: SortState | null) => void;
|
|
157
|
+
/**
|
|
158
|
+
* Los NIVELES por los que se parte, de afuera hacia adentro. Vacío o ausente
|
|
159
|
+
* es «sin agrupar».
|
|
160
|
+
*
|
|
161
|
+
* Varios, porque agrupar no es una pregunta: «por cliente y después por
|
|
162
|
+
* mes» es un árbol y no una lista de montones. El orden ES la jerarquía.
|
|
163
|
+
*/
|
|
164
|
+
levels?: readonly GroupColumn<T>[];
|
|
165
|
+
selected?: ReadonlySet<string>;
|
|
166
|
+
onSelectedChange?: (next: Set<string>) => void;
|
|
167
|
+
height?: number;
|
|
168
|
+
onRowClick?: (row: T) => void;
|
|
169
|
+
/** What you do to ONE row without going into it. They are drawn hidden and
|
|
170
|
+
* show up on the hovered row — see `RowActions.tsx`. */
|
|
171
|
+
rowActions?: (row: T) => readonly RowAction[];
|
|
172
|
+
/** The footer with the aggregate of every column that declared one.
|
|
173
|
+
*
|
|
174
|
+
* Context: up to now the aggregate only existed INSIDE a group, so an
|
|
175
|
+
* ungrouped table had no way to say how much it adds up to — and «how much
|
|
176
|
+
* does it add up to» is the question that makes a table of amounts a report
|
|
177
|
+
* and not a list. It stays pinned at the bottom: in a four-hundred-row table,
|
|
178
|
+
* a total you have to scroll to is a total nobody sees. */
|
|
179
|
+
totals?: boolean;
|
|
180
|
+
/** Group keys that are closed. Same keys as `ListGroup.collapsed`, so the
|
|
181
|
+
* table does not reopen what rows just folded. */
|
|
182
|
+
collapsed?: readonly string[];
|
|
183
|
+
onGroupToggle?: (key: string) => void;
|
|
184
|
+
/** Write a new record as a row of the list. See the file's header. */
|
|
185
|
+
create?: DataTableCreate<T>;
|
|
186
|
+
/** The cells' copy buttons, on by default. `false` takes them off the whole table; a
|
|
187
|
+
* column turns its own off with `copy: false`. */
|
|
188
|
+
copyable?: boolean;
|
|
189
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,330 @@
|
|
|
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 { CellCopy } from "./CellCopy.js";
|
|
6
|
+
import { CellEditor } from "./CellEditor.js";
|
|
7
|
+
import { EditRow, firstControl } from "./EditableTable.js";
|
|
8
|
+
import { RowActions } from "./RowActions.js";
|
|
9
|
+
import { SelectBox, Table, Td, Th, THead, Tr, } from "./Table.js";
|
|
10
|
+
import { aggregationFns, columnGroupingFeature, createColumnHelper, createExpandedRowModel, createGroupedRowModel, createSortedRowModel, rowAggregationFeature, rowExpandingFeature, rowSortingFeature, sortFns, tableFeatures, useTable, } from "@tanstack/react-table";
|
|
11
|
+
import { ChevronRight, Plus } from "lucide-react";
|
|
12
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
13
|
+
/** The aggregates that are an amount OF the column — a sum of bolivianos is bolivianos — and
|
|
14
|
+
* so are written the way the column writes its numbers. `count` and `unique` count rows
|
|
15
|
+
* and values: «Bs. 12,00 rows» would be nonsense. */
|
|
16
|
+
const IN_COLUMN_UNITS = new Set(["sum", "mean", "min", "max"]);
|
|
17
|
+
/**
|
|
18
|
+
* How the aggregate of a column is written: its `format` when the aggregate is in the
|
|
19
|
+
* column's units, `undefined` (the bare number) otherwise.
|
|
20
|
+
*
|
|
21
|
+
* Exported for the other places that draw `aggregateOf` — the bands of the sheet, the report
|
|
22
|
+
* built from the piles — so the footer and the band never write the same total two ways.
|
|
23
|
+
* Not public.
|
|
24
|
+
*/
|
|
25
|
+
export function aggregateFormat(column) {
|
|
26
|
+
return column.aggregate !== undefined && IN_COLUMN_UNITS.has(column.aggregate)
|
|
27
|
+
? column.format
|
|
28
|
+
: undefined;
|
|
29
|
+
}
|
|
30
|
+
/** An aggregate of a column, written — or the bare number when nothing says how. */
|
|
31
|
+
export function aggregateText(column, value) {
|
|
32
|
+
const format = aggregateFormat(column);
|
|
33
|
+
return format === undefined ? value : format(value);
|
|
34
|
+
}
|
|
35
|
+
function aggregateLabel(copy, column, v) {
|
|
36
|
+
const n = v.toLocaleString(copy.locale);
|
|
37
|
+
/* Raw, a sum of amounts read «78.752,9»: one decimal lost and no currency, under a
|
|
38
|
+
column whose every cell says «Bs. 12.300,00». */
|
|
39
|
+
const amount = (fallback) => column.format === undefined ? fallback : column.format(v);
|
|
40
|
+
switch (column.aggregate ?? "count") {
|
|
41
|
+
case "sum":
|
|
42
|
+
return amount(n);
|
|
43
|
+
case "count":
|
|
44
|
+
return `${n} ${v === 1 ? copy.table.row : copy.table.rows}`;
|
|
45
|
+
case "mean":
|
|
46
|
+
return fillCopy(copy.table.mean, {
|
|
47
|
+
value: amount(v.toLocaleString(copy.locale, { maximumFractionDigits: 2 })),
|
|
48
|
+
});
|
|
49
|
+
case "min":
|
|
50
|
+
return fillCopy(copy.table.min, { value: amount(n) });
|
|
51
|
+
case "max":
|
|
52
|
+
return fillCopy(copy.table.max, { value: amount(n) });
|
|
53
|
+
case "unique":
|
|
54
|
+
return fillCopy(copy.table.distinct, { value: n });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function raw(copy, v) {
|
|
58
|
+
if (v === null)
|
|
59
|
+
return "—";
|
|
60
|
+
if (typeof v === "number")
|
|
61
|
+
return v.toLocaleString(copy.locale);
|
|
62
|
+
if (typeof v === "boolean")
|
|
63
|
+
return v ? copy.table.yes : copy.table.no;
|
|
64
|
+
return v;
|
|
65
|
+
}
|
|
66
|
+
/** What a cell draws: its `cell`, or the raw value. */
|
|
67
|
+
function drawCell(copy, column, row) {
|
|
68
|
+
return column.cell === undefined ? raw(copy, column.value(row)) : column.cell(row);
|
|
69
|
+
}
|
|
70
|
+
/* The features are declared once. In v9 they are opt-in: asking only for what
|
|
71
|
+
is used is what keeps the bundle down to what is actually needed. */
|
|
72
|
+
const FEATURES = tableFeatures({
|
|
73
|
+
rowSortingFeature,
|
|
74
|
+
columnGroupingFeature,
|
|
75
|
+
rowExpandingFeature,
|
|
76
|
+
// `rowAggregationFeature` is what enables `aggregationFn` on a column.
|
|
77
|
+
// Without it, grouping pulls rows together but each column does not know what
|
|
78
|
+
// to show in the group row — which is half of the reason for grouping.
|
|
79
|
+
rowAggregationFeature,
|
|
80
|
+
sortedRowModel: createSortedRowModel(),
|
|
81
|
+
groupedRowModel: createGroupedRowModel(),
|
|
82
|
+
expandedRowModel: createExpandedRowModel(),
|
|
83
|
+
sortFns,
|
|
84
|
+
aggregationFns,
|
|
85
|
+
});
|
|
86
|
+
/**
|
|
87
|
+
* El agregado de una columna sobre un conjunto de filas.
|
|
88
|
+
*
|
|
89
|
+
* Vive afuera del componente porque lo preguntan tres lugares: el pie de la
|
|
90
|
+
* tabla, la banda de un montón en la planilla, y la línea de un informe armado
|
|
91
|
+
* a partir de los montones. Tres implementaciones de «cuánto suma esto» es que
|
|
92
|
+
* el pie diga un número y el montón otro.
|
|
93
|
+
*
|
|
94
|
+
* `null` cuando la columna no declaró agregado, no es numérica, o no hay ningún
|
|
95
|
+
* número que sumar — que es distinto de sumar cero.
|
|
96
|
+
*/
|
|
97
|
+
export function aggregateOf(column, rows) {
|
|
98
|
+
if (column.aggregate === undefined || column.numeric !== true)
|
|
99
|
+
return null;
|
|
100
|
+
const numbers = rows
|
|
101
|
+
.map((row) => column.value(row))
|
|
102
|
+
.filter((value) => typeof value === "number");
|
|
103
|
+
if (numbers.length === 0)
|
|
104
|
+
return null;
|
|
105
|
+
switch (column.aggregate) {
|
|
106
|
+
case "sum":
|
|
107
|
+
return numbers.reduce((a, b) => a + b, 0);
|
|
108
|
+
case "mean":
|
|
109
|
+
return numbers.reduce((a, b) => a + b, 0) / numbers.length;
|
|
110
|
+
case "min":
|
|
111
|
+
return Math.min(...numbers);
|
|
112
|
+
case "max":
|
|
113
|
+
return Math.max(...numbers);
|
|
114
|
+
case "unique":
|
|
115
|
+
return new Set(numbers).size;
|
|
116
|
+
default:
|
|
117
|
+
return numbers.length;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
export function DataTable({ data, columns, getRowId, sort, onSortChange, levels, selected, onSelectedChange, height, onRowClick, rowActions, totals = false, collapsed, onGroupToggle, create, copyable = true, }) {
|
|
121
|
+
const copy = useUiCopy();
|
|
122
|
+
const draft = create?.draft ?? null;
|
|
123
|
+
const draftRow = useRef(null);
|
|
124
|
+
const addButton = useRef(null);
|
|
125
|
+
/* Whether anything was written in the draft: leaving a blank one drops it. */
|
|
126
|
+
const touched = useRef(false);
|
|
127
|
+
const writing = draft !== null;
|
|
128
|
+
/* Each «add» is a session. With `Tab` the next draft replaces the saved one without
|
|
129
|
+
ever being `null`, so `writing` alone would not say a new record began. */
|
|
130
|
+
const [session, setSession] = useState(0);
|
|
131
|
+
/* «add» is hidden while writing: the focus goes back to it once it is there again. */
|
|
132
|
+
const backToAdd = useRef(false);
|
|
133
|
+
const start = () => {
|
|
134
|
+
setSession((n) => n + 1);
|
|
135
|
+
create?.onStart();
|
|
136
|
+
};
|
|
137
|
+
/* A draft that appears takes the focus on its first control — «add» is a request to
|
|
138
|
+
write, not to look for where to. */
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (!writing) {
|
|
141
|
+
if (backToAdd.current)
|
|
142
|
+
addButton.current?.focus();
|
|
143
|
+
backToAdd.current = false;
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
touched.current = false;
|
|
147
|
+
const node = draftRow.current;
|
|
148
|
+
if (node !== null)
|
|
149
|
+
(firstControl(node) ?? node).focus();
|
|
150
|
+
}, [writing, session]);
|
|
151
|
+
const saveDraft = (refocus) => {
|
|
152
|
+
if (create === undefined || draft === null)
|
|
153
|
+
return;
|
|
154
|
+
backToAdd.current = refocus;
|
|
155
|
+
if (touched.current)
|
|
156
|
+
create.onCommit(draft);
|
|
157
|
+
else
|
|
158
|
+
create.onCancel();
|
|
159
|
+
};
|
|
160
|
+
// In v9 the helper is bound to the features AND to the data type: that is what
|
|
161
|
+
// makes the compiler know about `enableGrouping` only if we ask for the
|
|
162
|
+
// feature.
|
|
163
|
+
const helper = useMemo(() => createColumnHelper(), []);
|
|
164
|
+
const defs = useMemo(() => [
|
|
165
|
+
...columns.map((c) => helper.accessor((row) => c.value(row), {
|
|
166
|
+
id: c.id,
|
|
167
|
+
header: c.header,
|
|
168
|
+
enableSorting: c.sortable !== false,
|
|
169
|
+
/* Ninguna columna agrupa. Lo que parte el conjunto son los NIVELES, y
|
|
170
|
+
cada uno entra como una columna suya —abajo— que existe sólo para
|
|
171
|
+
eso: nadie la dibuja y nadie la ordena. */
|
|
172
|
+
enableGrouping: false,
|
|
173
|
+
aggregationFn: c.aggregate ?? "count",
|
|
174
|
+
})),
|
|
175
|
+
/* Una columna por NIVEL, no por campo. Es la única forma de que «por mes y
|
|
176
|
+
adentro por día» sea dos cortes: con el id de la columna, los dos niveles
|
|
177
|
+
de `registered_at` eran el mismo id repetido, y un mismo id no se puede
|
|
178
|
+
agrupar dos veces.
|
|
179
|
+
|
|
180
|
+
Y la clave la pone el nivel, no la columna: `value` es la clave de
|
|
181
|
+
ORDEN —para una fecha, los milisegundos— y agrupar por ella deja un
|
|
182
|
+
montón por instante, con «1789294903895» de rótulo. Acá entra la misma
|
|
183
|
+
clave que arman las otras cinco presentaciones, así que los montones de
|
|
184
|
+
la tabla son LOS MISMOS y lo plegado vale para todas. */
|
|
185
|
+
...(levels ?? []).map((level) => helper.accessor((row) => level.keyOf(row), {
|
|
186
|
+
id: level.id,
|
|
187
|
+
header: level.label,
|
|
188
|
+
enableSorting: false,
|
|
189
|
+
enableGrouping: true,
|
|
190
|
+
})),
|
|
191
|
+
], [columns, helper, levels]);
|
|
192
|
+
/* The assertion, and why it is safe: `ColumnDef` is INVARIANT in its value
|
|
193
|
+
type (`in out TValue`), so an array of columns with concrete values is not
|
|
194
|
+
assignable to the one of `unknown` even though each one on its own is. The
|
|
195
|
+
library solves this with `helper.columns([...])`, which needs a literal
|
|
196
|
+
tuple — and here the columns come out of a `map` over what the consumer
|
|
197
|
+
declared, which is not literal.
|
|
198
|
+
|
|
199
|
+
It is safe because that value is only used by the library to sort and
|
|
200
|
+
group; it is never read with its type from our side. What gets drawn comes
|
|
201
|
+
from `c.cell(row)` or from `c.value(row)`, both typed against `T`. */
|
|
202
|
+
const table = useTable({
|
|
203
|
+
features: FEATURES,
|
|
204
|
+
data,
|
|
205
|
+
columns: defs,
|
|
206
|
+
getRowId: (row) => getRowId(row),
|
|
207
|
+
state: {
|
|
208
|
+
sorting: sort === null || sort === undefined
|
|
209
|
+
? []
|
|
210
|
+
: [{ id: sort.key, desc: sort.direction === "desc" }],
|
|
211
|
+
grouping: (levels ?? []).map((level) => level.id),
|
|
212
|
+
},
|
|
213
|
+
// Groups start open: grouping and then having to open each group is two
|
|
214
|
+
// gestures for what the user asked for in one.
|
|
215
|
+
initialState: { expanded: true },
|
|
216
|
+
});
|
|
217
|
+
const rows = table.getRowModel().rows;
|
|
218
|
+
/**
|
|
219
|
+
* The PATH of a row: the values of every group it hangs from, outermost
|
|
220
|
+
* first.
|
|
221
|
+
*
|
|
222
|
+
* The identity of a pile is its route and not its value, because with several
|
|
223
|
+
* levels two piles can be called the same — «2026-09» inside «Ferretería Sur» and
|
|
224
|
+
* «2026-09» inside «Distribuidora Andina» are different piles, and folding one must not
|
|
225
|
+
* fold the other. It walks up because that is where TanStack keeps the
|
|
226
|
+
* ancestry; a leaf contributes nothing, since `groupingColumnId` is only set
|
|
227
|
+
* on grouped rows.
|
|
228
|
+
*/
|
|
229
|
+
function pathOf(row) {
|
|
230
|
+
const path = [];
|
|
231
|
+
let node = row;
|
|
232
|
+
while (node !== undefined) {
|
|
233
|
+
const columnId = node.groupingColumnId;
|
|
234
|
+
if (columnId !== undefined && columnId !== null) {
|
|
235
|
+
path.unshift(String(node.getGroupingValue(columnId) ?? "—"));
|
|
236
|
+
}
|
|
237
|
+
node = node.getParentRow();
|
|
238
|
+
}
|
|
239
|
+
return path;
|
|
240
|
+
}
|
|
241
|
+
/* TanStack keeps every leaf in the model when `expanded: true`. Folding is
|
|
242
|
+
ours: the same path keys as the rows presentation, so switching present
|
|
243
|
+
does not reopen a group.
|
|
244
|
+
|
|
245
|
+
It checks EVERY ancestor and not just the parent. With one level they were
|
|
246
|
+
the same thing; with several, folding the outer pile has to take the inner
|
|
247
|
+
ones with it — otherwise closing «Ferretería Sur» leaves its months on screen,
|
|
248
|
+
orphaned under a header that says it is closed. */
|
|
249
|
+
const drawn = collapsed === undefined
|
|
250
|
+
? rows
|
|
251
|
+
: rows.filter((row) => {
|
|
252
|
+
let node = row.getParentRow();
|
|
253
|
+
while (node !== undefined) {
|
|
254
|
+
if (collapsed.includes(pathKey(pathOf(node))))
|
|
255
|
+
return false;
|
|
256
|
+
node = node.getParentRow();
|
|
257
|
+
}
|
|
258
|
+
return true;
|
|
259
|
+
});
|
|
260
|
+
const selecting = onSelectedChange !== undefined;
|
|
261
|
+
const leaves = useMemo(() => rows.filter((r) => r.subRows.length === 0), [rows]);
|
|
262
|
+
/* The aggregates are computed by the component and not by TanStack: its
|
|
263
|
+
`aggregationFn` lives in the GROUP model, and a footer exists precisely when
|
|
264
|
+
there are no groups. */
|
|
265
|
+
const footer = useMemo(() => (totals ? columns.map((c) => aggregateOf(c, data)) : null), [totals, columns, data]);
|
|
266
|
+
const all = leaves.length > 0 && leaves.every((r) => selected?.has(r.id) === true);
|
|
267
|
+
const some = leaves.some((r) => selected?.has(r.id) === true);
|
|
268
|
+
function toggle(id, on) {
|
|
269
|
+
const n = new Set(selected ?? []);
|
|
270
|
+
if (on)
|
|
271
|
+
n.add(id);
|
|
272
|
+
else
|
|
273
|
+
n.delete(id);
|
|
274
|
+
onSelectedChange?.(n);
|
|
275
|
+
}
|
|
276
|
+
const grid = (_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: "" })] }), _jsxs("tbody", { children: [drawn.map((row) => {
|
|
277
|
+
const isGroup = row.subRows.length > 0;
|
|
278
|
+
if (isGroup) {
|
|
279
|
+
const path = pathOf(row);
|
|
280
|
+
const key = pathKey(path);
|
|
281
|
+
const depth = path.length - 1;
|
|
282
|
+
const id = row.groupingColumnId ?? undefined;
|
|
283
|
+
const level = levels?.find((one) => one.id === id);
|
|
284
|
+
/* La CLAVE del montón —la del motor— y el rótulo, que lo pone el
|
|
285
|
+
nivel: son dos textos y sólo el segundo cambia con el idioma. */
|
|
286
|
+
const value = path[depth] ?? key;
|
|
287
|
+
const open = collapsed === undefined
|
|
288
|
+
? row.getIsExpanded()
|
|
289
|
+
: collapsed.includes(key) !== true;
|
|
290
|
+
return (
|
|
291
|
+
/* The group row: it spans the whole width, says which value it
|
|
292
|
+
groups by and how many are inside. The counter is what makes it
|
|
293
|
+
useful without opening it. */
|
|
294
|
+
_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
|
|
295
|
+
? undefined
|
|
296
|
+
: { 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));
|
|
297
|
+
}
|
|
298
|
+
const original = row.original;
|
|
299
|
+
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) => {
|
|
300
|
+
const t = c.tone?.(original);
|
|
301
|
+
const n = c.note?.(original);
|
|
302
|
+
const edit = c.edit?.when?.(original) === false ? undefined : c.edit;
|
|
303
|
+
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) })) : copyable && c.copy !== false ? (_jsx(CellCopy, { header: c.header, numeric: c.numeric === true, ...(c.copy === undefined ? {} : { value: c.copy(original) }), children: drawCell(copy, c, original) })) : (drawCell(copy, c, original)) }, c.id));
|
|
304
|
+
}), rowActions === undefined ? null : (_jsx(RowActions, { actions: rowActions(original) }))] }, row.id));
|
|
305
|
+
}), create === undefined || draft === null ? null : (_jsxs(EditRow, { as: "tr", rowId: "\\u0000draft", rowRef: (node) => {
|
|
306
|
+
draftRow.current = node;
|
|
307
|
+
}, editable: true, editing: true, tabbable: false, className: "group/row", onStart: () => { }, onCommit: saveDraft, onCancel: () => {
|
|
308
|
+
backToAdd.current = true;
|
|
309
|
+
create.onCancel();
|
|
310
|
+
}, onMove: () => { }, onFocusRow: () => { }, onTabOut: (direction) => {
|
|
311
|
+
/* Past the last control: saved, and the next one started — unless nothing
|
|
312
|
+
was written, which is «I am done». Backwards, the browser's way. */
|
|
313
|
+
if (direction === "prev" || !touched.current)
|
|
314
|
+
return false;
|
|
315
|
+
create.onCommit(draft);
|
|
316
|
+
start();
|
|
317
|
+
return true;
|
|
318
|
+
}, children: [selecting ? _jsx("td", { className: DRAFT_CELL }) : null, columns.map((c) => (_jsx("td", { ...(c.input === undefined ? {} : { "data-edit": "" }), className: `${DRAFT_CELL} ${c.numeric === true ? "text-right tabular-nums" : ""}`, children: c.input?.(draft, (next) => {
|
|
319
|
+
touched.current = true;
|
|
320
|
+
create.onChange(next);
|
|
321
|
+
}) ?? null }, c.id))), rowActions === undefined ? null : _jsx("td", { className: DRAFT_CELL })] }))] }), 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) => {
|
|
322
|
+
const value = footer[i] ?? null;
|
|
323
|
+
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, value)) }, c.id));
|
|
324
|
+
}), rowActions === undefined ? null : _jsx("td", {})] }) }))] }));
|
|
325
|
+
if (create === undefined)
|
|
326
|
+
return grid;
|
|
327
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col", children: [grid, writing ? null : (_jsx("div", { className: "px-1 py-0.5", children: _jsxs("button", { ref: addButton, type: "button", onClick: start, className: "flex w-full items-center gap-1.5 rounded-control px-3 py-1 text-left text-meta text-primary transition-colors duration-(--duration-instant) hover:bg-primary/5 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-primary", children: [_jsx(Plus, { className: "size-(--size-icon-sm)", strokeWidth: 2.5, "aria-hidden": true }), create.label ?? copy.table.add] }) }))] }));
|
|
328
|
+
}
|
|
329
|
+
/* A cell of the row being written: the table's cell, washed like an edited row. */
|
|
330
|
+
const DRAFT_CELL = "border-b border-line bg-primary/5 px-3 py-1 align-middle text-meta text-ink first:rounded-l-control last:rounded-r-control";
|