@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,83 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { byDensity, useDensity } from "../../foundations/density.js";
|
|
4
|
+
import { FieldLayoutProvider, } from "../../primitives/field/layout.js";
|
|
5
|
+
import { Hint } from "../../primitives/hint/Hint.js";
|
|
6
|
+
import { Children } from "react";
|
|
7
|
+
function Facts({ fields, columns, layout, }) {
|
|
8
|
+
const d = useDensity();
|
|
9
|
+
const horizontal = layout === "horizontal";
|
|
10
|
+
/* Horizontal facts are a run of pairs, not a grid stretched across the
|
|
11
|
+
paper. A two-column grid put «Total Bs 12.480» on the left and «Vence
|
|
12
|
+
15 nov» on the right, with the middle of the card empty. */
|
|
13
|
+
const asGrid = !horizontal && columns > 1 && d === "comfortable";
|
|
14
|
+
const grid = columns === 4
|
|
15
|
+
? "grid-cols-2 @lg:grid-cols-4"
|
|
16
|
+
: columns === 3
|
|
17
|
+
? "grid-cols-3"
|
|
18
|
+
: "grid-cols-2";
|
|
19
|
+
return (_jsx("dl", { className: horizontal
|
|
20
|
+
? "flex min-w-0 flex-wrap items-baseline gap-x-6 gap-y-1.5"
|
|
21
|
+
: asGrid
|
|
22
|
+
? `@container grid min-w-0 ${grid} ${byDensity(d, "gap-x-8 gap-y-2.5", "gap-x-6 gap-y-2")}`
|
|
23
|
+
: `flex min-w-0 flex-col ${byDensity(d, "gap-2.5", "gap-2")}`, children: fields.map((field) => {
|
|
24
|
+
const empty = field.value === undefined || field.value === null || field.value === "";
|
|
25
|
+
const value = (_jsx("dd", { className: `min-w-0 text-meta ${empty ? "text-muted italic" : "text-ink"} ${field.mono === true ? "font-mono tabular-nums" : ""}`, children: empty ? "—" : field.value }));
|
|
26
|
+
const name = (_jsxs("dt", { className: `flex items-center gap-1 text-micro text-muted ${horizontal ? "shrink-0" : "min-w-0"}`, children: [_jsx("span", { children: field.label }), field.hint === undefined ? null : _jsx(Hint, { children: field.hint })] }));
|
|
27
|
+
if (horizontal) {
|
|
28
|
+
/* A header fact is a pair: the number sits next to its name. The
|
|
29
|
+
8rem label column is for a form, where twenty values have to
|
|
30
|
+
start on the same vertical. Here it left «Filas» half a column
|
|
31
|
+
away from 400. */
|
|
32
|
+
return (_jsxs("div", { className: "flex min-w-0 items-baseline gap-2", children: [name, value] }, field.label));
|
|
33
|
+
}
|
|
34
|
+
return (_jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [name, value] }, field.label));
|
|
35
|
+
}) }));
|
|
36
|
+
}
|
|
37
|
+
export function Sheet({ fields, columns = 2, layout = "stacked", head, children, }) {
|
|
38
|
+
const d = useDensity();
|
|
39
|
+
const facts = fields !== undefined && fields.length > 0;
|
|
40
|
+
if (!facts && head === undefined && children === undefined)
|
|
41
|
+
return null;
|
|
42
|
+
const top = facts || head !== undefined ? (_jsxs("div", { className: `flex min-w-0 flex-col ${byDensity(d, "gap-4", "gap-3")}`, children: [facts ? _jsx(Facts, { fields: fields, columns: columns, layout: layout }) : null, head] })) : null;
|
|
43
|
+
return (_jsx(FieldLayoutProvider, { value: layout, children: _jsxs("div", { className: `flex min-w-0 flex-col ${byDensity(d, "gap-(--spacing-block)", "gap-2.5")}`, children: [top, children] }) }));
|
|
44
|
+
}
|
|
45
|
+
/** A row of columns on the paper. The paper is the sheet; a group is not a
|
|
46
|
+
* second box. A vertical hairline separates columns — that is what still
|
|
47
|
+
* groups them when a column has no title. */
|
|
48
|
+
export function SheetGroups({ children, columns = 2, }) {
|
|
49
|
+
const d = useDensity();
|
|
50
|
+
const stacked = d === "compact";
|
|
51
|
+
// Below `handheld` the columns stack the way compact density does: two 180 px
|
|
52
|
+
// columns on a phone left each label beside a control with no width at all. The
|
|
53
|
+
// container is a wrapper because a grid cannot query its own width.
|
|
54
|
+
const grid = stacked
|
|
55
|
+
? "grid-cols-1"
|
|
56
|
+
: columns === 4
|
|
57
|
+
? "grid-cols-2 @lg:grid-cols-4 @max-handheld:grid-cols-1"
|
|
58
|
+
: columns === 3
|
|
59
|
+
? "grid-cols-3 @max-handheld:grid-cols-1"
|
|
60
|
+
: "grid-cols-2 @max-handheld:grid-cols-1";
|
|
61
|
+
const items = Children.toArray(children);
|
|
62
|
+
return (_jsx("div", { className: "@container min-w-0", children: _jsx("div", { className: `grid min-w-0 ${grid}`, children: items.map((child, i) => {
|
|
63
|
+
const first = i === 0;
|
|
64
|
+
const last = i === items.length - 1;
|
|
65
|
+
const edge = stacked
|
|
66
|
+
? `${first ? "" : "border-t border-line pt-3"} ${last ? "" : "pb-3"}`
|
|
67
|
+
: `${first ? "" : "border-l border-line pl-6 @max-handheld:border-l-0 @max-handheld:border-t @max-handheld:pl-0 @max-handheld:pt-3"} ${last ? "" : "pr-6 @max-handheld:pr-0 @max-handheld:pb-3"}`;
|
|
68
|
+
return (_jsx("div", { className: `min-w-0 ${edge}`, children: child }, i));
|
|
69
|
+
}) }) }));
|
|
70
|
+
}
|
|
71
|
+
/** One column of a `SheetGroups` row.
|
|
72
|
+
*
|
|
73
|
+
* The title is OPTIONAL. With one, it is a name above the fields — the same
|
|
74
|
+
* name in read and in edit, so the paper does not rearrange. Without one,
|
|
75
|
+
* the column is still a group: the hairline between columns is the edge.
|
|
76
|
+
*
|
|
77
|
+
* It is not a tray and not a card. Nested boxes on the sheet made untitled
|
|
78
|
+
* groups look empty and made edit mode look like a dashboard. */
|
|
79
|
+
export function SheetGroup({ title, hint, icon: Icon, children, }) {
|
|
80
|
+
const d = useDensity();
|
|
81
|
+
const withHeader = title !== undefined;
|
|
82
|
+
return (_jsxs("section", { className: `flex min-w-0 flex-col ${byDensity(d, "gap-2.5", "gap-2")}`, children: [withHeader ? (_jsxs("header", { className: "flex min-w-0 items-center gap-2", children: [Icon === undefined ? null : (_jsx(Icon, { className: "size-(--size-icon-sm) shrink-0 text-muted", strokeWidth: 1.75, "aria-hidden": true })), _jsx("h3", { className: "min-w-0 truncate text-meta font-medium text-ink", children: title }), hint === undefined ? null : _jsx(Hint, { children: hint })] })) : hint === undefined ? null : (_jsx(Hint, { children: hint })), _jsx("div", { className: `flex min-w-0 flex-col ${byDensity(d, "gap-2.5", "gap-2")}`, children: children })] }));
|
|
83
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SeriesBar } from "./model.js";
|
|
2
|
+
/**
|
|
3
|
+
* One series, drawn as a bar list: the name, the count, and a track.
|
|
4
|
+
*
|
|
5
|
+
* A column of vertical bars with the names in a legend underneath asks the
|
|
6
|
+
* eye to match a bar to a word. Here the word sits on its own bar, which is
|
|
7
|
+
* the question this piece answers — how the values of one column are spread.
|
|
8
|
+
* CSS, not a charting library: the width is a percentage of the largest
|
|
9
|
+
* count, and the color is the data color, one series, not a palette.
|
|
10
|
+
*/
|
|
11
|
+
export declare function SeriesChart({ series }: {
|
|
12
|
+
series: readonly SeriesBar[];
|
|
13
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useUiCopy } from "../../foundations/copy.js";
|
|
4
|
+
/**
|
|
5
|
+
* One series, drawn as a bar list: the name, the count, and a track.
|
|
6
|
+
*
|
|
7
|
+
* A column of vertical bars with the names in a legend underneath asks the
|
|
8
|
+
* eye to match a bar to a word. Here the word sits on its own bar, which is
|
|
9
|
+
* the question this piece answers — how the values of one column are spread.
|
|
10
|
+
* CSS, not a charting library: the width is a percentage of the largest
|
|
11
|
+
* count, and the color is the data color, one series, not a palette.
|
|
12
|
+
*/
|
|
13
|
+
export function SeriesChart({ series }) {
|
|
14
|
+
const copy = useUiCopy();
|
|
15
|
+
const peak = Math.max(1, ...series.map((bar) => bar.count));
|
|
16
|
+
if (series.length === 0) {
|
|
17
|
+
return _jsx("p", { className: "px-3 py-4 text-body text-muted", children: copy.frame.noSeries });
|
|
18
|
+
}
|
|
19
|
+
return (_jsx("ul", { className: "flex flex-col gap-2.5 px-3 py-3", children: series.map((bar) => {
|
|
20
|
+
const width = Math.max((bar.count / peak) * 100, 2);
|
|
21
|
+
const count = bar.count.toLocaleString(copy.locale);
|
|
22
|
+
return (_jsxs("li", { className: "flex min-w-0 flex-col gap-1", title: `${bar.label}: ${count}`, children: [_jsxs("div", { className: "flex min-w-0 items-baseline justify-between gap-3", children: [_jsx("span", { className: "min-w-0 truncate text-meta text-ink", children: bar.label }), _jsx("span", { className: "shrink-0 text-micro tabular-nums text-muted", children: count })] }), _jsx("div", { className: "h-1.5 overflow-hidden rounded-pill bg-ink/8", children: _jsx("div", { className: "h-full rounded-pill bg-data", style: { width: `${width}%` } }) })] }, bar.label));
|
|
23
|
+
}) }));
|
|
24
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Frame, type FramePresent } from "./model.js";
|
|
2
|
+
import { type ListIdentity } from "../listing/ListingBar.js";
|
|
3
|
+
import { type Grouping } from "../../foundations/grouping.js";
|
|
4
|
+
import { type ReactNode } from "react";
|
|
5
|
+
export type FrameViewProps = {
|
|
6
|
+
frame: Frame;
|
|
7
|
+
identity?: ListIdentity;
|
|
8
|
+
caption?: ReactNode;
|
|
9
|
+
present?: FramePresent;
|
|
10
|
+
presents?: readonly FramePresent[];
|
|
11
|
+
onPresentChange?: (next: FramePresent) => void;
|
|
12
|
+
/** Which column the chart reads. Defaults to the first numeric, else the first. */
|
|
13
|
+
chartColumn?: string;
|
|
14
|
+
onChartColumnChange?: (id: string) => void;
|
|
15
|
+
visibleColumnIds?: readonly string[];
|
|
16
|
+
onVisibleColumnIdsChange?: (ids: readonly string[]) => void;
|
|
17
|
+
/** Search, filters, pagination: the same slots as a list. A frame inside a
|
|
18
|
+
* form is still a SET of rows, and a set without a narrowing line is a
|
|
19
|
+
* dump. Assembled by whoever holds the domain — this file does not know
|
|
20
|
+
* what a column name is. */
|
|
21
|
+
search?: ReactNode;
|
|
22
|
+
pagination?: ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* El agrupado, con el mismo contrato que usa la lista.
|
|
25
|
+
*
|
|
26
|
+
* Acá lo que se agrupa son ÍNDICES de fila y no registros: un marco es una
|
|
27
|
+
* matriz y sus filas no tienen identidad propia —son la fila 12 del archivo—,
|
|
28
|
+
* así que `T` es `number` y `of(fila, nivel)` lee la celda que corresponda. El
|
|
29
|
+
* contrato no cambia, que es exactamente la prueba de que no es restrictivo.
|
|
30
|
+
*/
|
|
31
|
+
grouping?: Grouping<number>;
|
|
32
|
+
};
|
|
33
|
+
export declare function FrameView({ frame, identity, caption, present: presentProp, presents, onPresentChange, chartColumn: chartColumnProp, onChartColumnChange, visibleColumnIds, onVisibleColumnIdsChange, search, pagination, grouping, }: FrameViewProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { SeriesChart } from "./Chart.js";
|
|
4
|
+
import { columnSeries, withColumns, } from "./model.js";
|
|
5
|
+
import { useColumnControl } from "../listing/columns.js";
|
|
6
|
+
import { ColumnsMenu, specsFromColumns, } from "../listing/ColumnsMenu.js";
|
|
7
|
+
import { Listing } from "../listing/Listing.js";
|
|
8
|
+
import { ListingBar } from "../listing/ListingBar.js";
|
|
9
|
+
import { ViewSwitcher } from "../listing/ViewSwitcher.js";
|
|
10
|
+
import { useUiCopy } from "../../foundations/copy.js";
|
|
11
|
+
import { pathKey, pileIsOpen, pilesOf, } from "../../foundations/grouping.js";
|
|
12
|
+
import { Menu } from "../../primitives/menu/Menu.js";
|
|
13
|
+
import { Empty } from "../../primitives/notice/Notice.js";
|
|
14
|
+
import { SheetTable } from "../../primitives/table/SheetTable.js";
|
|
15
|
+
import { BarChart3, Table2 } from "lucide-react";
|
|
16
|
+
import { useMemo, useState } from "react";
|
|
17
|
+
/**
|
|
18
|
+
* ARCHETYPE 3 — a FRAME: the cells of a payload, as a table or as a chart.
|
|
19
|
+
*
|
|
20
|
+
* Context: ListView is N records of a kind. Record is ONE record. Neither
|
|
21
|
+
* is the glance at the raw matrix an imported file carries — that was being drawn as
|
|
22
|
+
* a naked SheetTable, so it could not change presentation, hide a column or
|
|
23
|
+
* chart one, which is exactly what looking at a table is for.
|
|
24
|
+
*
|
|
25
|
+
* Columns use the same `useColumnControl` as ListView. Payload columns default
|
|
26
|
+
* to optional; pin one with `optional: false`.
|
|
27
|
+
*
|
|
28
|
+
* The app maps a preview (or any rectangle) onto a `Frame`. This archetype
|
|
29
|
+
* does not know what an invoice is.
|
|
30
|
+
*/
|
|
31
|
+
const PRESENT_ICONS = [
|
|
32
|
+
{ id: "table", icon: Table2 },
|
|
33
|
+
{ id: "chart", icon: BarChart3 },
|
|
34
|
+
];
|
|
35
|
+
export function FrameView({ frame, identity, caption, present: presentProp, presents = ["table", "chart"], onPresentChange, chartColumn: chartColumnProp, onChartColumnChange, visibleColumnIds, onVisibleColumnIdsChange, search, pagination, grouping, }) {
|
|
36
|
+
const copy = useUiCopy();
|
|
37
|
+
const [ownedPresent, setOwnedPresent] = useState(presentProp ?? "table");
|
|
38
|
+
const present = onPresentChange !== undefined ? (presentProp ?? "table") : ownedPresent;
|
|
39
|
+
const setPresent = onPresentChange ?? setOwnedPresent;
|
|
40
|
+
const defaultChart = frame.columns.find((column) => column.numeric)?.id ?? frame.columns[0]?.id ?? "";
|
|
41
|
+
const [ownedChart, setOwnedChart] = useState(chartColumnProp ?? defaultChart);
|
|
42
|
+
const chartColumn = onChartColumnChange !== undefined ? (chartColumnProp ?? defaultChart) : ownedChart;
|
|
43
|
+
const setChartColumn = onChartColumnChange ?? setOwnedChart;
|
|
44
|
+
const columnSpecs = useMemo(() => specsFromColumns(frame.columns, true), [frame.columns]);
|
|
45
|
+
const columns = useColumnControl(columnSpecs, {
|
|
46
|
+
...(visibleColumnIds === undefined ? {} : { visibleIds: visibleColumnIds }),
|
|
47
|
+
...(onVisibleColumnIdsChange === undefined ? {} : { onVisibleColumnIdsChange }),
|
|
48
|
+
});
|
|
49
|
+
const shown = useMemo(() => columns.visibleIds.length === frame.columns.length
|
|
50
|
+
? frame
|
|
51
|
+
: withColumns(frame, columns.visibleIds), [frame, columns.visibleIds]);
|
|
52
|
+
/* Los montones aplanados a una SECUENCIA: la planilla está virtualizada por
|
|
53
|
+
índice, así que la jerarquía la dice la sangría de cada banda y no el
|
|
54
|
+
anidamiento. Es el mismo aplanado que hace la lista, sobre índices en vez de
|
|
55
|
+
registros. */
|
|
56
|
+
const bands = useMemo(() => {
|
|
57
|
+
const piles = pilesOf(Array.from({ length: shown.rows }, (_, index) => index), grouping);
|
|
58
|
+
if (piles === null)
|
|
59
|
+
return null;
|
|
60
|
+
const out = [];
|
|
61
|
+
const walk = (list) => {
|
|
62
|
+
for (const pile of list) {
|
|
63
|
+
const open = pileIsOpen(pile, grouping);
|
|
64
|
+
out.push({
|
|
65
|
+
band: {
|
|
66
|
+
field: pile.field,
|
|
67
|
+
label: pile.label,
|
|
68
|
+
count: pile.count,
|
|
69
|
+
open,
|
|
70
|
+
depth: pile.depth,
|
|
71
|
+
key: pathKey(pile.path),
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
if (!open)
|
|
75
|
+
continue;
|
|
76
|
+
if (pile.piles.length > 0)
|
|
77
|
+
walk(pile.piles);
|
|
78
|
+
else
|
|
79
|
+
for (const row of pile.items)
|
|
80
|
+
out.push({ row });
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
walk(piles);
|
|
84
|
+
return out;
|
|
85
|
+
}, [shown.rows, grouping]);
|
|
86
|
+
const series = useMemo(() => (present === "chart" ? columnSeries(shown, chartColumn) : []), [present, shown, chartColumn]);
|
|
87
|
+
const switcher = presents.length > 1 ? (_jsx(ViewSwitcher, { options: PRESENT_ICONS.filter((option) => presents.includes(option.id)).map((option) => ({
|
|
88
|
+
id: option.id,
|
|
89
|
+
label: option.id === "table" ? copy.present.table : copy.present.chart,
|
|
90
|
+
icon: option.icon,
|
|
91
|
+
})), current: present, onSelect: (id) => setPresent(id) })) : undefined;
|
|
92
|
+
const chartMenu = present === "chart" ? (_jsx(Menu, { label: copy.frame.series, groups: [
|
|
93
|
+
{
|
|
94
|
+
label: copy.frame.column,
|
|
95
|
+
options: shown.columns.map((column) => ({
|
|
96
|
+
id: column.id,
|
|
97
|
+
label: column.type === undefined
|
|
98
|
+
? column.header
|
|
99
|
+
: `${column.header} · ${column.type}`,
|
|
100
|
+
checked: column.id === chartColumn,
|
|
101
|
+
})),
|
|
102
|
+
},
|
|
103
|
+
], onSelect: setChartColumn })) : null;
|
|
104
|
+
const bar = (_jsx(ListingBar, { ...(identity?.title === undefined ? {} : { title: identity.title }), ...(identity?.description === undefined ? {} : { description: identity.description }), ...(identity?.counters === undefined ? {} : { counters: identity.counters }), ...(search === undefined ? {} : { search }), ...(pagination === undefined ? {} : { pagination }), views: _jsxs(_Fragment, { children: [_jsx(ColumnsMenu, { control: columns }), chartMenu, switcher] }) }));
|
|
105
|
+
if (frame.rows === 0 || frame.columns.length === 0) {
|
|
106
|
+
return (_jsx(Listing, { bar: bar, children: _jsx(Empty, { sentence: copy.frame.empty }) }));
|
|
107
|
+
}
|
|
108
|
+
return (_jsx(Listing, { bar: bar, children: present === "chart" ? (_jsx(SeriesChart, { series: series })) : (_jsx(SheetTable, { columns: shown.columns, rows: bands === null ? shown.rows : bands.length, value: (index, column, columnIndex) => {
|
|
109
|
+
const row = bands === null ? index : bands[index]?.row;
|
|
110
|
+
return row === undefined ? null : shown.value(row, column, columnIndex);
|
|
111
|
+
}, height: "auto", ...(bands === null
|
|
112
|
+
? {}
|
|
113
|
+
: {
|
|
114
|
+
band: (index) => bands[index]?.band,
|
|
115
|
+
...(grouping?.onToggle === undefined
|
|
116
|
+
? {}
|
|
117
|
+
: {
|
|
118
|
+
onBandToggle: (index) => {
|
|
119
|
+
const key = bands[index]?.band?.key;
|
|
120
|
+
if (key !== undefined)
|
|
121
|
+
grouping.onToggle?.(key);
|
|
122
|
+
},
|
|
123
|
+
}),
|
|
124
|
+
}), ...(caption === undefined ? {} : { caption }) })) }));
|
|
125
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A FRAME is a rectangle of values: columns × rows.
|
|
3
|
+
*
|
|
4
|
+
* It is not a domain record and it is not a set of records. ListView talks
|
|
5
|
+
* about N entities of the same kind (the Facturas list, a queue). Record talks
|
|
6
|
+
* about ONE entity. The frame talks about the PAYLOAD — the cells that a
|
|
7
|
+
* record carries, or that a glance extracted from a file — so they can be
|
|
8
|
+
* shown as a table or as a chart, and so a column can be hidden, without the
|
|
9
|
+
* design system knowing what an invoice is.
|
|
10
|
+
*
|
|
11
|
+
* The value is a function, not a copied matrix: the consumer already has the
|
|
12
|
+
* bytes somewhere and this layer only reads them.
|
|
13
|
+
*/
|
|
14
|
+
export type FrameCell = string | number | null;
|
|
15
|
+
export type FrameColumn = {
|
|
16
|
+
id: string;
|
|
17
|
+
header: string;
|
|
18
|
+
type?: string;
|
|
19
|
+
numeric?: boolean;
|
|
20
|
+
width?: number;
|
|
21
|
+
/** Payload columns default to optional. Set `false` to pin a column. */
|
|
22
|
+
optional?: boolean;
|
|
23
|
+
defaultHidden?: boolean;
|
|
24
|
+
};
|
|
25
|
+
export type Frame = {
|
|
26
|
+
columns: readonly FrameColumn[];
|
|
27
|
+
rows: number;
|
|
28
|
+
value: (row: number, column: FrameColumn, columnIndex: number) => FrameCell;
|
|
29
|
+
};
|
|
30
|
+
export type FramePresent = "table" | "chart";
|
|
31
|
+
export type SeriesBar = {
|
|
32
|
+
label: string;
|
|
33
|
+
count: number;
|
|
34
|
+
};
|
|
35
|
+
export declare function frameFromRows(columns: readonly FrameColumn[], rows: readonly (readonly FrameCell[])[]): Frame;
|
|
36
|
+
/** Keep only some columns. The cells still come from the original frame. */
|
|
37
|
+
export declare function withColumns(frame: Frame, ids: readonly string[]): Frame;
|
|
38
|
+
/**
|
|
39
|
+
* Turn one column into bars: a histogram if the values are numbers, the
|
|
40
|
+
* most frequent labels otherwise. Twelve bars is the ceiling — more than
|
|
41
|
+
* that is a table again.
|
|
42
|
+
*/
|
|
43
|
+
export declare function columnSeries(frame: Frame, columnId: string): SeriesBar[];
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { currentCopy, currentCopyLocale } from "../../foundations/copy.js";
|
|
2
|
+
const BINS = 12;
|
|
3
|
+
const TOP = 12;
|
|
4
|
+
export function frameFromRows(columns, rows) {
|
|
5
|
+
return {
|
|
6
|
+
columns,
|
|
7
|
+
rows: rows.length,
|
|
8
|
+
value: (row, _column, columnIndex) => rows[row]?.[columnIndex] ?? null,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/** Keep only some columns. The cells still come from the original frame. */
|
|
12
|
+
export function withColumns(frame, ids) {
|
|
13
|
+
const wanted = new Set(ids);
|
|
14
|
+
const columns = frame.columns.filter((column) => wanted.has(column.id));
|
|
15
|
+
const indexOf = new Map(frame.columns.map((column, index) => [column.id, index]));
|
|
16
|
+
return {
|
|
17
|
+
columns,
|
|
18
|
+
rows: frame.rows,
|
|
19
|
+
value: (row, column) => {
|
|
20
|
+
const index = indexOf.get(column.id) ?? 0;
|
|
21
|
+
return frame.value(row, column, index);
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function asNumber(cell) {
|
|
26
|
+
if (typeof cell === "number" && Number.isFinite(cell))
|
|
27
|
+
return cell;
|
|
28
|
+
if (typeof cell === "string" && cell.trim() !== "") {
|
|
29
|
+
const n = Number(cell.replace(",", "."));
|
|
30
|
+
return Number.isFinite(n) ? n : null;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
function present(cell) {
|
|
35
|
+
return cell !== null && cell !== "";
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Turn one column into bars: a histogram if the values are numbers, the
|
|
39
|
+
* most frequent labels otherwise. Twelve bars is the ceiling — more than
|
|
40
|
+
* that is a table again.
|
|
41
|
+
*/
|
|
42
|
+
export function columnSeries(frame, columnId) {
|
|
43
|
+
const columnIndex = frame.columns.findIndex((column) => column.id === columnId);
|
|
44
|
+
const column = frame.columns[columnIndex];
|
|
45
|
+
if (column === undefined || columnIndex < 0)
|
|
46
|
+
return [];
|
|
47
|
+
const cells = [];
|
|
48
|
+
for (let row = 0; row < frame.rows; row += 1) {
|
|
49
|
+
cells.push(frame.value(row, column, columnIndex));
|
|
50
|
+
}
|
|
51
|
+
const numbers = cells.map(asNumber);
|
|
52
|
+
const numeric = numbers.filter((n) => n !== null);
|
|
53
|
+
const filled = cells.filter(present).length;
|
|
54
|
+
if (filled === 0)
|
|
55
|
+
return [];
|
|
56
|
+
if (column.numeric === true || numeric.length / filled >= 0.8) {
|
|
57
|
+
return histogram(numeric);
|
|
58
|
+
}
|
|
59
|
+
return categories(cells);
|
|
60
|
+
}
|
|
61
|
+
function histogram(values) {
|
|
62
|
+
if (values.length === 0)
|
|
63
|
+
return [];
|
|
64
|
+
const min = Math.min(...values);
|
|
65
|
+
const max = Math.max(...values);
|
|
66
|
+
if (min === max)
|
|
67
|
+
return [{ label: String(min), count: values.length }];
|
|
68
|
+
const width = (max - min) / BINS;
|
|
69
|
+
const counts = Array.from({ length: BINS }, () => 0);
|
|
70
|
+
for (const value of values) {
|
|
71
|
+
const slot = Math.min(BINS - 1, Math.floor((value - min) / width));
|
|
72
|
+
counts[slot] = (counts[slot] ?? 0) + 1;
|
|
73
|
+
}
|
|
74
|
+
return counts.map((count, index) => {
|
|
75
|
+
const from = min + index * width;
|
|
76
|
+
const to = index === BINS - 1 ? max : from + width;
|
|
77
|
+
return { label: `${formatTick(from)}–${formatTick(to)}`, count };
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
function categories(cells) {
|
|
81
|
+
const tally = new Map();
|
|
82
|
+
for (const cell of cells) {
|
|
83
|
+
if (!present(cell))
|
|
84
|
+
continue;
|
|
85
|
+
const label = String(cell);
|
|
86
|
+
tally.set(label, (tally.get(label) ?? 0) + 1);
|
|
87
|
+
}
|
|
88
|
+
return [...tally.entries()]
|
|
89
|
+
.sort((a, b) => b[1] - a[1])
|
|
90
|
+
.slice(0, TOP)
|
|
91
|
+
.map(([label, count]) => ({
|
|
92
|
+
label: label === "" ? currentCopy().cell.emptyBin : label,
|
|
93
|
+
count,
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
function formatTick(n) {
|
|
97
|
+
const abs = Math.abs(n);
|
|
98
|
+
if (abs >= 100 || Number.isInteger(n))
|
|
99
|
+
return String(Math.round(n));
|
|
100
|
+
return n.toLocaleString(currentCopyLocale(), { maximumFractionDigits: 1 });
|
|
101
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from "./assist/index.js";
|
|
2
|
+
export * from "./chatter/index.js";
|
|
3
|
+
export * from "./form/index.js";
|
|
4
|
+
export * from "./frame/index.js";
|
|
5
|
+
export * from "./lines/index.js";
|
|
6
|
+
export * from "./listing/index.js";
|
|
7
|
+
export * from "./navigation/index.js";
|
|
8
|
+
export * from "./record/index.js";
|
|
9
|
+
export * from "./search/index.js";
|
|
10
|
+
export * from "./shell/index.js";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* COMPONENTS — primitives composed to answer ONE question of the product, by family. */
|
|
2
|
+
export * from "./assist/index.js";
|
|
3
|
+
export * from "./chatter/index.js";
|
|
4
|
+
export * from "./form/index.js";
|
|
5
|
+
export * from "./frame/index.js";
|
|
6
|
+
export * from "./lines/index.js";
|
|
7
|
+
export * from "./listing/index.js";
|
|
8
|
+
export * from "./navigation/index.js";
|
|
9
|
+
export * from "./record/index.js";
|
|
10
|
+
export * from "./search/index.js";
|
|
11
|
+
export * from "./shell/index.js";
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { type DialogContext } from "../../primitives/dialog/Dialog.js";
|
|
2
|
+
import { type FormMode } from "../../primitives/field/mode.js";
|
|
3
|
+
import type { DataColumn } from "../../primitives/table/DataTable.js";
|
|
4
|
+
import { type EditColumn, type EditLayout } from "../../primitives/table/EditableTable.js";
|
|
5
|
+
import { type TotalsRow } from "../../primitives/value/Totals.js";
|
|
6
|
+
import { type ReactNode } from "react";
|
|
7
|
+
/**
|
|
8
|
+
* A column of the lines. The same contract as `DataTable`'s columns —`value` is
|
|
9
|
+
* the raw value (what the search reads), `cell` how it is drawn, `text` how it
|
|
10
|
+
* reads as plain text— plus `edit`, the control that replaces the cell when the
|
|
11
|
+
* row is being edited in place.
|
|
12
|
+
*/
|
|
13
|
+
export type LineItemColumn<T> = Pick<DataColumn<T>, "id" | "header" | "value" | "cell" | "text" | "numeric" | "width"> & Pick<EditColumn<T>, "stack" | "brief"> & {
|
|
14
|
+
/** The control while the row is edited in place. Without it the cell stays read
|
|
15
|
+
* only — a computed amount, a code. Only simple values belong here; the whole
|
|
16
|
+
* line is edited in the `editor`. */
|
|
17
|
+
edit?: (line: T, set: (next: T) => void) => ReactNode;
|
|
18
|
+
};
|
|
19
|
+
/** What the popup editor receives besides the draft. */
|
|
20
|
+
export type LineEditorState = {
|
|
21
|
+
/** «Listo» was pressed and the draft was not valid: show the errors now, not before. */
|
|
22
|
+
tried: boolean;
|
|
23
|
+
/** A line that does not exist yet. */
|
|
24
|
+
isNew: boolean;
|
|
25
|
+
};
|
|
26
|
+
export type LineItemsProps<T> = {
|
|
27
|
+
lines: readonly T[];
|
|
28
|
+
columns: readonly LineItemColumn<T>[];
|
|
29
|
+
getLineId: (line: T) => string;
|
|
30
|
+
/** How a line is named — in the removal notice, the editor's header and for the
|
|
31
|
+
* screen reader next to each row's buttons. Defaults to the first column's text. */
|
|
32
|
+
lineLabel?: (line: T) => string;
|
|
33
|
+
/** `read` for a document that can no longer change (a posted invoice): no add, no
|
|
34
|
+
* edit, no remove — just the lines. By default it inherits the form's mode. */
|
|
35
|
+
mode?: FormMode;
|
|
36
|
+
/** A line changed, in place or in the editor. Without it nothing is editable. */
|
|
37
|
+
onChange?: (id: string, next: T) => void;
|
|
38
|
+
/** A new line, already filled in the editor (or blank, without an editor). */
|
|
39
|
+
onAdd?: (line: T) => void;
|
|
40
|
+
/** The blank line «Agregar una línea» starts from. Required for adding. */
|
|
41
|
+
newLine?: () => T;
|
|
42
|
+
/** A line was removed. It is gone at once; the notice offers to undo it. */
|
|
43
|
+
onRemove?: (id: string) => void;
|
|
44
|
+
/** Put a removed line back where it was. Without it the notice has no «Deshacer». */
|
|
45
|
+
onRestore?: (line: T, index: number) => void;
|
|
46
|
+
/** The whole line as a form: the product, the quantity, the price, the discount.
|
|
47
|
+
* Written with the system's `Field` controls; `set` replaces the draft. */
|
|
48
|
+
editor?: (draft: T, set: (next: T) => void, state: LineEditorState) => ReactNode;
|
|
49
|
+
/** Whether the draft can be saved. When it cannot, «Listo» keeps the editor open
|
|
50
|
+
* and `state.tried` turns on so the editor can say what is missing. */
|
|
51
|
+
isValid?: (draft: T) => boolean;
|
|
52
|
+
/** What the editor is about, in its header: «factura · FAC/2026/0118». */
|
|
53
|
+
context?: DialogContext;
|
|
54
|
+
/** What the search reads. Defaults to every column's `text` or `value`. */
|
|
55
|
+
searchText?: (line: T) => string;
|
|
56
|
+
/** The search shows up only with MORE lines than this. Four lines are read, not
|
|
57
|
+
* searched; a search box over them is a control that answers nothing. */
|
|
58
|
+
searchFrom?: number;
|
|
59
|
+
/** The money summary under the lines, on the right: the system's `Totals`. */
|
|
60
|
+
totals?: readonly TotalsRow[];
|
|
61
|
+
/** `auto` (the default) draws the lines as stacked cards below
|
|
62
|
+
* `--container-handheld`; `table` and `stacked` force one. */
|
|
63
|
+
layout?: EditLayout;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* The lines of a document inside its record: a count and a search above, the lines
|
|
67
|
+
* (edited in place or in a popup editor), and the totals under them on the right.
|
|
68
|
+
*
|
|
69
|
+
* Context: the record used to compose this by hand — a `Table`, its rows, a
|
|
70
|
+
* `Totals` — and every module did it a little differently: where «add» went, whether
|
|
71
|
+
* removing asked first, what an empty document said. One component, built on
|
|
72
|
+
* `EditableTable` and `Dialog`, makes the one2many the same block everywhere.
|
|
73
|
+
*/
|
|
74
|
+
export declare function LineItems<T>({ lines, columns, getLineId, lineLabel, mode, onChange, onAdd, newLine, onRemove, onRestore, editor, isValid, context, searchText, searchFrom, totals, layout, }: LineItemsProps<T>): import("react").JSX.Element;
|