@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,159 @@
|
|
|
1
|
+
/** Las instancias de agrupado, en orden. Van SIEMPRE a la izquierda y no se
|
|
2
|
+
* cruzan con nada: el orden ES la jerarquía. */
|
|
3
|
+
export function groupsOf(all) {
|
|
4
|
+
return all.filter((one) => one.kind === "group");
|
|
5
|
+
}
|
|
6
|
+
/** Las de filtro, que son las que se anidan. */
|
|
7
|
+
export function filtersOf(all) {
|
|
8
|
+
return all.filter((one) => one.kind !== "group");
|
|
9
|
+
}
|
|
10
|
+
/** La instancia que hay en esa ruta, si hay alguna. */
|
|
11
|
+
export function at(all, path) {
|
|
12
|
+
const [head, ...rest] = path;
|
|
13
|
+
if (head === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
const here = all[head];
|
|
16
|
+
if (here === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
if (rest.length === 0)
|
|
19
|
+
return here;
|
|
20
|
+
return here.kind === "join" ? at(here.of, rest) : undefined;
|
|
21
|
+
}
|
|
22
|
+
/** El mismo árbol con una instancia cambiada. Devolver uno nuevo y no tocar el
|
|
23
|
+
* viejo es lo que deja comparar antes y después —y deshacer. */
|
|
24
|
+
export function replaceAt(all, path, next) {
|
|
25
|
+
const [head, ...rest] = path;
|
|
26
|
+
if (head === undefined)
|
|
27
|
+
return [...all];
|
|
28
|
+
return all.map((one, index) => {
|
|
29
|
+
if (index !== head)
|
|
30
|
+
return one;
|
|
31
|
+
if (rest.length === 0)
|
|
32
|
+
return next;
|
|
33
|
+
return one.kind === "join" ? { ...one, of: replaceAt(one.of, rest, next) } : one;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* El mismo árbol sin esa instancia.
|
|
38
|
+
*
|
|
39
|
+
* Un nodo que se queda con un solo hijo se DESARMA: «cualquiera de: (a)» es un
|
|
40
|
+
* paréntesis alrededor de nada, y dejarlo obliga a leer un grupo que no agrupa.
|
|
41
|
+
*/
|
|
42
|
+
export function removeAt(all, path) {
|
|
43
|
+
const [head, ...rest] = path;
|
|
44
|
+
if (head === undefined)
|
|
45
|
+
return [...all];
|
|
46
|
+
if (rest.length === 0)
|
|
47
|
+
return all.filter((_, index) => index !== head);
|
|
48
|
+
return all.flatMap((one, index) => {
|
|
49
|
+
if (index !== head || one.kind !== "join")
|
|
50
|
+
return [one];
|
|
51
|
+
const inside = removeAt(one.of, rest);
|
|
52
|
+
if (inside.length === 0)
|
|
53
|
+
return [];
|
|
54
|
+
if (inside.length === 1 && one.join !== "not")
|
|
55
|
+
return inside;
|
|
56
|
+
return [{ ...one, of: inside }];
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/** Cómo se juntan los de un nodo: `all` → `any` → `not` y vuelta a empezar. Es
|
|
60
|
+
* la misma rueda que el selector muestra abierta, para quien prefiera el
|
|
61
|
+
* atajo. */
|
|
62
|
+
export function nextJoin(join) {
|
|
63
|
+
return join === "all" ? "any" : join === "any" ? "not" : "all";
|
|
64
|
+
}
|
|
65
|
+
/** Un id que no choca con otro. Corto, porque se lee en el DOM. */
|
|
66
|
+
export function criteriaId(prefix) {
|
|
67
|
+
return `${prefix}-${Math.random().toString(36).slice(2, 9)}`;
|
|
68
|
+
}
|
|
69
|
+
/** Una instancia de agrupado, a partir de un nivel ya legible. */
|
|
70
|
+
export function groupCriteria(level, label) {
|
|
71
|
+
return { id: criteriaId("g"), kind: "group", label, level };
|
|
72
|
+
}
|
|
73
|
+
/** Una instancia de filtro. `label` es lo único que distingue a uno declarado en
|
|
74
|
+
* código de uno escrito en la barra; sin `where`, es uno que sólo contesta
|
|
75
|
+
* quien lo declaró. */
|
|
76
|
+
export function filterCriteria(where, label, id) {
|
|
77
|
+
return {
|
|
78
|
+
id: id ?? criteriaId("f"),
|
|
79
|
+
kind: "filter",
|
|
80
|
+
label,
|
|
81
|
+
...(where === undefined ? {} : { where }),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Lo que VIAJA: el `where` del motor, armado con las instancias de filtro.
|
|
86
|
+
*
|
|
87
|
+
* Es lo único que sale de acá hacia afuera, y sale sin rótulos, sin ids y sin
|
|
88
|
+
* clases: la UI se queda con lo que la UI necesita.
|
|
89
|
+
*/
|
|
90
|
+
export function whereOf(all) {
|
|
91
|
+
const parts = filtersOf(all)
|
|
92
|
+
.map(expressionOf)
|
|
93
|
+
.filter((one) => one !== undefined);
|
|
94
|
+
if (parts.length === 0)
|
|
95
|
+
return undefined;
|
|
96
|
+
if (parts.length === 1)
|
|
97
|
+
return parts[0];
|
|
98
|
+
return { all: parts };
|
|
99
|
+
}
|
|
100
|
+
function expressionOf(one) {
|
|
101
|
+
if (one.kind === "group")
|
|
102
|
+
return undefined;
|
|
103
|
+
/* Una instancia sin `where` no viaja: no hay manera de preguntarla, y mandar
|
|
104
|
+
algo parecido sería contestar otra cosa. La contesta quien la declaró. */
|
|
105
|
+
if (one.kind === "filter")
|
|
106
|
+
return one.where;
|
|
107
|
+
const inside = one.of
|
|
108
|
+
.map(expressionOf)
|
|
109
|
+
.filter((each) => each !== undefined);
|
|
110
|
+
if (inside.length === 0)
|
|
111
|
+
return undefined;
|
|
112
|
+
if (one.join === "not") {
|
|
113
|
+
const only = inside.length === 1 ? inside[0] : { any: inside };
|
|
114
|
+
return { negate: only };
|
|
115
|
+
}
|
|
116
|
+
if (inside.length === 1)
|
|
117
|
+
return inside[0];
|
|
118
|
+
return one.join === "all" ? { all: inside } : { any: inside };
|
|
119
|
+
}
|
|
120
|
+
/** Los niveles que viajan, en orden. */
|
|
121
|
+
export function levelsOf(all) {
|
|
122
|
+
return groupsOf(all).flatMap((one) => (one.kind === "group" ? [one.level] : []));
|
|
123
|
+
}
|
|
124
|
+
/* ---------------------------------------------------------------- *
|
|
125
|
+
* Las dos operaciones que todo consumidor necesita
|
|
126
|
+
* ---------------------------------------------------------------- */
|
|
127
|
+
/**
|
|
128
|
+
* Los niveles de agrupado, puestos al día.
|
|
129
|
+
*
|
|
130
|
+
* Reemplaza TODAS las instancias de grupo por las que salen de `levels`, y no
|
|
131
|
+
* toca ninguna de filtro. Existe porque el control de niveles es una lista
|
|
132
|
+
* ordenada que se edita entera —se agrega, se saca, se mueve— y reconciliarla
|
|
133
|
+
* instancia por instancia desde afuera es el mismo bucle escrito en cada
|
|
134
|
+
* pantalla.
|
|
135
|
+
*
|
|
136
|
+
* Los de grupo quedan ADELANTE, que es donde se dibujan.
|
|
137
|
+
*/
|
|
138
|
+
export function withLevels(all, levels, labelOf) {
|
|
139
|
+
return [...levels.map((level) => groupCriteria(level, labelOf(level))), ...filtersOf(all)];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Un filtro CON NOMBRE, puesto o sacado.
|
|
143
|
+
*
|
|
144
|
+
* Es lo único que hace un filtro declarado en código: traer su rótulo y su
|
|
145
|
+
* condición ya pensadas. Tildarlo lo agrega, destildarlo lo saca, y se reconoce
|
|
146
|
+
* por su `id` — el mismo que declaró quien lo escribió, así que sobrevive a
|
|
147
|
+
* guardarlo como favorito y volver a leerlo.
|
|
148
|
+
*/
|
|
149
|
+
export function toggleNamed(all, named) {
|
|
150
|
+
const at = all.findIndex((one) => one.id === named.id);
|
|
151
|
+
if (at >= 0)
|
|
152
|
+
return all.filter((_, index) => index !== at);
|
|
153
|
+
return [...all, filterCriteria(named.where, named.label, named.id)];
|
|
154
|
+
}
|
|
155
|
+
/** Si ese filtro con nombre está puesto. Lo usa el panel para dibujar la marca:
|
|
156
|
+
* la marca sale de lo PUESTO y no de un estado aparte que podría discrepar. */
|
|
157
|
+
export function hasNamed(all, id) {
|
|
158
|
+
return all.some((one) => one.id === id);
|
|
159
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EL FILTRO DE FECHA, como lo hace Odoo: meses, trimestres y años que se
|
|
3
|
+
* combinan.
|
|
4
|
+
*
|
|
5
|
+
* Fecha de orden
|
|
6
|
+
* ─ septiembre ← los tres meses más recientes, por nombre
|
|
7
|
+
* ─ agosto
|
|
8
|
+
* ─ julio
|
|
9
|
+
* ────────────
|
|
10
|
+
* ─ Q1 Q2 Q3 Q4 ← los cuatro trimestres
|
|
11
|
+
* ────────────
|
|
12
|
+
* ─ 2026 2025 2024 ← los tres años más recientes
|
|
13
|
+
*
|
|
14
|
+
* ── Por qué se combinan y no se eligen ──────────────────────────────
|
|
15
|
+
*
|
|
16
|
+
* Porque así se contestan las preguntas que la gente tiene. «Septiembre» y
|
|
17
|
+
* «agosto» juntos son los dos meses: opciones del MISMO renglón se suman con O.
|
|
18
|
+
* Pero «septiembre» y «2025» no es «septiembre o 2025», es **septiembre de
|
|
19
|
+
* 2025**: renglones distintos se cruzan con Y. Un selector de un solo período no
|
|
20
|
+
* puede decir ninguna de las dos.
|
|
21
|
+
*
|
|
22
|
+
* Sin ningún año tildado, los meses y trimestres son los del año en curso — que
|
|
23
|
+
* es lo que alguien quiere decir cuando tilda «septiembre» y nada más.
|
|
24
|
+
*
|
|
25
|
+
* ── Lo que se guarda es la INTENCIÓN ────────────────────────────────
|
|
26
|
+
*
|
|
27
|
+
* «Los últimos tres meses» guardado en septiembre tiene que contestar otra cosa
|
|
28
|
+
* en diciembre. Por eso lo que viaja es `month:9`, no `2026-09-01`, y el
|
|
29
|
+
* intervalo se calcula cada vez que se lee. Un favorito con fechas absolutas
|
|
30
|
+
* miente desde el día siguiente.
|
|
31
|
+
*/
|
|
32
|
+
/** Un período tildado. El año es aparte y por eso no está acá: es el otro eje. */
|
|
33
|
+
export type DatePeriod = {
|
|
34
|
+
kind: "month";
|
|
35
|
+
month: number;
|
|
36
|
+
} | {
|
|
37
|
+
kind: "quarter";
|
|
38
|
+
quarter: 1 | 2 | 3 | 4;
|
|
39
|
+
} | {
|
|
40
|
+
kind: "year";
|
|
41
|
+
year: number;
|
|
42
|
+
};
|
|
43
|
+
/** Un intervalo SEMIABIERTO `[from, to)`. El final queda AFUERA, y no es un
|
|
44
|
+
* detalle: con un `between` inclusivo, una fila guardada a las 00:00:00 del
|
|
45
|
+
* primero del mes siguiente cae en dos períodos contiguos y se cuenta dos
|
|
46
|
+
* veces. */
|
|
47
|
+
export type DateRange = {
|
|
48
|
+
from: Date;
|
|
49
|
+
to: Date;
|
|
50
|
+
};
|
|
51
|
+
export declare function periodId(period: DatePeriod): string;
|
|
52
|
+
export declare function readPeriodId(id: string): DatePeriod | null;
|
|
53
|
+
/** Los tres meses más recientes, del actual hacia atrás. Cada uno con el año al
|
|
54
|
+
* que pertenece, porque en enero los tres anteriores son de dos años. */
|
|
55
|
+
export declare function recentMonths(now?: Date, howMany?: number): {
|
|
56
|
+
month: number;
|
|
57
|
+
year: number;
|
|
58
|
+
}[];
|
|
59
|
+
/** Los años más recientes, del actual hacia atrás. */
|
|
60
|
+
export declare function recentYears(now?: Date, howMany?: number): number[];
|
|
61
|
+
/**
|
|
62
|
+
* Las opciones que se ofrecen, en tres renglones y en este orden: meses,
|
|
63
|
+
* trimestres, años. Es el orden de Odoo y el de la pregunta — de lo fino a lo
|
|
64
|
+
* grueso, porque «este mes» se pide mucho más seguido que «2024».
|
|
65
|
+
*/
|
|
66
|
+
export declare function datePeriodOptions(now?: Date,
|
|
67
|
+
/** Cómo se llama un mes. Sin esto, como lo llama el calendario del sistema en
|
|
68
|
+
* el idioma activo — una lista de doce meses escrita a mano es una lista que
|
|
69
|
+
* hay que volver a escribir para el idioma siguiente. */
|
|
70
|
+
monthName?: (month: number) => string): {
|
|
71
|
+
months: {
|
|
72
|
+
id: string;
|
|
73
|
+
label: string;
|
|
74
|
+
}[];
|
|
75
|
+
quarters: {
|
|
76
|
+
id: string;
|
|
77
|
+
label: string;
|
|
78
|
+
}[];
|
|
79
|
+
years: {
|
|
80
|
+
id: string;
|
|
81
|
+
label: string;
|
|
82
|
+
}[];
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Los intervalos que un juego de períodos tildados representa.
|
|
86
|
+
*
|
|
87
|
+
* Devuelve VARIOS y no uno: «septiembre y julio» son dos tramos con un agujero
|
|
88
|
+
* en el medio, y aplastarlos en un solo `[julio, octubre)` metería agosto, que
|
|
89
|
+
* nadie pidió. Quien los use los cruza con O.
|
|
90
|
+
*
|
|
91
|
+
* La regla es la de Odoo: los años multiplican, los meses y trimestres recortan.
|
|
92
|
+
* Sin años tildados se usa el año en curso; sin meses ni trimestres, el año
|
|
93
|
+
* entero.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resolvePeriods(periods: readonly DatePeriod[], now?: Date): DateRange[];
|
|
96
|
+
/**
|
|
97
|
+
* El recorte de una fecha como GRUPO del panel de filtros: plegable, con su
|
|
98
|
+
* nombre, y los tres renglones separados por una línea.
|
|
99
|
+
*
|
|
100
|
+
* Es una función y no un componente porque los filtros del panel son datos —el
|
|
101
|
+
* panel dibuja, el dominio declara—, y así una fecha no necesita una ranura
|
|
102
|
+
* aparte ni un camino propio adentro de `SearchBar`.
|
|
103
|
+
*/
|
|
104
|
+
export declare function datePeriodGroup(options: {
|
|
105
|
+
/** Cómo se llama el campo: «Fecha de pedido». */
|
|
106
|
+
label: string;
|
|
107
|
+
/** Los ids tildados, de `periodId`. */
|
|
108
|
+
active: readonly string[];
|
|
109
|
+
now?: Date;
|
|
110
|
+
/** Cómo se llama un mes, cuando el producto lo dice con otras palabras. Sin
|
|
111
|
+
* esto, como lo llama el calendario del sistema en el idioma activo. */
|
|
112
|
+
monthName?: (month: number) => string;
|
|
113
|
+
}): {
|
|
114
|
+
label: string;
|
|
115
|
+
collapsible: true;
|
|
116
|
+
options: {
|
|
117
|
+
id: string;
|
|
118
|
+
label: string;
|
|
119
|
+
checked: boolean;
|
|
120
|
+
divider?: boolean;
|
|
121
|
+
}[];
|
|
122
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { monthName as calendarMonthName } from "../../foundations/calendar.js";
|
|
2
|
+
import { currentCopy, fillCopy } from "../../foundations/copy.js";
|
|
3
|
+
export function periodId(period) {
|
|
4
|
+
if (period.kind === "month")
|
|
5
|
+
return `month:${period.month}`;
|
|
6
|
+
if (period.kind === "quarter")
|
|
7
|
+
return `quarter:${period.quarter}`;
|
|
8
|
+
return `year:${period.year}`;
|
|
9
|
+
}
|
|
10
|
+
export function readPeriodId(id) {
|
|
11
|
+
const [kind, raw] = id.split(":");
|
|
12
|
+
const value = Number(raw);
|
|
13
|
+
if (!Number.isInteger(value))
|
|
14
|
+
return null;
|
|
15
|
+
if (kind === "month")
|
|
16
|
+
return value >= 1 && value <= 12 ? { kind: "month", month: value } : null;
|
|
17
|
+
if (kind === "quarter") {
|
|
18
|
+
return value >= 1 && value <= 4
|
|
19
|
+
? { kind: "quarter", quarter: value }
|
|
20
|
+
: null;
|
|
21
|
+
}
|
|
22
|
+
if (kind === "year")
|
|
23
|
+
return { kind: "year", year: value };
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
/** Los tres meses más recientes, del actual hacia atrás. Cada uno con el año al
|
|
27
|
+
* que pertenece, porque en enero los tres anteriores son de dos años. */
|
|
28
|
+
export function recentMonths(now = new Date(), howMany = 3) {
|
|
29
|
+
const out = [];
|
|
30
|
+
for (let back = 0; back < howMany; back += 1) {
|
|
31
|
+
const when = new Date(now.getFullYear(), now.getMonth() - back, 1);
|
|
32
|
+
out.push({ month: when.getMonth() + 1, year: when.getFullYear() });
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
/** Los años más recientes, del actual hacia atrás. */
|
|
37
|
+
export function recentYears(now = new Date(), howMany = 3) {
|
|
38
|
+
return Array.from({ length: howMany }, (_, back) => now.getFullYear() - back);
|
|
39
|
+
}
|
|
40
|
+
const QUARTERS = [1, 2, 3, 4];
|
|
41
|
+
/**
|
|
42
|
+
* Las opciones que se ofrecen, en tres renglones y en este orden: meses,
|
|
43
|
+
* trimestres, años. Es el orden de Odoo y el de la pregunta — de lo fino a lo
|
|
44
|
+
* grueso, porque «este mes» se pide mucho más seguido que «2024».
|
|
45
|
+
*/
|
|
46
|
+
export function datePeriodOptions(now = new Date(),
|
|
47
|
+
/** Cómo se llama un mes. Sin esto, como lo llama el calendario del sistema en
|
|
48
|
+
* el idioma activo — una lista de doce meses escrita a mano es una lista que
|
|
49
|
+
* hay que volver a escribir para el idioma siguiente. */
|
|
50
|
+
monthName = calendarMonthName) {
|
|
51
|
+
return {
|
|
52
|
+
months: recentMonths(now).map(({ month }) => ({
|
|
53
|
+
id: periodId({ kind: "month", month }),
|
|
54
|
+
label: monthName(month),
|
|
55
|
+
})),
|
|
56
|
+
quarters: QUARTERS.map((quarter) => ({
|
|
57
|
+
id: periodId({ kind: "quarter", quarter }),
|
|
58
|
+
label: fillCopy(currentCopy().calendar.quarter, { n: quarter }),
|
|
59
|
+
})),
|
|
60
|
+
years: recentYears(now).map((year) => ({
|
|
61
|
+
id: periodId({ kind: "year", year }),
|
|
62
|
+
label: String(year),
|
|
63
|
+
})),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/** Un mes de un año, como intervalo semiabierto. */
|
|
67
|
+
function monthRange(year, month) {
|
|
68
|
+
return { from: new Date(year, month - 1, 1), to: new Date(year, month, 1) };
|
|
69
|
+
}
|
|
70
|
+
function quarterRange(year, quarter) {
|
|
71
|
+
const first = (quarter - 1) * 3;
|
|
72
|
+
return { from: new Date(year, first, 1), to: new Date(year, first + 3, 1) };
|
|
73
|
+
}
|
|
74
|
+
function yearRange(year) {
|
|
75
|
+
return { from: new Date(year, 0, 1), to: new Date(year + 1, 0, 1) };
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Los intervalos que un juego de períodos tildados representa.
|
|
79
|
+
*
|
|
80
|
+
* Devuelve VARIOS y no uno: «septiembre y julio» son dos tramos con un agujero
|
|
81
|
+
* en el medio, y aplastarlos en un solo `[julio, octubre)` metería agosto, que
|
|
82
|
+
* nadie pidió. Quien los use los cruza con O.
|
|
83
|
+
*
|
|
84
|
+
* La regla es la de Odoo: los años multiplican, los meses y trimestres recortan.
|
|
85
|
+
* Sin años tildados se usa el año en curso; sin meses ni trimestres, el año
|
|
86
|
+
* entero.
|
|
87
|
+
*/
|
|
88
|
+
export function resolvePeriods(periods, now = new Date()) {
|
|
89
|
+
if (periods.length === 0)
|
|
90
|
+
return [];
|
|
91
|
+
const years = periods.filter((one) => one.kind === "year").map((one) => one.year);
|
|
92
|
+
const months = periods.filter((one) => one.kind === "month").map((one) => one.month);
|
|
93
|
+
const quarters = periods.filter((one) => one.kind === "quarter").map((one) => one.quarter);
|
|
94
|
+
/* Sin año tildado, el año en curso. Es lo que alguien quiere decir cuando
|
|
95
|
+
tilda «septiembre» y nada más — y no «todos los septiembres de la
|
|
96
|
+
historia», que además nadie sabría escribir. */
|
|
97
|
+
const overYears = years.length > 0 ? years : [now.getFullYear()];
|
|
98
|
+
if (months.length === 0 && quarters.length === 0) {
|
|
99
|
+
return overYears.map(yearRange);
|
|
100
|
+
}
|
|
101
|
+
return overYears.flatMap((year) => [
|
|
102
|
+
...months.map((month) => monthRange(year, month)),
|
|
103
|
+
...quarters.map((quarter) => quarterRange(year, quarter)),
|
|
104
|
+
]);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* El recorte de una fecha como GRUPO del panel de filtros: plegable, con su
|
|
108
|
+
* nombre, y los tres renglones separados por una línea.
|
|
109
|
+
*
|
|
110
|
+
* Es una función y no un componente porque los filtros del panel son datos —el
|
|
111
|
+
* panel dibuja, el dominio declara—, y así una fecha no necesita una ranura
|
|
112
|
+
* aparte ni un camino propio adentro de `SearchBar`.
|
|
113
|
+
*/
|
|
114
|
+
export function datePeriodGroup(options) {
|
|
115
|
+
/* Se REENVÍA lo que llegó, sin default propio: con `?? String` acá, el panel
|
|
116
|
+
de filtros dibujaba «9 · 8 · 7» en vez de «Septiembre · Agosto · Julio»
|
|
117
|
+
—este `String` pisaba el default del calendario— y nadie veía por qué. Un
|
|
118
|
+
default repetido en dos lugares es un default que dice dos cosas. */
|
|
119
|
+
const { months, quarters, years } = datePeriodOptions(options.now ?? new Date(), options.monthName);
|
|
120
|
+
const mark = (one, divider) => ({
|
|
121
|
+
...one,
|
|
122
|
+
checked: options.active.includes(one.id),
|
|
123
|
+
...(divider === true ? { divider: true } : {}),
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
label: options.label,
|
|
127
|
+
collapsible: true,
|
|
128
|
+
options: [
|
|
129
|
+
...months.map((one) => mark(one)),
|
|
130
|
+
...quarters.map((one, index) => mark(one, index === 0)),
|
|
131
|
+
...years.map((one, index) => mark(one, index === 0)),
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { GroupGrain } from "../../foundations/grouping.js";
|
|
2
|
+
/**
|
|
3
|
+
* LOS RECORTES DE FECHA, en RELATIVO.
|
|
4
|
+
*
|
|
5
|
+
* Context: un filtro de fecha escrito en absoluto —«desde el 2026-06-01»— es una
|
|
6
|
+
* respuesta que envejece. Alguien guarda «los últimos tres meses» como favorito
|
|
7
|
+
* en septiembre y en diciembre le sigue mostrando junio, julio y agosto: el
|
|
8
|
+
* filtro dice lo que dijo, no lo que quiso decir. Lo que hay que guardar es la
|
|
9
|
+
* INTENCIÓN —«los últimos 3 meses»— y resolverla cada vez que se lee.
|
|
10
|
+
*
|
|
11
|
+
* **Y por eso lo resuelve la UI.** No hace falta preguntarle a nadie cuándo
|
|
12
|
+
* empieza el trimestre pasado: es aritmética de calendario sobre el reloj del
|
|
13
|
+
* que mira. Mandar al servidor «relativo: últimos 3 meses» y que él decida
|
|
14
|
+
* agrega un viaje y una segunda definición de «mes» — y la del servidor está en
|
|
15
|
+
* su huso, no en el de la persona.
|
|
16
|
+
*
|
|
17
|
+
* Lo que sale de acá es un intervalo SEMIABIERTO `[desde, hasta)`. El final
|
|
18
|
+
* queda AFUERA, y no es un detalle: con un `between` inclusivo, una fila guardada
|
|
19
|
+
* a las 00:00:00 del primero del mes siguiente cae en los dos recortes, y dos
|
|
20
|
+
* filtros contiguos devuelven la misma fila.
|
|
21
|
+
*/
|
|
22
|
+
export type DateRange = {
|
|
23
|
+
/** Desde, inclusive. */
|
|
24
|
+
from: Date;
|
|
25
|
+
/** Hasta, EXCLUSIVE. */
|
|
26
|
+
to: Date;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Un recorte relativo, tal como se guarda: una unidad y cuántas.
|
|
30
|
+
*
|
|
31
|
+
* `last` cuenta hacia atrás desde ahora incluyendo el período en curso; `prior`
|
|
32
|
+
* cuenta los períodos COMPLETOS anteriores, sin el que está corriendo. Son
|
|
33
|
+
* distintos y la diferencia importa el día 2 del mes: «este mes» tiene un día y
|
|
34
|
+
* «el mes pasado» tiene treinta.
|
|
35
|
+
*/
|
|
36
|
+
export type RelativeRange = {
|
|
37
|
+
unit: GroupGrain | "quarter";
|
|
38
|
+
count: number;
|
|
39
|
+
mode: "last" | "prior";
|
|
40
|
+
};
|
|
41
|
+
/** El id con el que viaja un recorte relativo, para guardarlo y reconocerlo. */
|
|
42
|
+
export declare function rangeId(range: RelativeRange): string;
|
|
43
|
+
export declare function readRangeId(id: string): RelativeRange | null;
|
|
44
|
+
/**
|
|
45
|
+
* Resuelve un recorte relativo contra un reloj.
|
|
46
|
+
*
|
|
47
|
+
* `now` se pasa y no se lee de adentro: una función que mira el reloj no se
|
|
48
|
+
* puede probar, y la que la prueba tendría que congelar el tiempo global.
|
|
49
|
+
*/
|
|
50
|
+
export declare function resolveRange(range: RelativeRange, now?: Date): DateRange;
|
|
51
|
+
/** Los recortes que vale la pena ofrecer, en el orden en que se leen. Es una
|
|
52
|
+
* sugerencia con nombre y no una lista cerrada: un consumidor arma la suya con
|
|
53
|
+
* `rangeId` si su dominio mira el tiempo de otra manera. */
|
|
54
|
+
export declare const COMMON_RANGES: readonly RelativeRange[];
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** El id con el que viaja un recorte relativo, para guardarlo y reconocerlo. */
|
|
2
|
+
export function rangeId(range) {
|
|
3
|
+
return `${range.mode}:${range.count}:${range.unit}`;
|
|
4
|
+
}
|
|
5
|
+
export function readRangeId(id) {
|
|
6
|
+
const [mode, count, unit] = id.split(":");
|
|
7
|
+
if (mode !== "last" && mode !== "prior")
|
|
8
|
+
return null;
|
|
9
|
+
const howMany = Number(count);
|
|
10
|
+
if (!Number.isInteger(howMany) || howMany < 1)
|
|
11
|
+
return null;
|
|
12
|
+
if (unit !== "day" &&
|
|
13
|
+
unit !== "week" &&
|
|
14
|
+
unit !== "month" &&
|
|
15
|
+
unit !== "quarter" &&
|
|
16
|
+
unit !== "year") {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return { mode, count: howMany, unit };
|
|
20
|
+
}
|
|
21
|
+
/** El arranque del período que contiene a `when`. Es donde empieza «este mes»,
|
|
22
|
+
* «esta semana», «este trimestre». */
|
|
23
|
+
function startOf(unit, when) {
|
|
24
|
+
const year = when.getFullYear();
|
|
25
|
+
const month = when.getMonth();
|
|
26
|
+
if (unit === "year")
|
|
27
|
+
return new Date(year, 0, 1);
|
|
28
|
+
if (unit === "quarter")
|
|
29
|
+
return new Date(year, Math.floor(month / 3) * 3, 1);
|
|
30
|
+
if (unit === "month")
|
|
31
|
+
return new Date(year, month, 1);
|
|
32
|
+
if (unit === "week") {
|
|
33
|
+
/* La semana arranca el LUNES, que es ISO y es lo que el motor usa para
|
|
34
|
+
recortar por semana. Con domingo, dos capas partirían el mismo dato en
|
|
35
|
+
semanas distintas y los conteos no cerrarían. */
|
|
36
|
+
const day = new Date(year, month, when.getDate());
|
|
37
|
+
const weekday = (day.getDay() + 6) % 7;
|
|
38
|
+
day.setDate(day.getDate() - weekday);
|
|
39
|
+
return day;
|
|
40
|
+
}
|
|
41
|
+
return new Date(year, month, when.getDate());
|
|
42
|
+
}
|
|
43
|
+
/** Corre `count` períodos desde `from`. Negativo va para atrás. */
|
|
44
|
+
function shift(unit, from, count) {
|
|
45
|
+
const out = new Date(from);
|
|
46
|
+
if (unit === "year")
|
|
47
|
+
out.setFullYear(out.getFullYear() + count);
|
|
48
|
+
else if (unit === "quarter")
|
|
49
|
+
out.setMonth(out.getMonth() + count * 3);
|
|
50
|
+
else if (unit === "month")
|
|
51
|
+
out.setMonth(out.getMonth() + count);
|
|
52
|
+
else if (unit === "week")
|
|
53
|
+
out.setDate(out.getDate() + count * 7);
|
|
54
|
+
else
|
|
55
|
+
out.setDate(out.getDate() + count);
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resuelve un recorte relativo contra un reloj.
|
|
60
|
+
*
|
|
61
|
+
* `now` se pasa y no se lee de adentro: una función que mira el reloj no se
|
|
62
|
+
* puede probar, y la que la prueba tendría que congelar el tiempo global.
|
|
63
|
+
*/
|
|
64
|
+
export function resolveRange(range, now = new Date()) {
|
|
65
|
+
const current = startOf(range.unit, now);
|
|
66
|
+
if (range.mode === "last") {
|
|
67
|
+
/* «Los últimos 3 meses» INCLUYE el que corre: termina al final del actual.
|
|
68
|
+
Que termine «ahora» dejaría afuera lo que se guarde en el resto del día, y
|
|
69
|
+
un filtro que esconde lo que acabás de crear se lee como un error. */
|
|
70
|
+
return {
|
|
71
|
+
from: shift(range.unit, current, -(range.count - 1)),
|
|
72
|
+
to: shift(range.unit, current, 1),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return { from: shift(range.unit, current, -range.count), to: current };
|
|
76
|
+
}
|
|
77
|
+
/** Los recortes que vale la pena ofrecer, en el orden en que se leen. Es una
|
|
78
|
+
* sugerencia con nombre y no una lista cerrada: un consumidor arma la suya con
|
|
79
|
+
* `rangeId` si su dominio mira el tiempo de otra manera. */
|
|
80
|
+
export const COMMON_RANGES = [
|
|
81
|
+
{ mode: "last", count: 7, unit: "day" },
|
|
82
|
+
{ mode: "last", count: 30, unit: "day" },
|
|
83
|
+
{ mode: "last", count: 3, unit: "month" },
|
|
84
|
+
{ mode: "prior", count: 1, unit: "month" },
|
|
85
|
+
{ mode: "last", count: 1, unit: "quarter" },
|
|
86
|
+
{ mode: "prior", count: 1, unit: "quarter" },
|
|
87
|
+
{ mode: "last", count: 1, unit: "year" },
|
|
88
|
+
{ mode: "last", count: 3, unit: "year" },
|
|
89
|
+
];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { type BuiltCriteria, type BuiltLevel, type BuiltSort, CriteriaBuilder, } from "./CriteriaBuilder.js";
|
|
2
|
+
export { blankGroup, type BuiltCondition, type BuiltFilter, describeFilter, FilterBuilder, type TreeCondition, treeFromFilter, type TreeGroup, type TreeNode, treeToFilter, } from "./FilterBuilder.js";
|
|
3
|
+
export { formatJson, JsonEditor } from "./JsonEditor.js";
|
|
4
|
+
export { SearchBar, type SearchBarProps, type SearchFacet, } from "./SearchBar.js";
|
|
5
|
+
export { at, criteriaId, type CriteriaJoin, type CriteriaPath, filterCriteria, filtersOf, type GroupCriteria, groupCriteria, groupsOf, hasNamed, levelsOf, nextJoin, removeAt, replaceAt, type SearchCriteria, toggleNamed, whereOf, withLevels, } from "./criteria.js";
|
|
6
|
+
export { CriteriaChips, type CriteriaChipsProps, } from "./CriteriaChips.js";
|
|
7
|
+
export { type FieldDefinition, fieldLabel, filterableNames, isFilterable, type ModelDefinition, type ModelTranslations, } from "./model.js";
|
|
8
|
+
export { answers, defaultOp, describeCondition, globToLike, opLabel, type QueryDraft, type QueryField, queryFields, type QueryKind, type QuerySuggestion, readTyped, suggest, type TypedValue, valueFor, type ValueShape, withoutValue, } from "./query.js";
|
|
9
|
+
export { type DatePeriod, datePeriodGroup, datePeriodOptions, type DateRange, periodId, readPeriodId, recentMonths, recentYears, resolvePeriods, } from "./datePeriods.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* Buscar con facetas, con los recortes metidos adentro del campo. */
|
|
2
|
+
export { CriteriaBuilder, } from "./CriteriaBuilder.js";
|
|
3
|
+
export { blankGroup, describeFilter, FilterBuilder, treeFromFilter, treeToFilter, } from "./FilterBuilder.js";
|
|
4
|
+
export { formatJson, JsonEditor } from "./JsonEditor.js";
|
|
5
|
+
export { SearchBar, } from "./SearchBar.js";
|
|
6
|
+
/* LO QUE LA BARRA TIENE PUESTO: instancias. Una sola cosa para las dos clases
|
|
7
|
+
—grupo y filtro—, con sus metadatos de UI afuera y el objeto del motor
|
|
8
|
+
adentro, intacto. */
|
|
9
|
+
export { at, criteriaId, filterCriteria, filtersOf, groupCriteria, groupsOf, hasNamed, levelsOf, nextJoin, removeAt, replaceAt, toggleNamed, whereOf, withLevels, } from "./criteria.js";
|
|
10
|
+
export { CriteriaChips, } from "./CriteriaChips.js";
|
|
11
|
+
/* EL VOCABULARIO DEL MODELO — lo que el servidor publica sobre sus campos. Lo
|
|
12
|
+
hablan el constructor de filtros, el de criterias y la barra de búsqueda, así
|
|
13
|
+
que vive aparte de los tres. */
|
|
14
|
+
export { fieldLabel, filterableNames, isFilterable, } from "./model.js";
|
|
15
|
+
/* LA GRAMÁTICA DE LA BARRA: qué se puede preguntar sobre un modelo, y con qué.
|
|
16
|
+
Tres pasos —campo, operador, valor— que se pueden escribir en uno. */
|
|
17
|
+
export { answers, defaultOp, describeCondition, globToLike, opLabel, queryFields, readTyped, suggest, valueFor, withoutValue, } from "./query.js";
|
|
18
|
+
/* El recorte de fecha, como lo hace Odoo: meses, trimestres y años que se
|
|
19
|
+
combinan. Se guarda la INTENCIÓN y se resuelve al leer. */
|
|
20
|
+
export { datePeriodGroup, datePeriodOptions, periodId, readPeriodId, recentMonths, recentYears, resolvePeriods, } from "./datePeriods.js";
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/** Un campo, tal como lo describe el modelo. Espeja `FieldMeta` del servidor.
|
|
2
|
+
*
|
|
3
|
+
* `ops` vacío es la señal de que este campo no se filtra, ordena ni agrupa directo: la
|
|
4
|
+
* publican tanto un valor-objeto (`embedded`) como cualquiera de los tres relacionales
|
|
5
|
+
* (`many2one`/`one2many`/`many2many`) — ninguno tiene una comparación propia. Un relacional
|
|
6
|
+
* se filtra por la columna que lo identifica (`identifiedBy`), que aparece como SU PROPIO
|
|
7
|
+
* campo escalar en el modelo. */
|
|
8
|
+
export interface FieldDefinition {
|
|
9
|
+
type: string;
|
|
10
|
+
nullable: boolean;
|
|
11
|
+
sortable: boolean;
|
|
12
|
+
ops: readonly string[];
|
|
13
|
+
/** La respuesta gruesa a «qué es este nombre»: un valor en la fila, un valor-objeto adentro
|
|
14
|
+
* (JSON, sin identidad propia) u otro agregado que hay que traer. */
|
|
15
|
+
kind?: "scalar" | "embedded" | "relation";
|
|
16
|
+
/** Varios de ellos: un `one2many`, un `many2many`, o una lista de valores-objeto. */
|
|
17
|
+
many?: boolean;
|
|
18
|
+
/** Para un campo relacional, el agregado del otro lado, por nombre. */
|
|
19
|
+
relation?: string | null;
|
|
20
|
+
/** Para un campo relacional, la columna que lo identifica — nunca se ofrece el campo
|
|
21
|
+
* relacional en sí como blanco de un filtro, un orden o un nivel de agrupado. */
|
|
22
|
+
identifiedBy?: string | null;
|
|
23
|
+
/** Los valores que un campo de enum puede tomar — de ahí sale un `<select>`. */
|
|
24
|
+
choices?: readonly unknown[];
|
|
25
|
+
}
|
|
26
|
+
/** Si este campo se puede usar en un `where`, un `order` o un `grouping.by` directamente.
|
|
27
|
+
* `ops.length === 0` alcanza como señal: la publican tanto `embedded` como los tres
|
|
28
|
+
* relacionales, así que no hace falta mirar `kind` aparte. */
|
|
29
|
+
export declare function isFilterable(field: FieldDefinition): boolean;
|
|
30
|
+
/** La definición del modelo que manda el servidor bajo `model_meta_data`. */
|
|
31
|
+
/** Cada palabra que una pantalla necesita para el modelo o un campo suyo: siempre
|
|
32
|
+
* `{"default": …, …idiomas}`. Espeja `Translated` del servidor. */
|
|
33
|
+
export interface ModelTranslations {
|
|
34
|
+
name: Record<string, string>;
|
|
35
|
+
labels: Record<string, Record<string, string>>;
|
|
36
|
+
help?: Record<string, Record<string, string>>;
|
|
37
|
+
}
|
|
38
|
+
export interface ModelDefinition {
|
|
39
|
+
aggregate: string;
|
|
40
|
+
identity: string;
|
|
41
|
+
fields: Record<string, FieldDefinition>;
|
|
42
|
+
/** Ausente en un modelo armado a mano —el `having` sintético de `CriteriaBuilder`, por
|
|
43
|
+
* ejemplo—; presente en cualquier `model_meta_data` real. */
|
|
44
|
+
translations?: ModelTranslations;
|
|
45
|
+
}
|
|
46
|
+
/** El rótulo de un campo en el idioma pedido: el que el modelo publicó para ese idioma, o el
|
|
47
|
+
* que publicó como `default`, o el nombre del campo tal cual si el modelo no dijo nada —un
|
|
48
|
+
* modelo sintético, o uno que todavía no llegó. Nunca inventa una palabra: lo que no está en
|
|
49
|
+
* `translations` se ve tal cual sale de la tabla, y eso ya avisa que falta declararlo. */
|
|
50
|
+
export declare function fieldLabel(model: ModelDefinition, field: string, locale: string): string;
|
|
51
|
+
/** Los nombres del modelo que se pueden usar como blanco de un filtro, un orden o un nivel de
|
|
52
|
+
* agrupado — nunca un relacional ni un valor-objeto, filtrados por `isFilterable`. Ordenados,
|
|
53
|
+
* porque es la lista que arma cada `<select>` de este archivo. */
|
|
54
|
+
export declare function filterableNames(model: ModelDefinition): string[];
|