@mittwald/flow-react-components 1.5.0-next.48 → 1.5.0-next.49
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/AGENTS.md +37 -0
- package/dist/assets/component-index.json +1 -1
- package/dist/assets/doc-properties.json +1 -1
- package/dist/js/node_modules/.pnpm/@lezer_lr@1.4.10/node_modules/@lezer/lr/dist/index.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Action/models/ActionState.mjs +8 -70
- package/dist/js/packages/components/src/components/Action/models/ActionState.mjs.map +1 -1
- package/dist/js/packages/components/src/components/Calendar/components/RangeCalendar/helpers/useCalendarDateRangePresets.mjs +1 -1
- package/dist/js/packages/components/src/components/List/components/Header/components/ActiveFilters/ActiveFilters.mjs +1 -1
- package/dist/js/packages/components/src/components/List/components/Header/components/ActiveFilters/ActiveFilters.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/List.mjs +15 -4
- package/dist/js/packages/components/src/components/List/model/List.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/ListSettingsStore.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/ListViewMode.mjs +35 -23
- package/dist/js/packages/components/src/components/List/model/ListViewMode.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/ReactTable.mjs +16 -10
- package/dist/js/packages/components/src/components/List/model/ReactTable.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/filter/DateRangeFilter.mjs +25 -13
- package/dist/js/packages/components/src/components/List/model/filter/DateRangeFilter.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/filter/Filter.mjs +20 -181
- package/dist/js/packages/components/src/components/List/model/filter/Filter.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/item/ItemCollection.mjs +11 -7
- package/dist/js/packages/components/src/components/List/model/item/ItemCollection.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/loading/IncrementalLoader.mjs +10 -13
- package/dist/js/packages/components/src/components/List/model/loading/IncrementalLoader.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/loading/IncrementalLoaderState.mjs +13 -61
- package/dist/js/packages/components/src/components/List/model/loading/IncrementalLoaderState.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/search/Search.mjs +19 -47
- package/dist/js/packages/components/src/components/List/model/search/Search.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/sorting/Sorting.mjs +15 -54
- package/dist/js/packages/components/src/components/List/model/sorting/Sorting.mjs.map +1 -1
- package/dist/js/packages/components/src/components/List/model/sorting/SortingFunctions.mjs.map +1 -1
- package/dist/js/packages/components/src/lib/remote/eventHandlerContext.mjs +3 -1
- package/dist/js/packages/components/src/lib/remote/eventHandlerContext.mjs.map +1 -1
- package/dist/js/packages/components-base/src/action/ActionState.mjs +87 -0
- package/dist/js/packages/components-base/src/action/ActionState.mjs.map +1 -0
- package/dist/js/packages/components-base/src/index.mjs +18 -0
- package/dist/js/packages/components-base/src/lib/array.mjs +9 -0
- package/dist/js/packages/components-base/src/lib/array.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/ListModelContext.mjs +19 -0
- package/dist/js/packages/components-base/src/list/ListModelContext.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/filter/ListDateRangeFilter.mjs +29 -0
- package/dist/js/packages/components-base/src/list/filter/ListDateRangeFilter.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/filter/ListFilter.mjs +229 -0
- package/dist/js/packages/components-base/src/list/filter/ListFilter.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/filter/ListFilterValue.mjs +51 -0
- package/dist/js/packages/components-base/src/list/filter/ListFilterValue.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/filter/dateRange.mjs +51 -0
- package/dist/js/packages/components-base/src/list/filter/dateRange.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/item/ListItem.mjs +21 -0
- package/dist/js/packages/components-base/src/list/item/ListItem.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/item/ListItemCollection.mjs +24 -0
- package/dist/js/packages/components-base/src/list/item/ListItemCollection.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/loading/ListLoaderState.mjs +86 -0
- package/dist/js/packages/components-base/src/list/loading/ListLoaderState.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/loading/types.mjs +29 -0
- package/dist/js/packages/components-base/src/list/loading/types.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/pagination/ListBatchesController.mjs +64 -0
- package/dist/js/packages/components-base/src/list/pagination/ListBatchesController.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/search/ListSearch.mjs +62 -0
- package/dist/js/packages/components-base/src/list/search/ListSearch.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/sorting/ListSorting.mjs +81 -0
- package/dist/js/packages/components-base/src/list/sorting/ListSorting.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/table/ListTable.mjs +80 -0
- package/dist/js/packages/components-base/src/list/table/ListTable.mjs.map +1 -0
- package/dist/js/packages/components-base/src/list/viewMode/ListViewMode.mjs +53 -0
- package/dist/js/packages/components-base/src/list/viewMode/ListViewMode.mjs.map +1 -0
- package/dist/types/_bundled/flow-components-base/action/ActionState.d.ts +36 -0
- package/dist/types/_bundled/flow-components-base/index.d.ts +23 -0
- package/dist/types/_bundled/flow-components-base/lib/array.d.ts +4 -0
- package/dist/types/_bundled/flow-components-base/list/ListModelContext.d.ts +30 -0
- package/dist/types/_bundled/flow-components-base/list/filter/ListDateRangeFilter.d.ts +18 -0
- package/dist/types/_bundled/flow-components-base/list/filter/ListFilter.d.ts +104 -0
- package/dist/types/_bundled/flow-components-base/list/filter/ListFilterValue.d.ts +21 -0
- package/dist/types/_bundled/flow-components-base/list/filter/dateRange.d.ts +23 -0
- package/dist/types/_bundled/flow-components-base/list/filter/types.d.ts +18 -0
- package/dist/types/_bundled/flow-components-base/list/item/ListItem.d.ts +10 -0
- package/dist/types/_bundled/flow-components-base/list/item/ListItemCollection.d.ts +14 -0
- package/dist/types/_bundled/flow-components-base/list/loading/ListLoaderState.d.ts +44 -0
- package/dist/types/_bundled/flow-components-base/list/loading/types.d.ts +69 -0
- package/dist/types/_bundled/flow-components-base/list/pagination/ListBatchesController.d.ts +32 -0
- package/dist/types/_bundled/flow-components-base/list/pagination/types.d.ts +17 -0
- package/dist/types/_bundled/flow-components-base/list/search/ListSearch.d.ts +27 -0
- package/dist/types/_bundled/flow-components-base/list/search/types.d.ts +5 -0
- package/dist/types/_bundled/flow-components-base/list/settings/types.d.ts +52 -0
- package/dist/types/_bundled/flow-components-base/list/sorting/ListSorting.d.ts +40 -0
- package/dist/types/_bundled/flow-components-base/list/sorting/types.d.ts +14 -0
- package/dist/types/_bundled/flow-components-base/list/table/ListTable.d.ts +34 -0
- package/dist/types/_bundled/flow-components-base/list/types.d.ts +18 -0
- package/dist/types/_bundled/flow-components-base/list/viewMode/ListViewMode.d.ts +25 -0
- package/dist/types/_bundled/flow-components-base/list/viewMode/types.d.ts +10 -0
- package/dist/types/components/Action/models/ActionState.d.ts +8 -21
- package/dist/types/components/List/components/Header/lib.d.ts +1 -2
- package/dist/types/components/List/model/List.d.ts +6 -1
- package/dist/types/components/List/model/ListSettingsStore.d.ts +2 -4
- package/dist/types/components/List/model/ListViewMode.d.ts +17 -10
- package/dist/types/components/List/model/ReactTable.d.ts +1 -1
- package/dist/types/components/List/model/filter/DateRangeFilter.d.ts +21 -9
- package/dist/types/components/List/model/filter/Filter.d.ts +18 -52
- package/dist/types/components/List/model/filter/FilterValue.d.ts +10 -11
- package/dist/types/components/List/model/filter/types.d.ts +6 -18
- package/dist/types/components/List/model/item/Item.d.ts +8 -7
- package/dist/types/components/List/model/item/ItemCollection.d.ts +9 -4
- package/dist/types/components/List/model/loading/IncrementalLoaderState.d.ts +11 -21
- package/dist/types/components/List/model/loading/types.d.ts +5 -26
- package/dist/types/components/List/model/pagination/BatchesController.d.ts +1 -15
- package/dist/types/components/List/model/pagination/types.d.ts +1 -3
- package/dist/types/components/List/model/search/Search.d.ts +16 -19
- package/dist/types/components/List/model/search/types.d.ts +6 -8
- package/dist/types/components/List/model/sorting/Sorting.d.ts +14 -22
- package/dist/types/components/List/model/sorting/SortingFunctions.d.ts +1 -1
- package/dist/types/components/List/model/sorting/types.d.ts +1 -13
- package/dist/types/components/List/model/types.d.ts +7 -24
- package/dist/types/components/List/typedList.d.ts +2 -2
- package/dist/types/lib/remote/eventHandlerContext.d.ts +2 -1
- package/package.json +11 -10
- package/dist/js/packages/components/src/components/List/model/filter/FilterValue.mjs +0 -43
- package/dist/js/packages/components/src/components/List/model/filter/FilterValue.mjs.map +0 -1
- package/dist/js/packages/components/src/components/List/model/filter/dateRangeFilterFn.mjs +0 -23
- package/dist/js/packages/components/src/components/List/model/filter/dateRangeFilterFn.mjs.map +0 -1
- package/dist/js/packages/components/src/components/List/model/item/Item.mjs +0 -20
- package/dist/js/packages/components/src/components/List/model/item/Item.mjs.map +0 -1
- package/dist/js/packages/components/src/components/List/model/pagination/BatchesController.mjs +0 -58
- package/dist/js/packages/components/src/components/List/model/pagination/BatchesController.mjs.map +0 -1
- package/dist/js/packages/components/src/lib/array/toArray.mjs +0 -8
- package/dist/js/packages/components/src/lib/array/toArray.mjs.map +0 -1
- package/dist/js/packages/components/src/lib/date/transformDateValueToDateTime.mjs +0 -15
- package/dist/js/packages/components/src/lib/date/transformDateValueToDateTime.mjs.map +0 -1
- package/dist/js/packages/components/src/lib/promises/sleep.mjs +0 -8
- package/dist/js/packages/components/src/lib/promises/sleep.mjs.map +0 -1
- package/dist/types/components/CartesianChart/typedCartesianChart.test-types.d.ts +0 -1
- package/dist/types/components/List/model/filter/dateRangeFilterFn.d.ts +0 -6
- package/dist/types/components/List/typedList.test-types.d.ts +0 -1
- package/dist/types/integrations/@mittwald/password-tools-js/passwordCreationFieldExport.test-types.d.ts +0 -1
- package/dist/types/lib/array/toArray.d.ts +0 -1
- package/dist/types/lib/types/array.d.ts +0 -1
- package/dist/types/lib/types/props.test-types.d.ts +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListFilterValue.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/filter/ListFilterValue.ts"],"sourcesContent":["import type { ListFilter } from \"./ListFilter\";\nimport { hash } from \"object-code\";\nimport { isShallowEqual } from \"remeda\";\n\nconst idPrefix = \"FilterValueId@@\";\n\n/**\n * One selectable value of a filter, and the identity it is stored under.\n *\n * The id is content-addressed — the property plus a hash of the value — so a\n * selection survives a reload without the values having to keep their order or\n * their index.\n */\nexport class ListFilterValue<TRendered = unknown> {\n public readonly filter: ListFilter<unknown, string, unknown, TRendered>;\n public readonly value: unknown;\n public readonly id: string;\n\n private constructor(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter: ListFilter<any, any, any, TRendered>,\n value: unknown,\n ) {\n this.filter = filter;\n\n /* An id round-tripped through storage, rather than a value. */\n if (typeof value === \"string\" && value.startsWith(idPrefix)) {\n this.value = filter.values.find((v) => v.id === value)?.value;\n this.id = value;\n } else {\n this.value = value;\n this.id = `${idPrefix}${this.filter.property}@@${hash(this.value)}`;\n }\n }\n\n public static create<TRendered>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n filter: ListFilter<any, any, any, TRendered>,\n value: unknown,\n ): ListFilterValue<TRendered> {\n if (value instanceof ListFilterValue) {\n return value as ListFilterValue<TRendered>;\n }\n return new ListFilterValue<TRendered>(filter, value);\n }\n\n public equals(otherValue: ListFilterValue<TRendered>): boolean {\n return isShallowEqual(this.value, otherValue.value);\n }\n\n public get isActive(): boolean {\n return this.filter.isValueActive(this);\n }\n\n public render(): TRendered {\n return this.filter.renderItem(this.value as never);\n }\n\n public toggle(): void {\n this.filter.toggleValue(this);\n }\n\n public deactivate(): void {\n this.filter.deactivateValue(this);\n }\n}\n\nexport default ListFilterValue;\n"],"mappings":";;;;;AAIA,IAAM,WAAW;;;;;;;;AASjB,IAAa,kBAAb,MAAa,gBAAqC;CAChD;CACA;CACA;CAEA,YAEE,QACA,OACA;EACA,KAAK,SAAS;EAGd,IAAI,OAAO,UAAU,YAAY,MAAM,WAAW,QAAQ,GAAG;GAC3D,KAAK,QAAQ,OAAO,OAAO,MAAM,MAAM,EAAE,OAAO,KAAK,CAAC,EAAE;GACxD,KAAK,KAAK;EACZ,OAAO;GACL,KAAK,QAAQ;GACb,KAAK,KAAK,GAAG,WAAW,KAAK,OAAO,SAAS,IAAI,KAAK,KAAK,KAAK;EAClE;CACF;CAEA,OAAc,OAEZ,QACA,OAC4B;EAC5B,IAAI,iBAAiB,iBACnB,OAAO;EAET,OAAO,IAAI,gBAA2B,QAAQ,KAAK;CACrD;CAEA,OAAc,YAAiD;EAC7D,OAAO,eAAe,KAAK,OAAO,WAAW,KAAK;CACpD;CAEA,IAAW,WAAoB;EAC7B,OAAO,KAAK,OAAO,cAAc,IAAI;CACvC;CAEA,SAA2B;EACzB,OAAO,KAAK,OAAO,WAAW,KAAK,KAAc;CACnD;CAEA,SAAsB;EACpB,KAAK,OAAO,YAAY,IAAI;CAC9B;CAEA,aAA0B;EACxB,KAAK,OAAO,gBAAgB,IAAI;CAClC;AACF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
import { CalendarDate } from "@internationalized/date";
|
|
4
|
+
import { DateTime } from "luxon";
|
|
5
|
+
//#region ../components-base/src/list/filter/dateRange.ts
|
|
6
|
+
/** A range needs both ends present to be one, even if either may be unset. */
|
|
7
|
+
var isListDateRange = (value) => !!value && typeof value === "object" && "start" in value && "end" in value;
|
|
8
|
+
var startOfDay = (date) => DateTime.fromObject({
|
|
9
|
+
year: date.year,
|
|
10
|
+
month: date.month,
|
|
11
|
+
day: date.day
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* The cell's value as a point in time, or `undefined` when the filter does not
|
|
15
|
+
* treat it as a date.
|
|
16
|
+
*
|
|
17
|
+
* Three shapes count, each on its own terms: a luxon `DateTime` and an ISO 8601
|
|
18
|
+
* string are compared as the instant they name — a date-only string as the
|
|
19
|
+
* start of its day — and a `CalendarDate` as its day. Anything else, a plain `{
|
|
20
|
+
* year, month, day }` object included, is not a date here.
|
|
21
|
+
*/
|
|
22
|
+
var toDateTime = (value) => {
|
|
23
|
+
if (value instanceof DateTime) return value;
|
|
24
|
+
if (value instanceof CalendarDate) return startOfDay(value);
|
|
25
|
+
if (typeof value === "string") return DateTime.fromISO(value);
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Whether a row's date falls inside the selected range.
|
|
29
|
+
*
|
|
30
|
+
* A value that is not a date keeps its row: a filter the data cannot answer
|
|
31
|
+
* should not empty the list. That includes a string luxon cannot parse — the
|
|
32
|
+
* invalid `DateTime` compares as `NaN`, and neither end excludes it.
|
|
33
|
+
*/
|
|
34
|
+
var dateRangeFilterFn = (row, columnId, range) => {
|
|
35
|
+
if (!range) return true;
|
|
36
|
+
const dateValue = toDateTime(row.getValue(columnId));
|
|
37
|
+
if (!dateValue) return true;
|
|
38
|
+
const startDate = range.start ? startOfDay(range.start) : void 0;
|
|
39
|
+
const endDate = range.end ? startOfDay(range.end).endOf("day") : void 0;
|
|
40
|
+
if (startDate && dateValue < startDate) return false;
|
|
41
|
+
return !(endDate && dateValue > endDate);
|
|
42
|
+
};
|
|
43
|
+
/** Turns the column into one a date range filters. */
|
|
44
|
+
var applyDateRangeColumnDef = (def) => {
|
|
45
|
+
def.enableColumnFilter = true;
|
|
46
|
+
def.filterFn = dateRangeFilterFn;
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
export { applyDateRangeColumnDef, dateRangeFilterFn, isListDateRange };
|
|
50
|
+
|
|
51
|
+
//# sourceMappingURL=dateRange.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dateRange.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/filter/dateRange.ts"],"sourcesContent":["import { CalendarDate } from \"@internationalized/date\";\nimport type { ColumnDef, Row } from \"@tanstack/table-core\";\nimport { DateTime } from \"luxon\";\n\n/** A calendar date, in whichever shape it reached the filter. */\nexport interface ListDateParts {\n readonly year: number;\n readonly month: number;\n readonly day: number;\n}\n\nexport interface ListDateRange {\n readonly start?: ListDateParts;\n readonly end?: ListDateParts;\n}\n\n/** A range needs both ends present to be one, even if either may be unset. */\nexport const isListDateRange = (value: unknown): value is ListDateRange =>\n !!value && typeof value === \"object\" && \"start\" in value && \"end\" in value;\n\n/*\n * The range's ends are read field by field: they come from the calendar, and\n * after crossing the remote boundary a `CalendarDate` is a plain object.\n */\nconst startOfDay = (date: ListDateParts): DateTime =>\n DateTime.fromObject({\n year: date.year,\n month: date.month,\n day: date.day,\n });\n\n/**\n * The cell's value as a point in time, or `undefined` when the filter does not\n * treat it as a date.\n *\n * Three shapes count, each on its own terms: a luxon `DateTime` and an ISO 8601\n * string are compared as the instant they name — a date-only string as the\n * start of its day — and a `CalendarDate` as its day. Anything else, a plain `{\n * year, month, day }` object included, is not a date here.\n */\nconst toDateTime = (value: unknown): DateTime | undefined => {\n if (value instanceof DateTime) {\n return value;\n }\n if (value instanceof CalendarDate) {\n return startOfDay(value);\n }\n if (typeof value === \"string\") {\n return DateTime.fromISO(value);\n }\n return undefined;\n};\n\n/**\n * Whether a row's date falls inside the selected range.\n *\n * A value that is not a date keeps its row: a filter the data cannot answer\n * should not empty the list. That includes a string luxon cannot parse — the\n * invalid `DateTime` compares as `NaN`, and neither end excludes it.\n */\nexport const dateRangeFilterFn = <T>(\n row: Row<T>,\n columnId: string,\n range?: ListDateRange,\n): boolean => {\n if (!range) {\n return true;\n }\n\n const dateValue = toDateTime(row.getValue(columnId));\n\n if (!dateValue) {\n return true;\n }\n\n const startDate = range.start ? startOfDay(range.start) : undefined;\n /* The whole end day counts, up to its last moment. */\n const endDate = range.end ? startOfDay(range.end).endOf(\"day\") : undefined;\n\n if (startDate && dateValue < startDate) {\n return false;\n }\n\n return !(endDate && dateValue > endDate);\n};\n\n/** Turns the column into one a date range filters. */\nexport const applyDateRangeColumnDef = <T>(def: ColumnDef<T>): void => {\n def.enableColumnFilter = true;\n def.filterFn = dateRangeFilterFn;\n};\n"],"mappings":";;;;;;AAiBA,IAAa,mBAAmB,UAC9B,CAAC,CAAC,SAAS,OAAO,UAAU,YAAY,WAAW,SAAS,SAAS;AAMvE,IAAM,cAAc,SAClB,SAAS,WAAW;CAClB,MAAM,KAAK;CACX,OAAO,KAAK;CACZ,KAAK,KAAK;AACZ,CAAC;;;;;;;;;;AAWH,IAAM,cAAc,UAAyC;CAC3D,IAAI,iBAAiB,UACnB,OAAO;CAET,IAAI,iBAAiB,cACnB,OAAO,WAAW,KAAK;CAEzB,IAAI,OAAO,UAAU,UACnB,OAAO,SAAS,QAAQ,KAAK;AAGjC;;;;;;;;AASA,IAAa,qBACX,KACA,UACA,UACY;CACZ,IAAI,CAAC,OACH,OAAO;CAGT,MAAM,YAAY,WAAW,IAAI,SAAS,QAAQ,CAAC;CAEnD,IAAI,CAAC,WACH,OAAO;CAGT,MAAM,YAAY,MAAM,QAAQ,WAAW,MAAM,KAAK,IAAI,KAAA;CAE1D,MAAM,UAAU,MAAM,MAAM,WAAW,MAAM,GAAG,CAAC,CAAC,MAAM,KAAK,IAAI,KAAA;CAEjE,IAAI,aAAa,YAAY,WAC3B,OAAO;CAGT,OAAO,EAAE,WAAW,YAAY;AAClC;;AAGA,IAAa,2BAA8B,QAA4B;CACrE,IAAI,qBAAqB;CACzB,IAAI,WAAW;AACjB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
//#region ../components-base/src/list/item/ListItem.ts
|
|
4
|
+
/** One row of the list, as the views see it: an id and the data behind it. */
|
|
5
|
+
var ListItem = class ListItem {
|
|
6
|
+
id;
|
|
7
|
+
data;
|
|
8
|
+
collection;
|
|
9
|
+
constructor(collection, id, data) {
|
|
10
|
+
this.collection = collection;
|
|
11
|
+
this.id = id;
|
|
12
|
+
this.data = data;
|
|
13
|
+
}
|
|
14
|
+
static fromRow(collection, row) {
|
|
15
|
+
return new ListItem(collection, row.id, row.original);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { ListItem };
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=ListItem.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListItem.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/item/ListItem.ts"],"sourcesContent":["import type { ListItemCollection } from \"./ListItemCollection\";\nimport type { Row } from \"@tanstack/table-core\";\n\n/** One row of the list, as the views see it: an id and the data behind it. */\nexport class ListItem<T> {\n public readonly id: string;\n public readonly data: T;\n public readonly collection: ListItemCollection<T>;\n\n public constructor(collection: ListItemCollection<T>, id: string, data: T) {\n this.collection = collection;\n this.id = id;\n this.data = data;\n }\n\n public static fromRow<T>(\n collection: ListItemCollection<T>,\n row: Row<T>,\n ): ListItem<T> {\n return new ListItem(collection, row.id, row.original);\n }\n}\n"],"mappings":";;;;AAIA,IAAa,WAAb,MAAa,SAAY;CACvB;CACA;CACA;CAEA,YAAmB,YAAmC,IAAY,MAAS;EACzE,KAAK,aAAa;EAClB,KAAK,KAAK;EACV,KAAK,OAAO;CACd;CAEA,OAAc,QACZ,YACA,KACa;EACb,OAAO,IAAI,SAAS,YAAY,IAAI,IAAI,IAAI,QAAQ;CACtD;AACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
import { ListItem } from "./ListItem.mjs";
|
|
4
|
+
//#region ../components-base/src/list/item/ListItemCollection.ts
|
|
5
|
+
/**
|
|
6
|
+
* The items the list currently shows.
|
|
7
|
+
*
|
|
8
|
+
* Read from the table's row model on every access rather than kept: the rows
|
|
9
|
+
* are what sorting, filtering and paging produce, so a stored copy would be a
|
|
10
|
+
* second answer to the same question.
|
|
11
|
+
*/
|
|
12
|
+
var ListItemCollection = class {
|
|
13
|
+
context;
|
|
14
|
+
constructor(context) {
|
|
15
|
+
this.context = context;
|
|
16
|
+
}
|
|
17
|
+
get entries() {
|
|
18
|
+
return this.context.dataTable.getRowModel().rows.map((r) => ListItem.fromRow(this, r));
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
//#endregion
|
|
22
|
+
export { ListItemCollection };
|
|
23
|
+
|
|
24
|
+
//# sourceMappingURL=ListItemCollection.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListItemCollection.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/item/ListItemCollection.ts"],"sourcesContent":["import type { ListModelContext } from \"../ListModelContext\";\nimport { ListItem } from \"./ListItem\";\n\n/**\n * The items the list currently shows.\n *\n * Read from the table's row model on every access rather than kept: the rows\n * are what sorting, filtering and paging produce, so a stored copy would be a\n * second answer to the same question.\n */\nexport class ListItemCollection<T> {\n public readonly context: ListModelContext<T>;\n\n public constructor(context: ListModelContext<T>) {\n this.context = context;\n }\n\n public get entries(): ListItem<T>[] {\n return this.context.dataTable\n .getRowModel()\n .rows.map((r) => ListItem.fromRow(this, r));\n }\n}\n"],"mappings":";;;;;;;;;;;AAUA,IAAa,qBAAb,MAAmC;CACjC;CAEA,YAAmB,SAA8B;EAC/C,KAAK,UAAU;CACjB;CAEA,IAAW,UAAyB;EAClC,OAAO,KAAK,QAAQ,UACjB,YAAY,CAAC,CACb,KAAK,KAAK,MAAM,SAAS,QAAQ,MAAM,CAAC,CAAC;CAC9C;AACF"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
import { uniqueBy } from "remeda";
|
|
4
|
+
import { action, computed, makeObservable, observable } from "mobx";
|
|
5
|
+
//#region ../components-base/src/list/loading/ListLoaderState.ts
|
|
6
|
+
/**
|
|
7
|
+
* What a list has loaded so far, and how far each batch has got.
|
|
8
|
+
*
|
|
9
|
+
* MobX, and nothing else — no hook, no framework. A binding reads it through
|
|
10
|
+
* its own reactivity: React with `useSelector`, Vue with `watchMobxValue`. That
|
|
11
|
+
* split is the point: the batching, the deduplication and the "still loading"
|
|
12
|
+
* rules are one implementation, and only the subscription is written twice.
|
|
13
|
+
*
|
|
14
|
+
* What is _not_ here is the loading itself. Fetching a batch is
|
|
15
|
+
* `usePromise`/Suspense in React and something else in Vue, so it stays with
|
|
16
|
+
* the binding, and only its outcome is reported here.
|
|
17
|
+
*/
|
|
18
|
+
var ListLoaderState = class {
|
|
19
|
+
dataBatches = [];
|
|
20
|
+
batchLoadingStates = ["void"];
|
|
21
|
+
metadata = void 0;
|
|
22
|
+
isInitiallyLoading = true;
|
|
23
|
+
/**
|
|
24
|
+
* The last non-empty result, kept outside MobX on purpose: it exists so a
|
|
25
|
+
* reset can keep rendering the previous data instead of flashing empty, and
|
|
26
|
+
* observing it would make that very reset re-render.
|
|
27
|
+
*/
|
|
28
|
+
prevDataBatches = [];
|
|
29
|
+
getItemId;
|
|
30
|
+
constructor(options = {}) {
|
|
31
|
+
this.getItemId = options.getItemId;
|
|
32
|
+
makeObservable(this, {
|
|
33
|
+
prevDataBatches: false,
|
|
34
|
+
dataBatches: observable.shallow,
|
|
35
|
+
batchLoadingStates: observable.shallow,
|
|
36
|
+
metadata: observable,
|
|
37
|
+
mergedData: computed,
|
|
38
|
+
isLoading: computed,
|
|
39
|
+
isLoadingMore: computed,
|
|
40
|
+
isInitiallyLoading: observable,
|
|
41
|
+
setIsInitiallyLoading: action.bound,
|
|
42
|
+
reset: action.bound,
|
|
43
|
+
setDataBatch: action.bound,
|
|
44
|
+
setBatchLoadingState: action.bound,
|
|
45
|
+
setMetadata: action.bound
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
reset() {
|
|
49
|
+
this.batchLoadingStates = [];
|
|
50
|
+
this.dataBatches = [];
|
|
51
|
+
}
|
|
52
|
+
setDataBatch(index, data) {
|
|
53
|
+
if (this.dataBatches.length === 0) this.prevDataBatches = [];
|
|
54
|
+
if (this.dataBatches[index] !== data) {
|
|
55
|
+
this.dataBatches[index] = data;
|
|
56
|
+
this.prevDataBatches[index] = data;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
setBatchLoadingState(index, state) {
|
|
60
|
+
if (state === "error" || state === "loaded") this.setIsInitiallyLoading(false);
|
|
61
|
+
if (this.batchLoadingStates[index] !== state) this.batchLoadingStates[index] = state;
|
|
62
|
+
}
|
|
63
|
+
setMetadata(metadata) {
|
|
64
|
+
this.metadata = metadata;
|
|
65
|
+
}
|
|
66
|
+
setIsInitiallyLoading(isInitiallyLoading) {
|
|
67
|
+
this.isInitiallyLoading = isInitiallyLoading;
|
|
68
|
+
}
|
|
69
|
+
get mergedData() {
|
|
70
|
+
const merged = (this.dataBatches.length === 0 ? this.prevDataBatches : this.dataBatches).flatMap((d) => d);
|
|
71
|
+
return this.getItemId ? uniqueBy(merged, this.getItemId) : merged;
|
|
72
|
+
}
|
|
73
|
+
get isLoading() {
|
|
74
|
+
return this.batchLoadingStates.some((s) => s === "loading" || s === "void");
|
|
75
|
+
}
|
|
76
|
+
get isLoadingMore() {
|
|
77
|
+
return this.isLoading && this.dataBatches.length > 0;
|
|
78
|
+
}
|
|
79
|
+
isBatchLoaded(batchIndex) {
|
|
80
|
+
return batchIndex in this.dataBatches;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
//#endregion
|
|
84
|
+
export { ListLoaderState, ListLoaderState as default };
|
|
85
|
+
|
|
86
|
+
//# sourceMappingURL=ListLoaderState.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListLoaderState.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/loading/ListLoaderState.ts"],"sourcesContent":["import type { BatchLoadingState, ListData } from \"./types\";\nimport { action, computed, makeObservable, observable } from \"mobx\";\nimport { uniqueBy } from \"remeda\";\n\nexport interface ListLoaderStateOptions<T> {\n /**\n * Identifies an item, so the same one loaded in two batches appears once.\n * Without it the batches are concatenated as they arrive.\n */\n getItemId?: (item: T) => string;\n}\n\n/**\n * What a list has loaded so far, and how far each batch has got.\n *\n * MobX, and nothing else — no hook, no framework. A binding reads it through\n * its own reactivity: React with `useSelector`, Vue with `watchMobxValue`. That\n * split is the point: the batching, the deduplication and the \"still loading\"\n * rules are one implementation, and only the subscription is written twice.\n *\n * What is _not_ here is the loading itself. Fetching a batch is\n * `usePromise`/Suspense in React and something else in Vue, so it stays with\n * the binding, and only its outcome is reported here.\n */\nexport class ListLoaderState<T> {\n public dataBatches: ListData<T>[] = [];\n public batchLoadingStates: BatchLoadingState[] = [\"void\"];\n public metadata?: unknown = undefined;\n public isInitiallyLoading = true;\n\n /**\n * The last non-empty result, kept outside MobX on purpose: it exists so a\n * reset can keep rendering the previous data instead of flashing empty, and\n * observing it would make that very reset re-render.\n */\n public prevDataBatches: ListData<T>[] = [];\n\n private readonly getItemId?: (item: T) => string;\n\n public constructor(options: ListLoaderStateOptions<T> = {}) {\n this.getItemId = options.getItemId;\n\n makeObservable(this, {\n prevDataBatches: false,\n dataBatches: observable.shallow,\n batchLoadingStates: observable.shallow,\n metadata: observable,\n mergedData: computed,\n isLoading: computed,\n isLoadingMore: computed,\n isInitiallyLoading: observable,\n setIsInitiallyLoading: action.bound,\n reset: action.bound,\n setDataBatch: action.bound,\n setBatchLoadingState: action.bound,\n setMetadata: action.bound,\n });\n }\n\n public reset(): void {\n this.batchLoadingStates = [];\n this.dataBatches = [];\n }\n\n public setDataBatch(index: number, data: ListData<T>): void {\n if (this.dataBatches.length === 0) {\n this.prevDataBatches = [];\n }\n\n if (this.dataBatches[index] !== data) {\n this.dataBatches[index] = data;\n this.prevDataBatches[index] = data;\n }\n }\n\n public setBatchLoadingState(index: number, state: BatchLoadingState): void {\n if (state === \"error\" || state === \"loaded\") {\n this.setIsInitiallyLoading(false);\n }\n if (this.batchLoadingStates[index] !== state) {\n this.batchLoadingStates[index] = state;\n }\n }\n\n public setMetadata(metadata?: unknown): void {\n this.metadata = metadata;\n }\n\n public setIsInitiallyLoading(isInitiallyLoading: boolean): void {\n this.isInitiallyLoading = isInitiallyLoading;\n }\n\n public get mergedData(): T[] {\n const dataBatches =\n this.dataBatches.length === 0 ? this.prevDataBatches : this.dataBatches;\n\n const merged = dataBatches.flatMap((d) => d);\n return this.getItemId ? uniqueBy(merged, this.getItemId) : merged;\n }\n\n public get isLoading(): boolean {\n return this.batchLoadingStates.some((s) => s === \"loading\" || s === \"void\");\n }\n\n public get isLoadingMore(): boolean {\n return this.isLoading && this.dataBatches.length > 0;\n }\n\n public isBatchLoaded(batchIndex: number): boolean {\n return batchIndex in this.dataBatches;\n }\n}\n\nexport default ListLoaderState;\n"],"mappings":";;;;;;;;;;;;;;;;;AAwBA,IAAa,kBAAb,MAAgC;CAC9B,cAAoC,CAAC;CACrC,qBAAiD,CAAC,MAAM;CACxD,WAA4B,KAAA;CAC5B,qBAA4B;;;;;;CAO5B,kBAAwC,CAAC;CAEzC;CAEA,YAAmB,UAAqC,CAAC,GAAG;EAC1D,KAAK,YAAY,QAAQ;EAEzB,eAAe,MAAM;GACnB,iBAAiB;GACjB,aAAa,WAAW;GACxB,oBAAoB,WAAW;GAC/B,UAAU;GACV,YAAY;GACZ,WAAW;GACX,eAAe;GACf,oBAAoB;GACpB,uBAAuB,OAAO;GAC9B,OAAO,OAAO;GACd,cAAc,OAAO;GACrB,sBAAsB,OAAO;GAC7B,aAAa,OAAO;EACtB,CAAC;CACH;CAEA,QAAqB;EACnB,KAAK,qBAAqB,CAAC;EAC3B,KAAK,cAAc,CAAC;CACtB;CAEA,aAAoB,OAAe,MAAyB;EAC1D,IAAI,KAAK,YAAY,WAAW,GAC9B,KAAK,kBAAkB,CAAC;EAG1B,IAAI,KAAK,YAAY,WAAW,MAAM;GACpC,KAAK,YAAY,SAAS;GAC1B,KAAK,gBAAgB,SAAS;EAChC;CACF;CAEA,qBAA4B,OAAe,OAAgC;EACzE,IAAI,UAAU,WAAW,UAAU,UACjC,KAAK,sBAAsB,KAAK;EAElC,IAAI,KAAK,mBAAmB,WAAW,OACrC,KAAK,mBAAmB,SAAS;CAErC;CAEA,YAAmB,UAA0B;EAC3C,KAAK,WAAW;CAClB;CAEA,sBAA6B,oBAAmC;EAC9D,KAAK,qBAAqB;CAC5B;CAEA,IAAW,aAAkB;EAI3B,MAAM,UAFJ,KAAK,YAAY,WAAW,IAAI,KAAK,kBAAkB,KAAK,YAAA,CAEnC,SAAS,MAAM,CAAC;EAC3C,OAAO,KAAK,YAAY,SAAS,QAAQ,KAAK,SAAS,IAAI;CAC7D;CAEA,IAAW,YAAqB;EAC9B,OAAO,KAAK,mBAAmB,MAAM,MAAM,MAAM,aAAa,MAAM,MAAM;CAC5E;CAEA,IAAW,gBAAyB;EAClC,OAAO,KAAK,aAAa,KAAK,YAAY,SAAS;CACrD;CAEA,cAAqB,YAA6B;EAChD,OAAO,cAAc,KAAK;CAC5B;AACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
//#region ../components-base/src/list/loading/types.ts
|
|
4
|
+
/**
|
|
5
|
+
* The query for one batch.
|
|
6
|
+
*
|
|
7
|
+
* Every branch is the same question — "does the source do this itself?" — and
|
|
8
|
+
* getting it wrong is silent: a source that paginates but is sent no pagination
|
|
9
|
+
* returns its first page forever.
|
|
10
|
+
*/
|
|
11
|
+
var getListDataLoaderOptions = (source, batchIndex) => {
|
|
12
|
+
const { batchSize, modes } = source;
|
|
13
|
+
return {
|
|
14
|
+
pagination: modes.manualPagination ? {
|
|
15
|
+
limit: batchSize,
|
|
16
|
+
offset: batchSize * batchIndex
|
|
17
|
+
} : void 0,
|
|
18
|
+
sorting: modes.manualSorting ? Object.fromEntries(source.sorting.filter((s) => s.isSorted()).map((s) => [s.property, s.direction])) : void 0,
|
|
19
|
+
filtering: modes.manualFiltering ? Object.fromEntries(source.filters.filter((f) => f.getValue() !== null).map((f) => [f.property, {
|
|
20
|
+
mode: f.mode,
|
|
21
|
+
values: f.getArrayValue().map((v) => v.value)
|
|
22
|
+
}])) : void 0,
|
|
23
|
+
searchString: modes.manualFiltering ? source.searchString : void 0
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
export { getListDataLoaderOptions };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=types.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/loading/types.ts"],"sourcesContent":["import type { FilterMode } from \"../filter/types\";\nimport type { ListLoaderModes } from \"../pagination/types\";\nimport type { SearchValue } from \"../search/types\";\nimport type { ListSorting } from \"../sorting/ListSorting\";\nimport type { PropertyName } from \"../types\";\n\n/** One batch of list data, as a loader hands it over. */\nexport type ListData<T> = readonly T[];\n\n/**\n * How far one batch has got.\n *\n * The same four values `@mittwald/react-use-promise` uses, spelled out rather\n * than imported: that package is React's, and this one is the half both\n * bindings share.\n */\nexport type BatchLoadingState = \"void\" | \"loading\" | \"loaded\" | \"error\";\n\n/** A value per property of `T`, e.g. a direction or a filter's selection. */\nexport type ListPropertyRecord<T, TValue> = Partial<\n Record<PropertyName<T>, TValue>\n>;\n\ninterface ListDataLoaderPaginationOptions {\n offset: number;\n limit: number;\n}\n\n/**\n * What a list asks its data source for.\n *\n * The consumer-facing contract of every dynamic loader, and the reason it is\n * here rather than with a binding: a Vue app and a React app write the same\n * loader function. Each field is filled only where the source said it does that\n * job itself — see `ListLoaderModes`.\n */\nexport interface ListDataLoaderOptions<T> {\n sorting?: ListPropertyRecord<T, \"asc\" | \"desc\">;\n filtering?: ListPropertyRecord<T, { mode: FilterMode; values: unknown[] }>;\n searchString?: SearchValue;\n pagination?: ListDataLoaderPaginationOptions;\n}\n\nexport interface ListDataLoaderResult<T> {\n data: ListData<T>;\n itemTotalCount?: number;\n metadata?: unknown;\n}\n\nexport type ListAsyncDataLoader<T> = (\n options: ListDataLoaderOptions<T>,\n) => Promise<ListDataLoaderResult<T>>;\n\n/** What building the loader options needs of a filter, and nothing more. */\nexport interface QueryableFilter<T> {\n readonly property: PropertyName<T>;\n readonly mode: FilterMode;\n getValue(): unknown;\n getArrayValue(): { value: unknown }[];\n}\n\nexport interface ListDataLoaderOptionsSource<T> {\n readonly batchSize: number;\n readonly modes: ListLoaderModes;\n readonly sorting: ListSorting<T>[];\n readonly filters: QueryableFilter<T>[];\n readonly searchString: SearchValue;\n}\n\n/**\n * The query for one batch.\n *\n * Every branch is the same question — \"does the source do this itself?\" — and\n * getting it wrong is silent: a source that paginates but is sent no pagination\n * returns its first page forever.\n */\nexport const getListDataLoaderOptions = <T>(\n source: ListDataLoaderOptionsSource<T>,\n batchIndex: number,\n): ListDataLoaderOptions<T> => {\n const { batchSize, modes } = source;\n\n return {\n pagination: modes.manualPagination\n ? { limit: batchSize, offset: batchSize * batchIndex }\n : undefined,\n\n sorting: modes.manualSorting\n ? (Object.fromEntries(\n source.sorting\n .filter((s) => s.isSorted())\n .map((s) => [s.property, s.direction]),\n ) as ListDataLoaderOptions<T>[\"sorting\"])\n : undefined,\n\n filtering: modes.manualFiltering\n ? (Object.fromEntries(\n source.filters\n .filter((f) => f.getValue() !== null)\n .map((f) => [\n f.property,\n { mode: f.mode, values: f.getArrayValue().map((v) => v.value) },\n ]),\n ) as ListDataLoaderOptions<T>[\"filtering\"])\n : undefined,\n\n searchString: modes.manualFiltering ? source.searchString : undefined,\n };\n};\n"],"mappings":";;;;;;;;;;AA4EA,IAAa,4BACX,QACA,eAC6B;CAC7B,MAAM,EAAE,WAAW,UAAU;CAE7B,OAAO;EACL,YAAY,MAAM,mBACd;GAAE,OAAO;GAAW,QAAQ,YAAY;EAAW,IACnD,KAAA;EAEJ,SAAS,MAAM,gBACV,OAAO,YACN,OAAO,QACJ,QAAQ,MAAM,EAAE,SAAS,CAAC,CAAC,CAC3B,KAAK,MAAM,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,CACzC,IACA,KAAA;EAEJ,WAAW,MAAM,kBACZ,OAAO,YACN,OAAO,QACJ,QAAQ,MAAM,EAAE,SAAS,MAAM,IAAI,CAAC,CACpC,KAAK,MAAM,CACV,EAAE,UACF;GAAE,MAAM,EAAE;GAAM,QAAQ,EAAE,cAAc,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK;EAAE,CAChE,CAAC,CACL,IACA,KAAA;EAEJ,cAAc,MAAM,kBAAkB,OAAO,eAAe,KAAA;CAC9D;AACF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
//#region ../components-base/src/list/pagination/ListBatchesController.ts
|
|
4
|
+
/**
|
|
5
|
+
* How much of the list is shown, in batches of `batchSize`.
|
|
6
|
+
*
|
|
7
|
+
* Resetting on a changed filter or search is **not** wired here: the filters
|
|
8
|
+
* and the search belong to the list that assembles them, and it is the list
|
|
9
|
+
* that calls `reset()`. Keeping the wiring out also keeps the variance trap out
|
|
10
|
+
* — a list of concrete filters is not assignable to a list of generic ones.
|
|
11
|
+
*/
|
|
12
|
+
var ListBatchesController = class {
|
|
13
|
+
batchSize;
|
|
14
|
+
context;
|
|
15
|
+
constructor(context, shape = {}) {
|
|
16
|
+
const { batchSize = 20 } = shape;
|
|
17
|
+
this.context = context;
|
|
18
|
+
this.batchSize = batchSize;
|
|
19
|
+
}
|
|
20
|
+
get dataTable() {
|
|
21
|
+
return this.context.dataTable;
|
|
22
|
+
}
|
|
23
|
+
getBatchIndex() {
|
|
24
|
+
return this.dataTable.getState().pagination.pageIndex;
|
|
25
|
+
}
|
|
26
|
+
hasNextBatch() {
|
|
27
|
+
if (this.context.loader.manualPagination) return this.dataTable.getCanNextPage();
|
|
28
|
+
return this.dataTable.getState().pagination.pageSize < this.getTotalItemsCount();
|
|
29
|
+
}
|
|
30
|
+
getTotalItemsCount() {
|
|
31
|
+
if (this.context.loader.manualPagination) return this.dataTable.getRowCount();
|
|
32
|
+
return this.dataTable.getFilteredRowModel().rows.length;
|
|
33
|
+
}
|
|
34
|
+
getVisibleItemsCount() {
|
|
35
|
+
return this.dataTable.getRowModel().rows.length ?? 0;
|
|
36
|
+
}
|
|
37
|
+
updateItemTotalCount(value) {
|
|
38
|
+
this.dataTable.setOptions((s) => ({
|
|
39
|
+
...s,
|
|
40
|
+
rowCount: value
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
reset() {
|
|
44
|
+
if (this.context.loader.manualFiltering) this.updateItemTotalCount(0);
|
|
45
|
+
this.dataTable.setPagination(() => ({
|
|
46
|
+
pageIndex: 0,
|
|
47
|
+
pageSize: this.batchSize
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
nextBatch() {
|
|
51
|
+
if (this.context.loader.manualPagination) {
|
|
52
|
+
this.dataTable.nextPage();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
this.dataTable.setPagination((prev) => ({
|
|
56
|
+
...prev,
|
|
57
|
+
pageSize: prev.pageSize + this.batchSize
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
export { ListBatchesController };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=ListBatchesController.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListBatchesController.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/pagination/ListBatchesController.ts"],"sourcesContent":["import type { ListModelContext } from \"../ListModelContext\";\nimport type { ListBatchesControllerShape, ListLoaderModes } from \"./types\";\n\n/**\n * The context a batches controller needs: the table, plus who does the paging.\n *\n * Declared here rather than folded into `ListModelContext`, so a model that\n * only sorts or filters is not made to carry a loader it never asks anything.\n */\nexport interface ListPaginationContext<T> extends ListModelContext<T> {\n readonly loader: ListLoaderModes;\n}\n\n/**\n * How much of the list is shown, in batches of `batchSize`.\n *\n * Resetting on a changed filter or search is **not** wired here: the filters\n * and the search belong to the list that assembles them, and it is the list\n * that calls `reset()`. Keeping the wiring out also keeps the variance trap out\n * — a list of concrete filters is not assignable to a list of generic ones.\n */\nexport class ListBatchesController<T> {\n public readonly batchSize: number;\n private readonly context: ListPaginationContext<T>;\n\n public constructor(\n context: ListPaginationContext<T>,\n shape: ListBatchesControllerShape = {},\n ) {\n const { batchSize = 20 } = shape;\n\n this.context = context;\n this.batchSize = batchSize;\n }\n\n private get dataTable() {\n return this.context.dataTable;\n }\n\n public getBatchIndex(): number {\n return this.dataTable.getState().pagination.pageIndex;\n }\n\n public hasNextBatch(): boolean {\n if (this.context.loader.manualPagination) {\n return this.dataTable.getCanNextPage();\n }\n return (\n this.dataTable.getState().pagination.pageSize < this.getTotalItemsCount()\n );\n }\n\n public getTotalItemsCount(): number {\n if (this.context.loader.manualPagination) {\n return this.dataTable.getRowCount();\n }\n return this.dataTable.getFilteredRowModel().rows.length;\n }\n\n public getVisibleItemsCount(): number {\n return this.dataTable.getRowModel().rows.length ?? 0;\n }\n\n public updateItemTotalCount(value: number): void {\n this.dataTable.setOptions((s) => ({\n ...s,\n rowCount: value,\n }));\n }\n\n public reset(): void {\n if (this.context.loader.manualFiltering) {\n this.updateItemTotalCount(0);\n }\n this.dataTable.setPagination(() => ({\n pageIndex: 0,\n pageSize: this.batchSize,\n }));\n }\n\n public nextBatch(): void {\n if (this.context.loader.manualPagination) {\n this.dataTable.nextPage();\n return;\n }\n\n this.dataTable.setPagination((prev) => ({\n ...prev,\n pageSize: prev.pageSize + this.batchSize,\n }));\n }\n}\n"],"mappings":";;;;;;;;;;;AAqBA,IAAa,wBAAb,MAAsC;CACpC;CACA;CAEA,YACE,SACA,QAAoC,CAAC,GACrC;EACA,MAAM,EAAE,YAAY,OAAO;EAE3B,KAAK,UAAU;EACf,KAAK,YAAY;CACnB;CAEA,IAAY,YAAY;EACtB,OAAO,KAAK,QAAQ;CACtB;CAEA,gBAA+B;EAC7B,OAAO,KAAK,UAAU,SAAS,CAAC,CAAC,WAAW;CAC9C;CAEA,eAA+B;EAC7B,IAAI,KAAK,QAAQ,OAAO,kBACtB,OAAO,KAAK,UAAU,eAAe;EAEvC,OACE,KAAK,UAAU,SAAS,CAAC,CAAC,WAAW,WAAW,KAAK,mBAAmB;CAE5E;CAEA,qBAAoC;EAClC,IAAI,KAAK,QAAQ,OAAO,kBACtB,OAAO,KAAK,UAAU,YAAY;EAEpC,OAAO,KAAK,UAAU,oBAAoB,CAAC,CAAC,KAAK;CACnD;CAEA,uBAAsC;EACpC,OAAO,KAAK,UAAU,YAAY,CAAC,CAAC,KAAK,UAAU;CACrD;CAEA,qBAA4B,OAAqB;EAC/C,KAAK,UAAU,YAAY,OAAO;GAChC,GAAG;GACH,UAAU;EACZ,EAAE;CACJ;CAEA,QAAqB;EACnB,IAAI,KAAK,QAAQ,OAAO,iBACtB,KAAK,qBAAqB,CAAC;EAE7B,KAAK,UAAU,qBAAqB;GAClC,WAAW;GACX,UAAU,KAAK;EACjB,EAAE;CACJ;CAEA,YAAyB;EACvB,IAAI,KAAK,QAAQ,OAAO,kBAAkB;GACxC,KAAK,UAAU,SAAS;GACxB;EACF;EAEA,KAAK,UAAU,eAAe,UAAU;GACtC,GAAG;GACH,UAAU,KAAK,WAAW,KAAK;EACjC,EAAE;CACJ;AACF"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
//#region ../components-base/src/list/search/ListSearch.ts
|
|
4
|
+
/**
|
|
5
|
+
* The list's search term: where it starts, where it is kept, who to tell.
|
|
6
|
+
*
|
|
7
|
+
* The term itself lives in the table's global filter — this owns the initial
|
|
8
|
+
* value, the persistence and the notification, none of which is a framework's
|
|
9
|
+
* business. What a binding adds on top is how the field is _rendered_.
|
|
10
|
+
*/
|
|
11
|
+
var ListSearch = class {
|
|
12
|
+
context;
|
|
13
|
+
onUpdateCallbacks = /* @__PURE__ */ new Set();
|
|
14
|
+
initialValue;
|
|
15
|
+
defaultValue;
|
|
16
|
+
autosave;
|
|
17
|
+
constructor(context, shape) {
|
|
18
|
+
const { autosave = context.settingsDefaults?.search?.autosave ?? false, defaultValue } = shape;
|
|
19
|
+
this.autosave = autosave;
|
|
20
|
+
this.context = context;
|
|
21
|
+
this.defaultValue = defaultValue;
|
|
22
|
+
this.initialValue = this.getInitialValue();
|
|
23
|
+
}
|
|
24
|
+
get value() {
|
|
25
|
+
return this.context.dataTable.getState().globalFilter;
|
|
26
|
+
}
|
|
27
|
+
get isSet() {
|
|
28
|
+
return this.value !== void 0;
|
|
29
|
+
}
|
|
30
|
+
callOnUpdateCallbacks() {
|
|
31
|
+
this.onUpdateCallbacks.forEach((cb) => cb());
|
|
32
|
+
}
|
|
33
|
+
getInitialValue() {
|
|
34
|
+
return this.context.settings?.get("search", { autosave: this.autosave })?.value ?? this.defaultValue;
|
|
35
|
+
}
|
|
36
|
+
updateInitialState(initialState) {
|
|
37
|
+
initialState.globalFilter = this.initialValue;
|
|
38
|
+
}
|
|
39
|
+
setValue(value) {
|
|
40
|
+
const table = this.context.dataTable;
|
|
41
|
+
if (value === void 0 || value.trim() === "") {
|
|
42
|
+
if (table.getState().globalFilter) {
|
|
43
|
+
table.setGlobalFilter(void 0);
|
|
44
|
+
this.callOnUpdateCallbacks();
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
table.setGlobalFilter(value);
|
|
48
|
+
this.callOnUpdateCallbacks();
|
|
49
|
+
}
|
|
50
|
+
this.context.settings?.store("search", { value }, { autosave: this.autosave });
|
|
51
|
+
}
|
|
52
|
+
clear() {
|
|
53
|
+
this.setValue(void 0);
|
|
54
|
+
}
|
|
55
|
+
onUpdated(cb) {
|
|
56
|
+
this.onUpdateCallbacks.add(cb);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
//#endregion
|
|
60
|
+
export { ListSearch, ListSearch as default };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=ListSearch.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListSearch.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/search/ListSearch.ts"],"sourcesContent":["import type { ListModelContext } from \"../ListModelContext\";\nimport type { ListSearchShape, SearchValue } from \"./types\";\nimport type { InitialTableState } from \"@tanstack/table-core\";\n\n/**\n * The list's search term: where it starts, where it is kept, who to tell.\n *\n * The term itself lives in the table's global filter — this owns the initial\n * value, the persistence and the notification, none of which is a framework's\n * business. What a binding adds on top is how the field is _rendered_.\n */\nexport class ListSearch<T> {\n public readonly context: ListModelContext<T>;\n private readonly onUpdateCallbacks = new Set<() => unknown>();\n private readonly initialValue?: string;\n private readonly defaultValue?: string;\n private readonly autosave: boolean;\n\n public constructor(context: ListModelContext<T>, shape: ListSearchShape) {\n const {\n autosave = context.settingsDefaults?.search?.autosave ?? false,\n defaultValue,\n } = shape;\n\n this.autosave = autosave;\n this.context = context;\n this.defaultValue = defaultValue;\n this.initialValue = this.getInitialValue();\n }\n\n public get value(): SearchValue {\n return this.context.dataTable.getState().globalFilter as SearchValue;\n }\n\n public get isSet(): boolean {\n return this.value !== undefined;\n }\n\n private callOnUpdateCallbacks(): void {\n this.onUpdateCallbacks.forEach((cb) => cb());\n }\n\n private getInitialValue(): string | undefined {\n return (\n this.context.settings?.get(\"search\", { autosave: this.autosave })\n ?.value ?? this.defaultValue\n );\n }\n\n public updateInitialState(initialState: InitialTableState): void {\n initialState.globalFilter = this.initialValue;\n }\n\n public setValue(value: SearchValue): void {\n const table = this.context.dataTable;\n\n if (value === undefined || value.trim() === \"\") {\n /*\n * Only when something was actually set: clearing an empty search would\n * otherwise reset the loader and refetch on every keystroke that leaves\n * the field empty.\n */\n if (table.getState().globalFilter) {\n table.setGlobalFilter(undefined);\n this.callOnUpdateCallbacks();\n }\n } else {\n table.setGlobalFilter(value);\n this.callOnUpdateCallbacks();\n }\n\n this.context.settings?.store(\n \"search\",\n { value },\n { autosave: this.autosave },\n );\n }\n\n public clear(): void {\n this.setValue(undefined);\n }\n\n public onUpdated(cb: () => unknown): void {\n this.onUpdateCallbacks.add(cb);\n }\n}\n\nexport default ListSearch;\n"],"mappings":";;;;;;;;;;AAWA,IAAa,aAAb,MAA2B;CACzB;CACA,oCAAqC,IAAI,IAAmB;CAC5D;CACA;CACA;CAEA,YAAmB,SAA8B,OAAwB;EACvE,MAAM,EACJ,WAAW,QAAQ,kBAAkB,QAAQ,YAAY,OACzD,iBACE;EAEJ,KAAK,WAAW;EAChB,KAAK,UAAU;EACf,KAAK,eAAe;EACpB,KAAK,eAAe,KAAK,gBAAgB;CAC3C;CAEA,IAAW,QAAqB;EAC9B,OAAO,KAAK,QAAQ,UAAU,SAAS,CAAC,CAAC;CAC3C;CAEA,IAAW,QAAiB;EAC1B,OAAO,KAAK,UAAU,KAAA;CACxB;CAEA,wBAAsC;EACpC,KAAK,kBAAkB,SAAS,OAAO,GAAG,CAAC;CAC7C;CAEA,kBAA8C;EAC5C,OACE,KAAK,QAAQ,UAAU,IAAI,UAAU,EAAE,UAAU,KAAK,SAAS,CAAC,CAAC,EAC7D,SAAS,KAAK;CAEtB;CAEA,mBAA0B,cAAuC;EAC/D,aAAa,eAAe,KAAK;CACnC;CAEA,SAAgB,OAA0B;EACxC,MAAM,QAAQ,KAAK,QAAQ;EAE3B,IAAI,UAAU,KAAA,KAAa,MAAM,KAAK,MAAM,IAMtC;OAAA,MAAM,SAAS,CAAC,CAAC,cAAc;IACjC,MAAM,gBAAgB,KAAA,CAAS;IAC/B,KAAK,sBAAsB;GAC7B;SACK;GACL,MAAM,gBAAgB,KAAK;GAC3B,KAAK,sBAAsB;EAC7B;EAEA,KAAK,QAAQ,UAAU,MACrB,UACA,EAAE,MAAM,GACR,EAAE,UAAU,KAAK,SAAS,CAC5B;CACF;CAEA,QAAqB;EACnB,KAAK,SAAS,KAAA,CAAS;CACzB;CAEA,UAAiB,IAAyB;EACxC,KAAK,kBAAkB,IAAI,EAAE;CAC/B;AACF"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
import { getListColumn } from "../ListModelContext.mjs";
|
|
4
|
+
//#region ../components-base/src/list/sorting/ListSorting.ts
|
|
5
|
+
/**
|
|
6
|
+
* One way a list can be sorted — a property, a direction, and whether it is on.
|
|
7
|
+
*
|
|
8
|
+
* Framework-free: the state lives in the TanStack table and the preference in
|
|
9
|
+
* the settings store, and this only translates between them. Which is why it
|
|
10
|
+
* could move here whole, unlike the loader, which had to leave its fetching
|
|
11
|
+
* behind.
|
|
12
|
+
*/
|
|
13
|
+
var ListSorting = class {
|
|
14
|
+
context;
|
|
15
|
+
property;
|
|
16
|
+
name;
|
|
17
|
+
directionName;
|
|
18
|
+
direction;
|
|
19
|
+
initialEnabled;
|
|
20
|
+
customSortingFn;
|
|
21
|
+
autosave;
|
|
22
|
+
constructor(context, shape) {
|
|
23
|
+
const { property, name, directionName, direction = "asc", customSortingFn, autosave = context.settingsDefaults?.sorting?.autosave ?? true } = shape;
|
|
24
|
+
this.autosave = autosave;
|
|
25
|
+
this.context = context;
|
|
26
|
+
this.property = property;
|
|
27
|
+
this.name = name;
|
|
28
|
+
this.directionName = directionName;
|
|
29
|
+
this.direction = direction;
|
|
30
|
+
this.customSortingFn = customSortingFn;
|
|
31
|
+
this.initialEnabled = this.getInitialEnabled(shape);
|
|
32
|
+
}
|
|
33
|
+
getInitialEnabled(shape) {
|
|
34
|
+
if (shape.defaultEnabled === "hidden") return "hidden";
|
|
35
|
+
const storedSorting = this.context.settings?.get("sorting", { autosave: this.autosave });
|
|
36
|
+
return (storedSorting ? storedSorting.property === this.property && storedSorting.direction === this.direction : void 0) ?? shape.defaultEnabled ?? false;
|
|
37
|
+
}
|
|
38
|
+
updateTableColumnDef(def) {
|
|
39
|
+
def.enableSorting = true;
|
|
40
|
+
if (this.customSortingFn) def.sortingFn = this.customSortingFn;
|
|
41
|
+
}
|
|
42
|
+
getReactTableColumnSort() {
|
|
43
|
+
return {
|
|
44
|
+
id: this.property,
|
|
45
|
+
desc: this.direction === "desc"
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
isSorted() {
|
|
49
|
+
return this.getTableColumn().getIsSorted() == this.direction;
|
|
50
|
+
}
|
|
51
|
+
getTableColumn() {
|
|
52
|
+
return getListColumn(this.context.dataTable, this.property);
|
|
53
|
+
}
|
|
54
|
+
enable() {
|
|
55
|
+
this.getTableColumn().toggleSorting(this.direction === "desc", false);
|
|
56
|
+
this.context.settings?.store("sorting", {
|
|
57
|
+
property: this.property,
|
|
58
|
+
direction: this.direction
|
|
59
|
+
}, { autosave: this.autosave });
|
|
60
|
+
}
|
|
61
|
+
clear() {
|
|
62
|
+
this.getTableColumn().clearSorting();
|
|
63
|
+
}
|
|
64
|
+
get id() {
|
|
65
|
+
return `${this.getTableColumn().id}:${this.direction}`;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* The next sorting state, with every hidden sorting still in it.
|
|
70
|
+
*
|
|
71
|
+
* A hidden sorting is not a user choice — it is how the list is ordered when
|
|
72
|
+
* nothing else says otherwise, so a click on a visible sorting must not drop
|
|
73
|
+
* it. The table's own updater knows nothing about that distinction.
|
|
74
|
+
*/
|
|
75
|
+
var mergeHiddenSorting = (sortings, next) => {
|
|
76
|
+
return [...sortings.filter((s) => s.initialEnabled === "hidden" && !next.some((existing) => existing.id === s.property)).map((s) => s.getReactTableColumnSort()), ...next];
|
|
77
|
+
};
|
|
78
|
+
//#endregion
|
|
79
|
+
export { ListSorting, ListSorting as default, mergeHiddenSorting };
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=ListSorting.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListSorting.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/sorting/ListSorting.ts"],"sourcesContent":["import { getListColumn, type ListModelContext } from \"../ListModelContext\";\nimport type { PropertyName } from \"../types\";\nimport type { ListSortingShape, SortingDefaultMode, SortingFn } from \"./types\";\nimport type {\n Column,\n ColumnDef,\n ColumnSort,\n SortDirection,\n} from \"@tanstack/table-core\";\n\n/**\n * One way a list can be sorted — a property, a direction, and whether it is on.\n *\n * Framework-free: the state lives in the TanStack table and the preference in\n * the settings store, and this only translates between them. Which is why it\n * could move here whole, unlike the loader, which had to leave its fetching\n * behind.\n */\nexport class ListSorting<T> {\n public readonly context: ListModelContext<T>;\n public readonly property: PropertyName<T>;\n public readonly name?: string;\n public readonly directionName?: string;\n public readonly direction: SortDirection;\n public readonly initialEnabled: SortingDefaultMode;\n public readonly customSortingFn?: SortingFn<T>;\n public readonly autosave: boolean;\n\n public constructor(context: ListModelContext<T>, shape: ListSortingShape<T>) {\n const {\n property,\n name,\n directionName,\n direction = \"asc\",\n customSortingFn,\n autosave = context.settingsDefaults?.sorting?.autosave ?? true,\n } = shape;\n\n this.autosave = autosave;\n this.context = context;\n this.property = property;\n this.name = name;\n this.directionName = directionName;\n this.direction = direction;\n this.customSortingFn = customSortingFn;\n this.initialEnabled = this.getInitialEnabled(shape);\n }\n\n private getInitialEnabled(shape: ListSortingShape<T>): SortingDefaultMode {\n if (shape.defaultEnabled === \"hidden\") {\n return \"hidden\";\n }\n\n const storedSorting = this.context.settings?.get(\"sorting\", {\n autosave: this.autosave,\n });\n\n const storedEnabled = storedSorting\n ? storedSorting.property === this.property &&\n storedSorting.direction === this.direction\n : undefined;\n\n return storedEnabled ?? shape.defaultEnabled ?? false;\n }\n\n public updateTableColumnDef(def: ColumnDef<T>): void {\n def.enableSorting = true;\n if (this.customSortingFn) {\n def.sortingFn = this.customSortingFn;\n }\n }\n\n public getReactTableColumnSort(): ColumnSort {\n return {\n id: this.property as string,\n desc: this.direction === \"desc\",\n };\n }\n\n public isSorted(): boolean {\n return this.getTableColumn().getIsSorted() == this.direction;\n }\n\n public getTableColumn(): Column<T> {\n return getListColumn(this.context.dataTable, this.property);\n }\n\n public enable(): void {\n this.getTableColumn().toggleSorting(this.direction === \"desc\", false);\n\n this.context.settings?.store(\n \"sorting\",\n {\n property: this.property as string,\n direction: this.direction,\n },\n {\n autosave: this.autosave,\n },\n );\n }\n\n public clear(): void {\n this.getTableColumn().clearSorting();\n }\n\n public get id(): string {\n return `${this.getTableColumn().id}:${this.direction}`;\n }\n}\n\nexport default ListSorting;\n\n/**\n * The next sorting state, with every hidden sorting still in it.\n *\n * A hidden sorting is not a user choice — it is how the list is ordered when\n * nothing else says otherwise, so a click on a visible sorting must not drop\n * it. The table's own updater knows nothing about that distinction.\n */\nexport const mergeHiddenSorting = <T>(\n sortings: ListSorting<T>[],\n next: ColumnSort[],\n): ColumnSort[] => {\n const hidden = sortings\n .filter(\n (s) =>\n s.initialEnabled === \"hidden\" &&\n !next.some((existing) => existing.id === s.property),\n )\n .map((s) => s.getReactTableColumnSort());\n\n return [...hidden, ...next];\n};\n"],"mappings":";;;;;;;;;;;;AAkBA,IAAa,cAAb,MAA4B;CAC1B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAmB,SAA8B,OAA4B;EAC3E,MAAM,EACJ,UACA,MACA,eACA,YAAY,OACZ,iBACA,WAAW,QAAQ,kBAAkB,SAAS,YAAY,SACxD;EAEJ,KAAK,WAAW;EAChB,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,OAAO;EACZ,KAAK,gBAAgB;EACrB,KAAK,YAAY;EACjB,KAAK,kBAAkB;EACvB,KAAK,iBAAiB,KAAK,kBAAkB,KAAK;CACpD;CAEA,kBAA0B,OAAgD;EACxE,IAAI,MAAM,mBAAmB,UAC3B,OAAO;EAGT,MAAM,gBAAgB,KAAK,QAAQ,UAAU,IAAI,WAAW,EAC1D,UAAU,KAAK,SACjB,CAAC;EAOD,QALsB,gBAClB,cAAc,aAAa,KAAK,YAChC,cAAc,cAAc,KAAK,YACjC,KAAA,MAEoB,MAAM,kBAAkB;CAClD;CAEA,qBAA4B,KAAyB;EACnD,IAAI,gBAAgB;EACpB,IAAI,KAAK,iBACP,IAAI,YAAY,KAAK;CAEzB;CAEA,0BAA6C;EAC3C,OAAO;GACL,IAAI,KAAK;GACT,MAAM,KAAK,cAAc;EAC3B;CACF;CAEA,WAA2B;EACzB,OAAO,KAAK,eAAe,CAAC,CAAC,YAAY,KAAK,KAAK;CACrD;CAEA,iBAAmC;EACjC,OAAO,cAAc,KAAK,QAAQ,WAAW,KAAK,QAAQ;CAC5D;CAEA,SAAsB;EACpB,KAAK,eAAe,CAAC,CAAC,cAAc,KAAK,cAAc,QAAQ,KAAK;EAEpE,KAAK,QAAQ,UAAU,MACrB,WACA;GACE,UAAU,KAAK;GACf,WAAW,KAAK;EAClB,GACA,EACE,UAAU,KAAK,SACjB,CACF;CACF;CAEA,QAAqB;EACnB,KAAK,eAAe,CAAC,CAAC,aAAa;CACrC;CAEA,IAAW,KAAa;EACtB,OAAO,GAAG,KAAK,eAAe,CAAC,CAAC,GAAG,GAAG,KAAK;CAC7C;AACF;;;;;;;;AAWA,IAAa,sBACX,UACA,SACiB;CASjB,OAAO,CAAC,GARO,SACZ,QACE,MACC,EAAE,mBAAmB,YACrB,CAAC,KAAK,MAAM,aAAa,SAAS,OAAO,EAAE,QAAQ,CACvD,CAAC,CACA,KAAK,MAAM,EAAE,wBAAwB,CAE7B,GAAQ,GAAG,IAAI;AAC5B"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
/* */
|
|
3
|
+
import { action, makeObservable, observable } from "mobx";
|
|
4
|
+
import { createTable } from "@tanstack/table-core";
|
|
5
|
+
//#region ../components-base/src/list/table/ListTable.ts
|
|
6
|
+
/**
|
|
7
|
+
* A TanStack table whose state is MobX.
|
|
8
|
+
*
|
|
9
|
+
* `useReactTable` is `createTable` plus a `useState` for the table's own state
|
|
10
|
+
* and a `setOptions` on every render that folds that state back in. Only the
|
|
11
|
+
* `useState` is React, and `@tanstack/vue-table` reimplements the same twenty
|
|
12
|
+
* lines against Vue's reactivity — so the state lives here instead and each
|
|
13
|
+
* binding brings its subscription.
|
|
14
|
+
*
|
|
15
|
+
* Options still arrive from the binding on every render, because that is where
|
|
16
|
+
* they come from: the loaded data, the column definitions the list's filters
|
|
17
|
+
* and sortings produce, a controlled piece of state. `setOptions` is therefore
|
|
18
|
+
* called by the binding, and re-applied here whenever the state changes.
|
|
19
|
+
*/
|
|
20
|
+
var ListTable = class {
|
|
21
|
+
table;
|
|
22
|
+
state;
|
|
23
|
+
/**
|
|
24
|
+
* Bumped whenever the state _or_ the options changed.
|
|
25
|
+
*
|
|
26
|
+
* The row models are derived from both, and the options are plain data that
|
|
27
|
+
* arrives from the binding — so a binding observing only `state` keeps
|
|
28
|
+
* rendering the previous rows after new data was handed in. It is the one
|
|
29
|
+
* signal that says "ask the table again".
|
|
30
|
+
*/
|
|
31
|
+
revision = 0;
|
|
32
|
+
options;
|
|
33
|
+
constructor(options) {
|
|
34
|
+
const resolved = {
|
|
35
|
+
state: {},
|
|
36
|
+
onStateChange: () => {},
|
|
37
|
+
renderFallbackValue: null,
|
|
38
|
+
...options
|
|
39
|
+
};
|
|
40
|
+
this.table = createTable(resolved);
|
|
41
|
+
this.state = this.table.initialState;
|
|
42
|
+
this.options = options;
|
|
43
|
+
makeObservable(this, {
|
|
44
|
+
state: observable.ref,
|
|
45
|
+
revision: observable,
|
|
46
|
+
setState: action.bound,
|
|
47
|
+
setOptions: action.bound
|
|
48
|
+
});
|
|
49
|
+
this.applyOptions();
|
|
50
|
+
}
|
|
51
|
+
setState(updater) {
|
|
52
|
+
this.state = typeof updater === "function" ? updater(this.state) : updater;
|
|
53
|
+
this.applyOptions();
|
|
54
|
+
}
|
|
55
|
+
/** Called by the binding on every render, with that render's options. */
|
|
56
|
+
setOptions(options) {
|
|
57
|
+
this.options = options;
|
|
58
|
+
this.applyOptions();
|
|
59
|
+
}
|
|
60
|
+
applyOptions() {
|
|
61
|
+
const options = this.options;
|
|
62
|
+
this.revision += 1;
|
|
63
|
+
this.table.setOptions((prev) => ({
|
|
64
|
+
...prev,
|
|
65
|
+
...options,
|
|
66
|
+
state: {
|
|
67
|
+
...this.state,
|
|
68
|
+
...options.state
|
|
69
|
+
},
|
|
70
|
+
onStateChange: (updater) => {
|
|
71
|
+
this.setState(updater);
|
|
72
|
+
options.onStateChange?.(updater);
|
|
73
|
+
}
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
//#endregion
|
|
78
|
+
export { ListTable };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=ListTable.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ListTable.mjs","names":[],"sources":["../../../../../../../../components-base/src/list/table/ListTable.ts"],"sourcesContent":["import { action, makeObservable, observable } from \"mobx\";\nimport { createTable } from \"@tanstack/table-core\";\nimport type {\n RowData,\n Table,\n TableOptions,\n TableOptionsResolved,\n TableState,\n Updater,\n} from \"@tanstack/table-core\";\n\n/**\n * A TanStack table whose state is MobX.\n *\n * `useReactTable` is `createTable` plus a `useState` for the table's own state\n * and a `setOptions` on every render that folds that state back in. Only the\n * `useState` is React, and `@tanstack/vue-table` reimplements the same twenty\n * lines against Vue's reactivity — so the state lives here instead and each\n * binding brings its subscription.\n *\n * Options still arrive from the binding on every render, because that is where\n * they come from: the loaded data, the column definitions the list's filters\n * and sortings produce, a controlled piece of state. `setOptions` is therefore\n * called by the binding, and re-applied here whenever the state changes.\n */\nexport class ListTable<T extends RowData> {\n public readonly table: Table<T>;\n public state: TableState;\n /**\n * Bumped whenever the state _or_ the options changed.\n *\n * The row models are derived from both, and the options are plain data that\n * arrives from the binding — so a binding observing only `state` keeps\n * rendering the previous rows after new data was handed in. It is the one\n * signal that says \"ask the table again\".\n */\n public revision = 0;\n private options: TableOptions<T>;\n\n public constructor(options: TableOptions<T>) {\n const resolved: TableOptionsResolved<T> = {\n state: {},\n onStateChange: () => {\n // replaced by `applyOptions` below\n },\n renderFallbackValue: null,\n ...options,\n };\n\n this.table = createTable<T>(resolved);\n this.state = this.table.initialState;\n this.options = options;\n\n makeObservable(this, {\n state: observable.ref,\n revision: observable,\n setState: action.bound,\n setOptions: action.bound,\n });\n\n this.applyOptions();\n }\n\n public setState(updater: Updater<TableState>): void {\n this.state = typeof updater === \"function\" ? updater(this.state) : updater;\n this.applyOptions();\n }\n\n /** Called by the binding on every render, with that render's options. */\n public setOptions(options: TableOptions<T>): void {\n this.options = options;\n this.applyOptions();\n }\n\n private applyOptions(): void {\n const options = this.options;\n this.revision += 1;\n\n this.table.setOptions((prev) => ({\n ...prev,\n ...options,\n state: {\n ...this.state,\n ...options.state,\n },\n onStateChange: (updater) => {\n this.setState(updater);\n options.onStateChange?.(updater);\n },\n }));\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAyBA,IAAa,YAAb,MAA0C;CACxC;CACA;;;;;;;;;CASA,WAAkB;CAClB;CAEA,YAAmB,SAA0B;EAC3C,MAAM,WAAoC;GACxC,OAAO,CAAC;GACR,qBAAqB,CAErB;GACA,qBAAqB;GACrB,GAAG;EACL;EAEA,KAAK,QAAQ,YAAe,QAAQ;EACpC,KAAK,QAAQ,KAAK,MAAM;EACxB,KAAK,UAAU;EAEf,eAAe,MAAM;GACnB,OAAO,WAAW;GAClB,UAAU;GACV,UAAU,OAAO;GACjB,YAAY,OAAO;EACrB,CAAC;EAED,KAAK,aAAa;CACpB;CAEA,SAAgB,SAAoC;EAClD,KAAK,QAAQ,OAAO,YAAY,aAAa,QAAQ,KAAK,KAAK,IAAI;EACnE,KAAK,aAAa;CACpB;;CAGA,WAAkB,SAAgC;EAChD,KAAK,UAAU;EACf,KAAK,aAAa;CACpB;CAEA,eAA6B;EAC3B,MAAM,UAAU,KAAK;EACrB,KAAK,YAAY;EAEjB,KAAK,MAAM,YAAY,UAAU;GAC/B,GAAG;GACH,GAAG;GACH,OAAO;IACL,GAAG,KAAK;IACR,GAAG,QAAQ;GACb;GACA,gBAAgB,YAAY;IAC1B,KAAK,SAAS,OAAO;IACrB,QAAQ,gBAAgB,OAAO;GACjC;EACF,EAAE;CACJ;AACF"}
|