@mittwald/flow-react-components 1.6.0-next.42 → 1.6.0-next.44

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.
Files changed (27) hide show
  1. package/dist/assets/component-index.json +1 -1
  2. package/dist/assets/doc-properties.json +1 -1
  3. package/dist/js/packages/components/src/components/List/components/Table/Table.mjs +21 -1
  4. package/dist/js/packages/components/src/components/List/components/Table/Table.mjs.map +1 -1
  5. package/dist/js/packages/components/src/components/List/model/table/Table.mjs +11 -1
  6. package/dist/js/packages/components/src/components/List/model/table/Table.mjs.map +1 -1
  7. package/dist/js/packages/components/src/components/List/model/table/TableBody.mjs +5 -1
  8. package/dist/js/packages/components/src/components/List/model/table/TableBody.mjs.map +1 -1
  9. package/dist/js/packages/components/src/components/List/model/table/TableColumn.mjs +4 -0
  10. package/dist/js/packages/components/src/components/List/model/table/TableColumn.mjs.map +1 -1
  11. package/dist/js/packages/components/src/components/List/model/table/TableHeader.mjs +8 -1
  12. package/dist/js/packages/components/src/components/List/model/table/TableHeader.mjs.map +1 -1
  13. package/dist/js/packages/components/src/lib/object/isPlainObjectDeepEqual.mjs +29 -0
  14. package/dist/js/packages/components/src/lib/object/isPlainObjectDeepEqual.mjs.map +1 -0
  15. package/dist/js/packages/components/src/lib/propsContext/components/ClearPropsContext.mjs +2 -1
  16. package/dist/js/packages/components/src/lib/propsContext/components/ClearPropsContext.mjs.map +1 -1
  17. package/dist/js/packages/components/src/lib/propsContext/components/PropsContextProvider.mjs +14 -3
  18. package/dist/js/packages/components/src/lib/propsContext/components/PropsContextProvider.mjs.map +1 -1
  19. package/dist/js/packages/components-base/src/list/loading/ListLoaderState.mjs +10 -5
  20. package/dist/js/packages/components-base/src/list/loading/ListLoaderState.mjs.map +1 -1
  21. package/dist/types/_bundled/flow-components-base/list/loading/ListLoaderState.d.ts +5 -0
  22. package/dist/types/components/List/model/table/Table.d.ts +7 -0
  23. package/dist/types/components/List/model/table/TableBody.d.ts +1 -0
  24. package/dist/types/components/List/model/table/TableColumn.d.ts +1 -0
  25. package/dist/types/components/List/model/table/TableHeader.d.ts +1 -0
  26. package/dist/types/lib/object/isPlainObjectDeepEqual.d.ts +8 -0
  27. package/package.json +10 -10
@@ -11,10 +11,13 @@ import TableColumnView from "../../../../views/TableColumnView.mjs";
11
11
  import { TableBodyLoadingView } from "./components/TableBodyLoadingView.mjs";
12
12
  import { Row } from "./components/Row.mjs";
13
13
  import clsx from "clsx";
14
+ import { useRef } from "react";
14
15
  import { jsx, jsxs } from "react/jsx-runtime";
16
+ import { isShallowEqual } from "remeda";
15
17
  //#region src/components/List/components/Table/Table.tsx
16
18
  var Table = () => {
17
19
  const list = useList();
20
+ const lastRender = useRef(null);
18
21
  const table = list.table;
19
22
  const listIsEmpty = list.useIsEmpty();
20
23
  const isLoading = list.loader.useIsLoading();
@@ -24,6 +27,17 @@ var Table = () => {
24
27
  const onAction = useLatestOnAction(list);
25
28
  if (!table || listIsEmpty) return null;
26
29
  const tableClassName = clsx(Table_module_default.table, isLoading && !(list.infiniteScroll && isLoadingMore) && Table_module_default.isLoading, table.componentProps.className);
30
+ const inputs = [
31
+ tableClassName,
32
+ isInitiallyLoading,
33
+ isLoading,
34
+ onAction,
35
+ triggerIndex,
36
+ triggerRef,
37
+ ...list.items.entries.flatMap((item) => [item.id, item.data])
38
+ ];
39
+ const last = lastRender.current;
40
+ if (last && isShallowEqual(last.inputs, inputs) && table.rendersSameAs(last.table)) return last.element;
27
41
  const rows = list.items.entries.map((item, index) => /* @__PURE__ */ jsx(Row, {
28
42
  id: item.id,
29
43
  data: item.data,
@@ -31,7 +45,7 @@ var Table = () => {
31
45
  onAction,
32
46
  triggerRef: index === triggerIndex ? triggerRef : void 0
33
47
  }, item.id));
34
- return /* @__PURE__ */ jsxs(TableView, {
48
+ const element = /* @__PURE__ */ jsxs(TableView, {
35
49
  ...list.componentProps,
36
50
  ...table.componentProps,
37
51
  className: tableClassName,
@@ -45,6 +59,12 @@ var Table = () => {
45
59
  children: isInitiallyLoading ? /* @__PURE__ */ jsx(TableBodyLoadingView, {}) : rows
46
60
  })]
47
61
  });
62
+ lastRender.current = {
63
+ inputs,
64
+ table,
65
+ element
66
+ };
67
+ return element;
48
68
  };
49
69
  //#endregion
50
70
  export { Table };
@@ -1 +1 @@
1
- {"version":3,"file":"Table.mjs","names":["styles"],"sources":["../../../../../../../../../src/components/List/components/Table/Table.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useList } from \"@/components/List\";\nimport styles from \"./Table.module.scss\";\nimport clsx from \"clsx\";\nimport TableView from \"@/views/TableView\";\nimport TableHeaderView from \"@/views/TableHeaderView\";\nimport TableBodyView from \"@/views/TableBodyView\";\nimport TableColumnView from \"@/views/TableColumnView\";\nimport { TableBodyLoadingView } from \"@/components/List/components/Table/components/TableBodyLoadingView\";\nimport { useInfiniteScrollTrigger } from \"@/components/List/hooks/useInfiniteScrollTrigger\";\nimport { useLatestOnAction } from \"@/components/List/hooks/useLatestOnAction\";\nimport { Row } from \"@/components/List/components/Table/components/Row\";\n\nexport const Table: FC = () => {\n const list = useList();\n const table = list.table;\n const listIsEmpty = list.useIsEmpty();\n\n const isLoading = list.loader.useIsLoading();\n const isInitiallyLoading = list.loader.useIsInitiallyLoading();\n const isLoadingMore = list.loader.useIsLoadingMore();\n\n const { triggerRef, triggerIndex } = useInfiniteScrollTrigger();\n const onAction = useLatestOnAction(list);\n\n if (!table || listIsEmpty) {\n return null;\n }\n\n const tableClassName = clsx(\n styles.table,\n isLoading && !(list.infiniteScroll && isLoadingMore) && styles.isLoading,\n table.componentProps.className,\n );\n\n const rows = list.items.entries.map((item, index) => (\n <Row\n key={item.id}\n id={item.id}\n data={item.data}\n list={list}\n onAction={onAction}\n triggerRef={index === triggerIndex ? triggerRef : undefined}\n />\n ));\n\n return (\n <TableView\n {...list.componentProps}\n {...table.componentProps}\n className={tableClassName}\n aria-hidden={isInitiallyLoading}\n aria-busy={isLoading}\n >\n <TableHeaderView {...table.header.componentProps}>\n {table.header.columns.map((col, i) => (\n <TableColumnView key={i} {...col.componentProps} />\n ))}\n </TableHeaderView>\n <TableBodyView {...table.body.componentProps}>\n {isInitiallyLoading ? <TableBodyLoadingView /> : rows}\n </TableBodyView>\n </TableView>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;AAaA,IAAa,cAAkB;CAC7B,MAAM,OAAO,QAAQ;CACrB,MAAM,QAAQ,KAAK;CACnB,MAAM,cAAc,KAAK,WAAW;CAEpC,MAAM,YAAY,KAAK,OAAO,aAAa;CAC3C,MAAM,qBAAqB,KAAK,OAAO,sBAAsB;CAC7D,MAAM,gBAAgB,KAAK,OAAO,iBAAiB;CAEnD,MAAM,EAAE,YAAY,iBAAiB,yBAAyB;CAC9D,MAAM,WAAW,kBAAkB,IAAI;CAEvC,IAAI,CAAC,SAAS,aACZ,OAAO;CAGT,MAAM,iBAAiB,KACrBA,qBAAO,OACP,aAAa,EAAE,KAAK,kBAAkB,kBAAkBA,qBAAO,WAC/D,MAAM,eAAe,SACvB;CAEA,MAAM,OAAO,KAAK,MAAM,QAAQ,KAAK,MAAM,UACzC,oBAAC,KAAD;EAEE,IAAI,KAAK;EACT,MAAM,KAAK;EACL;EACI;EACV,YAAY,UAAU,eAAe,aAAa,KAAA;CACnD,GANM,KAAK,EAMX,CACF;CAED,OACE,qBAAC,WAAD;EACE,GAAI,KAAK;EACT,GAAI,MAAM;EACV,WAAW;EACX,eAAa;EACb,aAAW;EALb,UAAA,CAOE,oBAAC,iBAAD;GAAiB,GAAI,MAAM,OAAO;GAC/B,UAAA,MAAM,OAAO,QAAQ,KAAK,KAAK,MAC9B,oBAAC,iBAAD,EAAyB,GAAI,IAAI,eAAiB,GAA5B,CAA4B,CACnD;EACc,CAAA,GACjB,oBAAC,eAAD;GAAe,GAAI,MAAM,KAAK;GAC3B,UAAA,qBAAqB,oBAAC,sBAAD,CAAuB,CAAA,IAAI;EACpC,CAAA,CACN;;AAEf"}
1
+ {"version":3,"file":"Table.mjs","names":["styles"],"sources":["../../../../../../../../../src/components/List/components/Table/Table.tsx"],"sourcesContent":["import type { FC, ReactElement } from \"react\";\nimport { useRef } from \"react\";\nimport { isShallowEqual } from \"remeda\";\nimport { useList } from \"@/components/List\";\nimport styles from \"./Table.module.scss\";\nimport clsx from \"clsx\";\nimport TableView from \"@/views/TableView\";\nimport TableHeaderView from \"@/views/TableHeaderView\";\nimport TableBodyView from \"@/views/TableBodyView\";\nimport TableColumnView from \"@/views/TableColumnView\";\nimport { TableBodyLoadingView } from \"@/components/List/components/Table/components/TableBodyLoadingView\";\nimport { useInfiniteScrollTrigger } from \"@/components/List/hooks/useInfiniteScrollTrigger\";\nimport { useLatestOnAction } from \"@/components/List/hooks/useLatestOnAction\";\nimport { Row } from \"@/components/List/components/Table/components/Row\";\nimport type { Table as TableModel } from \"@/components/List/model/table/Table\";\n\ninterface LastRender {\n inputs: unknown[];\n table: TableModel<never>;\n element: ReactElement;\n}\n\nexport const Table: FC = () => {\n const list = useList();\n const lastRender = useRef<LastRender | null>(null);\n const table = list.table;\n const listIsEmpty = list.useIsEmpty();\n\n const isLoading = list.loader.useIsLoading();\n const isInitiallyLoading = list.loader.useIsInitiallyLoading();\n const isLoadingMore = list.loader.useIsLoadingMore();\n\n const { triggerRef, triggerIndex } = useInfiniteScrollTrigger();\n const onAction = useLatestOnAction(list);\n\n if (!table || listIsEmpty) {\n return null;\n }\n\n const tableClassName = clsx(\n styles.table,\n isLoading && !(list.infiniteScroll && isLoadingMore) && styles.isLoading,\n table.componentProps.className,\n );\n\n /*\n * react-aria re-renders every row and cell whenever its table renders, its\n * row memo or not (adobe/react-spectrum#10759). While nothing the table\n * shows has changed, it gets the element of the last render, so it does not\n * render at all.\n */\n const inputs = [\n tableClassName,\n isInitiallyLoading,\n isLoading,\n onAction,\n triggerIndex,\n triggerRef,\n ...list.items.entries.flatMap((item) => [item.id, item.data]),\n ];\n const last = lastRender.current;\n if (\n last &&\n isShallowEqual(last.inputs, inputs) &&\n table.rendersSameAs(last.table)\n ) {\n return last.element;\n }\n\n const rows = list.items.entries.map((item, index) => (\n <Row\n key={item.id}\n id={item.id}\n data={item.data}\n list={list}\n onAction={onAction}\n triggerRef={index === triggerIndex ? triggerRef : undefined}\n />\n ));\n\n const element = (\n <TableView\n {...list.componentProps}\n {...table.componentProps}\n className={tableClassName}\n aria-hidden={isInitiallyLoading}\n aria-busy={isLoading}\n >\n <TableHeaderView {...table.header.componentProps}>\n {table.header.columns.map((col, i) => (\n <TableColumnView key={i} {...col.componentProps} />\n ))}\n </TableHeaderView>\n <TableBodyView {...table.body.componentProps}>\n {isInitiallyLoading ? <TableBodyLoadingView /> : rows}\n </TableBodyView>\n </TableView>\n );\n lastRender.current = { inputs, table, element };\n return element;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAsBA,IAAa,cAAkB;CAC7B,MAAM,OAAO,QAAQ;CACrB,MAAM,aAAa,OAA0B,IAAI;CACjD,MAAM,QAAQ,KAAK;CACnB,MAAM,cAAc,KAAK,WAAW;CAEpC,MAAM,YAAY,KAAK,OAAO,aAAa;CAC3C,MAAM,qBAAqB,KAAK,OAAO,sBAAsB;CAC7D,MAAM,gBAAgB,KAAK,OAAO,iBAAiB;CAEnD,MAAM,EAAE,YAAY,iBAAiB,yBAAyB;CAC9D,MAAM,WAAW,kBAAkB,IAAI;CAEvC,IAAI,CAAC,SAAS,aACZ,OAAO;CAGT,MAAM,iBAAiB,KACrBA,qBAAO,OACP,aAAa,EAAE,KAAK,kBAAkB,kBAAkBA,qBAAO,WAC/D,MAAM,eAAe,SACvB;CAQA,MAAM,SAAS;EACb;EACA;EACA;EACA;EACA;EACA;EACA,GAAG,KAAK,MAAM,QAAQ,SAAS,SAAS,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC;CAC9D;CACA,MAAM,OAAO,WAAW;CACxB,IACE,QACA,eAAe,KAAK,QAAQ,MAAM,KAClC,MAAM,cAAc,KAAK,KAAK,GAE9B,OAAO,KAAK;CAGd,MAAM,OAAO,KAAK,MAAM,QAAQ,KAAK,MAAM,UACzC,oBAAC,KAAD;EAEE,IAAI,KAAK;EACT,MAAM,KAAK;EACL;EACI;EACV,YAAY,UAAU,eAAe,aAAa,KAAA;CACnD,GANM,KAAK,EAMX,CACF;CAED,MAAM,UACJ,qBAAC,WAAD;EACE,GAAI,KAAK;EACT,GAAI,MAAM;EACV,WAAW;EACX,eAAa;EACb,aAAW;EALb,UAAA,CAOE,oBAAC,iBAAD;GAAiB,GAAI,MAAM,OAAO;GAC/B,UAAA,MAAM,OAAO,QAAQ,KAAK,KAAK,MAC9B,oBAAC,iBAAD,EAAyB,GAAI,IAAI,eAAiB,GAA5B,CAA4B,CACnD;EACc,CAAA,GACjB,oBAAC,eAAD;GAAe,GAAI,MAAM,KAAK;GAC3B,UAAA,qBAAqB,oBAAC,sBAAD,CAAuB,CAAA,IAAI;EACpC,CAAA,CACN;;CAEb,WAAW,UAAU;EAAE;EAAQ;EAAO;CAAQ;CAC9C,OAAO;AACT"}
@@ -2,6 +2,7 @@
2
2
  /* */
3
3
  import { TableHeader } from "./TableHeader.mjs";
4
4
  import { TableBody } from "./TableBody.mjs";
5
+ import { isShallowEqual } from "remeda";
5
6
  //#region src/components/List/model/table/Table.ts
6
7
  var Table = class {
7
8
  list;
@@ -9,12 +10,21 @@ var Table = class {
9
10
  body;
10
11
  componentProps;
11
12
  constructor(list, shape = {}) {
12
- const { header, body, ...restProps } = shape;
13
+ const { header, body, children: ignoredChildren, ...restProps } = shape;
13
14
  this.list = list;
14
15
  this.header = new TableHeader(this, header);
15
16
  this.body = new TableBody(this, body);
16
17
  this.componentProps = restProps;
17
18
  }
19
+ /**
20
+ * Whether `other` would render the table identically, rows aside. Like
21
+ * `TableRow.rendersSameAs`, it only holds for rows that declare
22
+ * `renderDependencies`. Props count by identity, one by one, including the
23
+ * list's own.
24
+ */
25
+ rendersSameAs(other) {
26
+ return !!other && isShallowEqual(this.list.componentProps, other.list.componentProps) && isShallowEqual(this.componentProps, other.componentProps) && this.header.rendersSameAs(other.header) && this.body.rendersSameAs(other.body);
27
+ }
18
28
  };
19
29
  //#endregion
20
30
  export { Table };
@@ -1 +1 @@
1
- {"version":3,"file":"Table.mjs","names":[],"sources":["../../../../../../../../../src/components/List/model/table/Table.ts"],"sourcesContent":["import type {\n TableShape,\n TableSupportedComponentProps,\n} from \"@/components/List/model/table/types\";\nimport type List from \"@/components/List/model/List\";\nimport { TableHeader } from \"@/components/List/model/table/TableHeader\";\nimport { TableBody } from \"@/components/List/model/table/TableBody\";\n\nexport class Table<T> {\n public readonly list: List<T>;\n public readonly header: TableHeader<T>;\n public readonly body: TableBody<T>;\n public readonly componentProps: TableSupportedComponentProps;\n\n public constructor(list: List<T>, shape: TableShape<T> = {}) {\n const { header, body, ...restProps } = shape;\n this.list = list;\n this.header = new TableHeader(this, header);\n this.body = new TableBody(this, body);\n this.componentProps = restProps;\n }\n}\n"],"mappings":";;;;;AAQA,IAAa,QAAb,MAAsB;CACpB;CACA;CACA;CACA;CAEA,YAAmB,MAAe,QAAuB,CAAC,GAAG;EAC3D,MAAM,EAAE,QAAQ,MAAM,GAAG,cAAc;EACvC,KAAK,OAAO;EACZ,KAAK,SAAS,IAAI,YAAY,MAAM,MAAM;EAC1C,KAAK,OAAO,IAAI,UAAU,MAAM,IAAI;EACpC,KAAK,iBAAiB;CACxB;AACF"}
1
+ {"version":3,"file":"Table.mjs","names":[],"sources":["../../../../../../../../../src/components/List/model/table/Table.ts"],"sourcesContent":["import type {\n TableShape,\n TableSupportedComponentProps,\n} from \"@/components/List/model/table/types\";\nimport type List from \"@/components/List/model/List\";\nimport { TableHeader } from \"@/components/List/model/table/TableHeader\";\nimport { TableBody } from \"@/components/List/model/table/TableBody\";\nimport { isShallowEqual } from \"remeda\";\n\nexport class Table<T> {\n public readonly list: List<T>;\n public readonly header: TableHeader<T>;\n public readonly body: TableBody<T>;\n public readonly componentProps: TableSupportedComponentProps;\n\n public constructor(list: List<T>, shape: TableShape<T> = {}) {\n // `children` holds the header and body elements, which arrive as `header`\n // and `body`.\n const { header, body, children: ignoredChildren, ...restProps } = shape;\n this.list = list;\n this.header = new TableHeader(this, header);\n this.body = new TableBody(this, body);\n this.componentProps = restProps;\n }\n\n /**\n * Whether `other` would render the table identically, rows aside. Like\n * `TableRow.rendersSameAs`, it only holds for rows that declare\n * `renderDependencies`. Props count by identity, one by one, including the\n * list's own.\n */\n public rendersSameAs(other?: Table<T>): boolean {\n return (\n !!other &&\n isShallowEqual(this.list.componentProps, other.list.componentProps) &&\n isShallowEqual(this.componentProps, other.componentProps) &&\n this.header.rendersSameAs(other.header) &&\n this.body.rendersSameAs(other.body)\n );\n }\n}\n"],"mappings":";;;;;;AASA,IAAa,QAAb,MAAsB;CACpB;CACA;CACA;CACA;CAEA,YAAmB,MAAe,QAAuB,CAAC,GAAG;EAG3D,MAAM,EAAE,QAAQ,MAAM,UAAU,iBAAiB,GAAG,cAAc;EAClE,KAAK,OAAO;EACZ,KAAK,SAAS,IAAI,YAAY,MAAM,MAAM;EAC1C,KAAK,OAAO,IAAI,UAAU,MAAM,IAAI;EACpC,KAAK,iBAAiB;CACxB;;;;;;;CAQA,cAAqB,OAA2B;EAC9C,OACE,CAAC,CAAC,SACF,eAAe,KAAK,KAAK,gBAAgB,MAAM,KAAK,cAAc,KAClE,eAAe,KAAK,gBAAgB,MAAM,cAAc,KACxD,KAAK,OAAO,cAAc,MAAM,MAAM,KACtC,KAAK,KAAK,cAAc,MAAM,IAAI;CAEtC;AACF"}
@@ -1,17 +1,21 @@
1
1
  "use client"
2
2
  /* */
3
3
  import { TableRow } from "./TableRow.mjs";
4
+ import { isShallowEqual } from "remeda";
4
5
  //#region src/components/List/model/table/TableBody.ts
5
6
  var TableBody = class {
6
7
  table;
7
8
  componentProps;
8
9
  row;
9
10
  constructor(table, shape = {}) {
10
- const { row, ...rest } = shape;
11
+ const { row, children: ignoredChildren, ...rest } = shape;
11
12
  this.table = table;
12
13
  this.row = new TableRow(this, row);
13
14
  this.componentProps = rest;
14
15
  }
16
+ rendersSameAs(other) {
17
+ return isShallowEqual(this.componentProps, other.componentProps) && this.row.rendersSameAs(other.row);
18
+ }
15
19
  };
16
20
  //#endregion
17
21
  export { TableBody };
@@ -1 +1 @@
1
- {"version":3,"file":"TableBody.mjs","names":[],"sources":["../../../../../../../../../src/components/List/model/table/TableBody.ts"],"sourcesContent":["import type { Table } from \"@/components/List/model/table/Table\";\nimport type {\n TableBodyShape,\n TableBodySupportedComponentProps,\n} from \"@/components/List/model/table/types\";\nimport { TableRow } from \"@/components/List/model/table/TableRow\";\n\nexport class TableBody<T> {\n public readonly table: Table<T>;\n public readonly componentProps: TableBodySupportedComponentProps;\n public readonly row: TableRow<T>;\n\n public constructor(table: Table<T>, shape: TableBodyShape<T> = {}) {\n const { row, ...rest } = shape;\n this.table = table;\n this.row = new TableRow(this, row);\n this.componentProps = rest;\n }\n}\n"],"mappings":";;;;AAOA,IAAa,YAAb,MAA0B;CACxB;CACA;CACA;CAEA,YAAmB,OAAiB,QAA2B,CAAC,GAAG;EACjE,MAAM,EAAE,KAAK,GAAG,SAAS;EACzB,KAAK,QAAQ;EACb,KAAK,MAAM,IAAI,SAAS,MAAM,GAAG;EACjC,KAAK,iBAAiB;CACxB;AACF"}
1
+ {"version":3,"file":"TableBody.mjs","names":[],"sources":["../../../../../../../../../src/components/List/model/table/TableBody.ts"],"sourcesContent":["import type { Table } from \"@/components/List/model/table/Table\";\nimport type {\n TableBodyShape,\n TableBodySupportedComponentProps,\n} from \"@/components/List/model/table/types\";\nimport { TableRow } from \"@/components/List/model/table/TableRow\";\nimport { isShallowEqual } from \"remeda\";\n\nexport class TableBody<T> {\n public readonly table: Table<T>;\n public readonly componentProps: TableBodySupportedComponentProps;\n public readonly row: TableRow<T>;\n\n public constructor(table: Table<T>, shape: TableBodyShape<T> = {}) {\n // `children` holds the row element, which arrives as `row`.\n const { row, children: ignoredChildren, ...rest } = shape;\n this.table = table;\n this.row = new TableRow(this, row);\n this.componentProps = rest;\n }\n\n public rendersSameAs(other: TableBody<T>): boolean {\n return (\n isShallowEqual(this.componentProps, other.componentProps) &&\n this.row.rendersSameAs(other.row)\n );\n }\n}\n"],"mappings":";;;;;AAQA,IAAa,YAAb,MAA0B;CACxB;CACA;CACA;CAEA,YAAmB,OAAiB,QAA2B,CAAC,GAAG;EAEjE,MAAM,EAAE,KAAK,UAAU,iBAAiB,GAAG,SAAS;EACpD,KAAK,QAAQ;EACb,KAAK,MAAM,IAAI,SAAS,MAAM,GAAG;EACjC,KAAK,iBAAiB;CACxB;CAEA,cAAqB,OAA8B;EACjD,OACE,eAAe,KAAK,gBAAgB,MAAM,cAAc,KACxD,KAAK,IAAI,cAAc,MAAM,GAAG;CAEpC;AACF"}
@@ -1,5 +1,6 @@
1
1
  "use client"
2
2
  /* */
3
+ import { isShallowEqual } from "remeda";
3
4
  //#region src/components/List/model/table/TableColumn.ts
4
5
  var TableColumn = class {
5
6
  tableHeader;
@@ -8,6 +9,9 @@ var TableColumn = class {
8
9
  this.tableHeader = tableHeader;
9
10
  this.componentProps = shape;
10
11
  }
12
+ rendersSameAs(other) {
13
+ return isShallowEqual(this.componentProps, other.componentProps);
14
+ }
11
15
  };
12
16
  //#endregion
13
17
  export { TableColumn };
@@ -1 +1 @@
1
- {"version":3,"file":"TableColumn.mjs","names":[],"sources":["../../../../../../../../../src/components/List/model/table/TableColumn.ts"],"sourcesContent":["import type {\n TableColumnShape,\n TableColumnSupportedComponentProps,\n} from \"@/components/List/model/table/types\";\nimport type { TableHeader } from \"@/components/List/model/table/TableHeader\";\n\nexport class TableColumn<T> {\n public readonly tableHeader: TableHeader<T>;\n public readonly componentProps: TableColumnSupportedComponentProps;\n\n public constructor(\n tableHeader: TableHeader<T>,\n shape: TableColumnShape = {},\n ) {\n this.tableHeader = tableHeader;\n this.componentProps = shape;\n }\n}\n"],"mappings":";;;AAMA,IAAa,cAAb,MAA4B;CAC1B;CACA;CAEA,YACE,aACA,QAA0B,CAAC,GAC3B;EACA,KAAK,cAAc;EACnB,KAAK,iBAAiB;CACxB;AACF"}
1
+ {"version":3,"file":"TableColumn.mjs","names":[],"sources":["../../../../../../../../../src/components/List/model/table/TableColumn.ts"],"sourcesContent":["import type {\n TableColumnShape,\n TableColumnSupportedComponentProps,\n} from \"@/components/List/model/table/types\";\nimport type { TableHeader } from \"@/components/List/model/table/TableHeader\";\nimport { isShallowEqual } from \"remeda\";\n\nexport class TableColumn<T> {\n public readonly tableHeader: TableHeader<T>;\n public readonly componentProps: TableColumnSupportedComponentProps;\n\n public constructor(\n tableHeader: TableHeader<T>,\n shape: TableColumnShape = {},\n ) {\n this.tableHeader = tableHeader;\n this.componentProps = shape;\n }\n\n public rendersSameAs(other: TableColumn<T>): boolean {\n return isShallowEqual(this.componentProps, other.componentProps);\n }\n}\n"],"mappings":";;;;AAOA,IAAa,cAAb,MAA4B;CAC1B;CACA;CAEA,YACE,aACA,QAA0B,CAAC,GAC3B;EACA,KAAK,cAAc;EACnB,KAAK,iBAAiB;CACxB;CAEA,cAAqB,OAAgC;EACnD,OAAO,eAAe,KAAK,gBAAgB,MAAM,cAAc;CACjE;AACF"}
@@ -1,17 +1,24 @@
1
1
  "use client"
2
2
  /* */
3
3
  import { TableColumn } from "./TableColumn.mjs";
4
+ import { isShallowEqual } from "remeda";
4
5
  //#region src/components/List/model/table/TableHeader.ts
5
6
  var TableHeader = class {
6
7
  table;
7
8
  componentProps;
8
9
  columns;
9
10
  constructor(table, shape = {}) {
10
- const { columns = [], ...rest } = shape;
11
+ const { columns = [], children: ignoredChildren, ...rest } = shape;
11
12
  this.table = table;
12
13
  this.columns = columns.map((colShape) => new TableColumn(this, colShape));
13
14
  this.componentProps = rest;
14
15
  }
16
+ rendersSameAs(other) {
17
+ return isShallowEqual(this.componentProps, other.componentProps) && this.columns.length === other.columns.length && this.columns.every((column, index) => {
18
+ const otherColumn = other.columns[index];
19
+ return !!otherColumn && column.rendersSameAs(otherColumn);
20
+ });
21
+ }
15
22
  };
16
23
  //#endregion
17
24
  export { TableHeader };
@@ -1 +1 @@
1
- {"version":3,"file":"TableHeader.mjs","names":[],"sources":["../../../../../../../../../src/components/List/model/table/TableHeader.ts"],"sourcesContent":["import type { Table } from \"@/components/List/model/table/Table\";\nimport type {\n TableHeaderShape,\n TableHeaderSupportedComponentProps,\n} from \"@/components/List/model/table/types\";\nimport { TableColumn } from \"@/components/List/model/table/TableColumn\";\n\nexport class TableHeader<T> {\n public readonly table: Table<T>;\n public readonly componentProps: TableHeaderSupportedComponentProps;\n public readonly columns: TableColumn<T>[];\n\n public constructor(table: Table<T>, shape: TableHeaderShape = {}) {\n const { columns = [], ...rest } = shape;\n this.table = table;\n this.columns = columns.map((colShape) => new TableColumn(this, colShape));\n this.componentProps = rest;\n }\n}\n"],"mappings":";;;;AAOA,IAAa,cAAb,MAA4B;CAC1B;CACA;CACA;CAEA,YAAmB,OAAiB,QAA0B,CAAC,GAAG;EAChE,MAAM,EAAE,UAAU,CAAC,GAAG,GAAG,SAAS;EAClC,KAAK,QAAQ;EACb,KAAK,UAAU,QAAQ,KAAK,aAAa,IAAI,YAAY,MAAM,QAAQ,CAAC;EACxE,KAAK,iBAAiB;CACxB;AACF"}
1
+ {"version":3,"file":"TableHeader.mjs","names":[],"sources":["../../../../../../../../../src/components/List/model/table/TableHeader.ts"],"sourcesContent":["import type { Table } from \"@/components/List/model/table/Table\";\nimport type {\n TableHeaderShape,\n TableHeaderSupportedComponentProps,\n} from \"@/components/List/model/table/types\";\nimport { TableColumn } from \"@/components/List/model/table/TableColumn\";\nimport { isShallowEqual } from \"remeda\";\n\nexport class TableHeader<T> {\n public readonly table: Table<T>;\n public readonly componentProps: TableHeaderSupportedComponentProps;\n public readonly columns: TableColumn<T>[];\n\n public constructor(table: Table<T>, shape: TableHeaderShape = {}) {\n // `children` holds the column elements, which arrive as `columns`.\n const { columns = [], children: ignoredChildren, ...rest } = shape;\n this.table = table;\n this.columns = columns.map((colShape) => new TableColumn(this, colShape));\n this.componentProps = rest;\n }\n\n public rendersSameAs(other: TableHeader<T>): boolean {\n return (\n isShallowEqual(this.componentProps, other.componentProps) &&\n this.columns.length === other.columns.length &&\n this.columns.every((column, index) => {\n const otherColumn = other.columns[index];\n return !!otherColumn && column.rendersSameAs(otherColumn);\n })\n );\n }\n}\n"],"mappings":";;;;;AAQA,IAAa,cAAb,MAA4B;CAC1B;CACA;CACA;CAEA,YAAmB,OAAiB,QAA0B,CAAC,GAAG;EAEhE,MAAM,EAAE,UAAU,CAAC,GAAG,UAAU,iBAAiB,GAAG,SAAS;EAC7D,KAAK,QAAQ;EACb,KAAK,UAAU,QAAQ,KAAK,aAAa,IAAI,YAAY,MAAM,QAAQ,CAAC;EACxE,KAAK,iBAAiB;CACxB;CAEA,cAAqB,OAAgC;EACnD,OACE,eAAe,KAAK,gBAAgB,MAAM,cAAc,KACxD,KAAK,QAAQ,WAAW,MAAM,QAAQ,UACtC,KAAK,QAAQ,OAAO,QAAQ,UAAU;GACpC,MAAM,cAAc,MAAM,QAAQ;GAClC,OAAO,CAAC,CAAC,eAAe,OAAO,cAAc,WAAW;EAC1D,CAAC;CAEL;AACF"}
@@ -0,0 +1,29 @@
1
+ "use client"
2
+ /* */
3
+ //#region src/lib/object/isPlainObjectDeepEqual.ts
4
+ var isRefObject = (something) => {
5
+ const keys = Object.keys(something);
6
+ return keys.length === 1 && keys[0] === "current";
7
+ };
8
+ var isPlainObject = (something) => {
9
+ if (typeof something !== "object" || something === null || "$$typeof" in something) return false;
10
+ const prototype = Object.getPrototypeOf(something);
11
+ return (prototype === Object.prototype || prototype === null) && !isRefObject(something);
12
+ };
13
+ /**
14
+ * Compares plain objects recursively and every other value by identity.
15
+ * Functions, arrays, class instances, React elements and ref objects (`{
16
+ * current }`) only count as equal when they are the same value, so a value
17
+ * created during render counts as a change.
18
+ */
19
+ var isPlainObjectDeepEqual = (first, second) => {
20
+ if (Object.is(first, second)) return true;
21
+ if (!isPlainObject(first) || !isPlainObject(second)) return false;
22
+ const firstKeys = Object.keys(first);
23
+ if (firstKeys.length !== Object.keys(second).length) return false;
24
+ return firstKeys.every((key) => Object.hasOwn(second, key) && isPlainObjectDeepEqual(first[key], second[key]));
25
+ };
26
+ //#endregion
27
+ export { isPlainObjectDeepEqual as default, isPlainObjectDeepEqual };
28
+
29
+ //# sourceMappingURL=isPlainObjectDeepEqual.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"isPlainObjectDeepEqual.mjs","names":[],"sources":["../../../../../../../src/lib/object/isPlainObjectDeepEqual.ts"],"sourcesContent":["const isRefObject = (something: object) => {\n const keys = Object.keys(something);\n return keys.length === 1 && keys[0] === \"current\";\n};\n\nconst isPlainObject = (\n something: unknown,\n): something is Record<string, unknown> => {\n if (\n typeof something !== \"object\" ||\n something === null ||\n \"$$typeof\" in something\n ) {\n return false;\n }\n const prototype = Object.getPrototypeOf(something);\n return (\n (prototype === Object.prototype || prototype === null) &&\n !isRefObject(something)\n );\n};\n\n/**\n * Compares plain objects recursively and every other value by identity.\n * Functions, arrays, class instances, React elements and ref objects (`{\n * current }`) only count as equal when they are the same value, so a value\n * created during render counts as a change.\n */\nexport const isPlainObjectDeepEqual = (\n first: unknown,\n second: unknown,\n): boolean => {\n if (Object.is(first, second)) {\n return true;\n }\n\n if (!isPlainObject(first) || !isPlainObject(second)) {\n return false;\n }\n\n const firstKeys = Object.keys(first);\n\n if (firstKeys.length !== Object.keys(second).length) {\n return false;\n }\n\n return firstKeys.every(\n (key) =>\n Object.hasOwn(second, key) &&\n isPlainObjectDeepEqual(first[key], second[key]),\n );\n};\n\nexport default isPlainObjectDeepEqual;\n"],"mappings":";;;AAAA,IAAM,eAAe,cAAsB;CACzC,MAAM,OAAO,OAAO,KAAK,SAAS;CAClC,OAAO,KAAK,WAAW,KAAK,KAAK,OAAO;AAC1C;AAEA,IAAM,iBACJ,cACyC;CACzC,IACE,OAAO,cAAc,YACrB,cAAc,QACd,cAAc,WAEd,OAAO;CAET,MAAM,YAAY,OAAO,eAAe,SAAS;CACjD,QACG,cAAc,OAAO,aAAa,cAAc,SACjD,CAAC,YAAY,SAAS;AAE1B;;;;;;;AAQA,IAAa,0BACX,OACA,WACY;CACZ,IAAI,OAAO,GAAG,OAAO,MAAM,GACzB,OAAO;CAGT,IAAI,CAAC,cAAc,KAAK,KAAK,CAAC,cAAc,MAAM,GAChD,OAAO;CAGT,MAAM,YAAY,OAAO,KAAK,KAAK;CAEnC,IAAI,UAAU,WAAW,OAAO,KAAK,MAAM,CAAC,CAAC,QAC3C,OAAO;CAGT,OAAO,UAAU,OACd,QACC,OAAO,OAAO,QAAQ,GAAG,KACzB,uBAAuB,MAAM,MAAM,OAAO,IAAI,CAClD;AACF"}
@@ -4,11 +4,12 @@ import { PropsContextProvider } from "../propsContext.mjs";
4
4
  import "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  //#region src/lib/propsContext/components/ClearPropsContext.tsx
7
+ var emptyPropsContext = {};
7
8
  /** @flr-generate all */
8
9
  var ClearPropsContext = (props) => {
9
10
  const { children } = props;
10
11
  return /* @__PURE__ */ jsx(PropsContextProvider, {
11
- value: {},
12
+ value: emptyPropsContext,
12
13
  children
13
14
  });
14
15
  };
@@ -1 +1 @@
1
- {"version":3,"file":"ClearPropsContext.mjs","names":[],"sources":["../../../../../../../../src/lib/propsContext/components/ClearPropsContext.tsx"],"sourcesContent":["import { PropsContextProvider } from \"@/lib/propsContext/propsContext\";\nimport { type FC, type PropsWithChildren } from \"react\";\n\nexport type ClearPropsContextProps = PropsWithChildren;\n\n/** @flr-generate all */\nexport const ClearPropsContext: FC<PropsWithChildren> = (props) => {\n const { children } = props;\n return <PropsContextProvider value={{}}>{children}</PropsContextProvider>;\n};\n\nexport default ClearPropsContext;\n"],"mappings":";;;;;;;AAMA,IAAa,qBAA4C,UAAU;CACjE,MAAM,EAAE,aAAa;CACrB,OAAO,oBAAC,sBAAD;EAAsB,OAAO,CAAC;EAAI;CAA+B,CAAA;AAC1E"}
1
+ {"version":3,"file":"ClearPropsContext.mjs","names":[],"sources":["../../../../../../../../src/lib/propsContext/components/ClearPropsContext.tsx"],"sourcesContent":["import { PropsContextProvider } from \"@/lib/propsContext/propsContext\";\nimport { type FC, type PropsWithChildren } from \"react\";\n\nexport type ClearPropsContextProps = PropsWithChildren;\n\nconst emptyPropsContext = {};\n\n/** @flr-generate all */\nexport const ClearPropsContext: FC<PropsWithChildren> = (props) => {\n const { children } = props;\n return (\n <PropsContextProvider value={emptyPropsContext}>\n {children}\n </PropsContextProvider>\n );\n};\n\nexport default ClearPropsContext;\n"],"mappings":";;;;;;AAKA,IAAM,oBAAoB,CAAC;;AAG3B,IAAa,qBAA4C,UAAU;CACjE,MAAM,EAAE,aAAa;CACrB,OACE,oBAAC,sBAAD;EAAsB,OAAO;EAC1B;CACmB,CAAA;AAE1B"}
@@ -3,18 +3,29 @@
3
3
  import { propsContext, usePropsContext } from "../propsContext.mjs";
4
4
  import { addNestingLevel } from "../nestedPropsContext/lib.mjs";
5
5
  import mergePropsContext from "../mergePropsContext.mjs";
6
- import { memo, useMemo } from "react";
6
+ import isPlainObjectDeepEqual from "../../object/isPlainObjectDeepEqual.mjs";
7
+ import { memo, useMemo, useRef } from "react";
7
8
  import { jsx } from "react/jsx-runtime";
8
9
  //#region src/lib/propsContext/components/PropsContextProvider.tsx
9
10
  var Provider = memo(propsContext.Provider);
10
11
  Provider.displayName = "PropsContextProviderInner";
12
+ var useStablePropsContext = (props) => {
13
+ const stableProps = useRef(props);
14
+ if (!isPlainObjectDeepEqual(stableProps.current, props)) stableProps.current = props;
15
+ return stableProps.current;
16
+ };
11
17
  var PropsContextProvider = memo((props) => {
12
18
  const { props: providedProps, dependencies = [], children } = props;
13
19
  const parentPropsContext = usePropsContext();
20
+ const stableProvidedProps = useStablePropsContext(providedProps);
14
21
  const propsWithParentPropsContext = useMemo(() => {
15
- const propsWithNestingLevel = addNestingLevel(providedProps);
22
+ const propsWithNestingLevel = addNestingLevel(stableProvidedProps);
16
23
  return mergePropsContext(parentPropsContext, propsWithNestingLevel);
17
- }, [parentPropsContext, ...dependencies]);
24
+ }, [
25
+ parentPropsContext,
26
+ stableProvidedProps,
27
+ ...dependencies
28
+ ]);
18
29
  return /* @__PURE__ */ jsx(Provider, {
19
30
  value: propsWithParentPropsContext,
20
31
  children
@@ -1 +1 @@
1
- {"version":3,"file":"PropsContextProvider.mjs","names":[],"sources":["../../../../../../../../src/lib/propsContext/components/PropsContextProvider.tsx"],"sourcesContent":["import mergePropsContext from \"@/lib/propsContext/mergePropsContext\";\nimport { addNestingLevel } from \"@/lib/propsContext/nestedPropsContext/lib\";\nimport { propsContext, usePropsContext } from \"@/lib/propsContext/propsContext\";\nimport type { PropsContext as PropsContextShape } from \"@/lib/propsContext/types\";\nimport type { DependencyList, FC, PropsWithChildren } from \"react\";\nimport { memo, useMemo } from \"react\";\n\ninterface Props extends PropsWithChildren {\n props: PropsContextShape;\n dependencies?: DependencyList;\n}\n\nconst Provider = memo(propsContext.Provider);\nProvider.displayName = \"PropsContextProviderInner\";\n\nexport const PropsContextProvider: FC<Props> = memo((props) => {\n const { props: providedProps, dependencies = [], children } = props;\n\n const parentPropsContext = usePropsContext();\n\n const propsWithParentPropsContext = useMemo(() => {\n const propsWithNestingLevel = addNestingLevel(providedProps);\n return mergePropsContext(parentPropsContext, propsWithNestingLevel);\n }, [parentPropsContext, ...dependencies]);\n\n return <Provider value={propsWithParentPropsContext}>{children}</Provider>;\n});\n\nPropsContextProvider.displayName = \"PropsContextProvider\";\n\nexport default PropsContextProvider;\n"],"mappings":";;;;;;;;AAYA,IAAM,WAAW,KAAK,aAAa,QAAQ;AAC3C,SAAS,cAAc;AAEvB,IAAa,uBAAkC,MAAM,UAAU;CAC7D,MAAM,EAAE,OAAO,eAAe,eAAe,CAAC,GAAG,aAAa;CAE9D,MAAM,qBAAqB,gBAAgB;CAE3C,MAAM,8BAA8B,cAAc;EAChD,MAAM,wBAAwB,gBAAgB,aAAa;EAC3D,OAAO,kBAAkB,oBAAoB,qBAAqB;CACpE,GAAG,CAAC,oBAAoB,GAAG,YAAY,CAAC;CAExC,OAAO,oBAAC,UAAD;EAAU,OAAO;EAA8B;CAAmB,CAAA;AAC3E,CAAC;AAED,qBAAqB,cAAc"}
1
+ {"version":3,"file":"PropsContextProvider.mjs","names":[],"sources":["../../../../../../../../src/lib/propsContext/components/PropsContextProvider.tsx"],"sourcesContent":["import mergePropsContext from \"@/lib/propsContext/mergePropsContext\";\nimport { addNestingLevel } from \"@/lib/propsContext/nestedPropsContext/lib\";\nimport { propsContext, usePropsContext } from \"@/lib/propsContext/propsContext\";\nimport { isPlainObjectDeepEqual } from \"@/lib/object/isPlainObjectDeepEqual\";\nimport type { PropsContext as PropsContextShape } from \"@/lib/propsContext/types\";\nimport type { DependencyList, FC, PropsWithChildren } from \"react\";\nimport { memo, useMemo, useRef } from \"react\";\n\ninterface Props extends PropsWithChildren {\n props: PropsContextShape;\n dependencies?: DependencyList;\n}\n\nconst Provider = memo(propsContext.Provider);\nProvider.displayName = \"PropsContextProviderInner\";\n\nconst useStablePropsContext = (props: PropsContextShape) => {\n const stableProps = useRef(props);\n if (!isPlainObjectDeepEqual(stableProps.current, props)) {\n stableProps.current = props;\n }\n return stableProps.current;\n};\n\nexport const PropsContextProvider: FC<Props> = memo((props) => {\n const { props: providedProps, dependencies = [], children } = props;\n\n const parentPropsContext = usePropsContext();\n const stableProvidedProps = useStablePropsContext(providedProps);\n\n const propsWithParentPropsContext = useMemo(() => {\n const propsWithNestingLevel = addNestingLevel(stableProvidedProps);\n return mergePropsContext(parentPropsContext, propsWithNestingLevel);\n }, [parentPropsContext, stableProvidedProps, ...dependencies]);\n\n return <Provider value={propsWithParentPropsContext}>{children}</Provider>;\n});\n\nPropsContextProvider.displayName = \"PropsContextProvider\";\n\nexport default PropsContextProvider;\n"],"mappings":";;;;;;;;;AAaA,IAAM,WAAW,KAAK,aAAa,QAAQ;AAC3C,SAAS,cAAc;AAEvB,IAAM,yBAAyB,UAA6B;CAC1D,MAAM,cAAc,OAAO,KAAK;CAChC,IAAI,CAAC,uBAAuB,YAAY,SAAS,KAAK,GACpD,YAAY,UAAU;CAExB,OAAO,YAAY;AACrB;AAEA,IAAa,uBAAkC,MAAM,UAAU;CAC7D,MAAM,EAAE,OAAO,eAAe,eAAe,CAAC,GAAG,aAAa;CAE9D,MAAM,qBAAqB,gBAAgB;CAC3C,MAAM,sBAAsB,sBAAsB,aAAa;CAE/D,MAAM,8BAA8B,cAAc;EAChD,MAAM,wBAAwB,gBAAgB,mBAAmB;EACjE,OAAO,kBAAkB,oBAAoB,qBAAqB;CACpE,GAAG;EAAC;EAAoB;EAAqB,GAAG;CAAY,CAAC;CAE7D,OAAO,oBAAC,UAAD;EAAU,OAAO;EAA8B;CAAmB,CAAA;AAC3E,CAAC;AAED,qBAAqB,cAAc"}
@@ -1,6 +1,6 @@
1
1
  "use client"
2
2
  /* */
3
- import { uniqueBy } from "remeda";
3
+ import { isShallowEqual, uniqueBy } from "remeda";
4
4
  import { action, computed, makeObservable, observable } from "mobx";
5
5
  //#region ../components-base/src/list/loading/ListLoaderState.ts
6
6
  /**
@@ -49,12 +49,17 @@ var ListLoaderState = class {
49
49
  this.batchLoadingStates = [];
50
50
  this.dataBatches = [];
51
51
  }
52
+ /**
53
+ * Keeps the current batch when `data` holds the same items in the same order.
54
+ * A loader that builds a new array on every fetch — a refetch whose items its
55
+ * data layer reuses — would otherwise re-render the whole list for nothing.
56
+ */
52
57
  setDataBatch(index, data) {
53
58
  if (this.dataBatches.length === 0) this.prevDataBatches = [];
54
- if (this.dataBatches[index] !== data) {
55
- this.dataBatches[index] = data;
56
- this.prevDataBatches[index] = data;
57
- }
59
+ const current = this.dataBatches[index];
60
+ if (current !== void 0 && isShallowEqual(current, data)) return;
61
+ this.dataBatches[index] = data;
62
+ this.prevDataBatches[index] = data;
58
63
  }
59
64
  setBatchLoadingState(index, state) {
60
65
  if (state === "error" || state === "loaded") this.setIsInitiallyLoading(false);
@@ -1 +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"}
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 { isShallowEqual, 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 /**\n * Keeps the current batch when `data` holds the same items in the same order.\n * A loader that builds a new array on every fetch — a refetch whose items its\n * data layer reuses — would otherwise re-render the whole list for nothing.\n */\n public setDataBatch(index: number, data: ListData<T>): void {\n if (this.dataBatches.length === 0) {\n this.prevDataBatches = [];\n }\n\n const current = this.dataBatches[index];\n if (current !== undefined && isShallowEqual(current, data)) {\n return;\n }\n\n this.dataBatches[index] = data;\n this.prevDataBatches[index] = data;\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;;;;;;CAOA,aAAoB,OAAe,MAAyB;EAC1D,IAAI,KAAK,YAAY,WAAW,GAC9B,KAAK,kBAAkB,CAAC;EAG1B,MAAM,UAAU,KAAK,YAAY;EACjC,IAAI,YAAY,KAAA,KAAa,eAAe,SAAS,IAAI,GACvD;EAGF,KAAK,YAAY,SAAS;EAC1B,KAAK,gBAAgB,SAAS;CAChC;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"}
@@ -32,6 +32,11 @@ export declare class ListLoaderState<T> {
32
32
  private readonly getItemId?;
33
33
  constructor(options?: ListLoaderStateOptions<T>);
34
34
  reset(): void;
35
+ /**
36
+ * Keeps the current batch when `data` holds the same items in the same order.
37
+ * A loader that builds a new array on every fetch — a refetch whose items its
38
+ * data layer reuses — would otherwise re-render the whole list for nothing.
39
+ */
35
40
  setDataBatch(index: number, data: ListData<T>): void;
36
41
  setBatchLoadingState(index: number, state: BatchLoadingState): void;
37
42
  setMetadata(metadata?: unknown): void;
@@ -8,4 +8,11 @@ export declare class Table<T> {
8
8
  readonly body: TableBody<T>;
9
9
  readonly componentProps: TableSupportedComponentProps;
10
10
  constructor(list: List<T>, shape?: TableShape<T>);
11
+ /**
12
+ * Whether `other` would render the table identically, rows aside. Like
13
+ * `TableRow.rendersSameAs`, it only holds for rows that declare
14
+ * `renderDependencies`. Props count by identity, one by one, including the
15
+ * list's own.
16
+ */
17
+ rendersSameAs(other?: Table<T>): boolean;
11
18
  }
@@ -6,4 +6,5 @@ export declare class TableBody<T> {
6
6
  readonly componentProps: TableBodySupportedComponentProps;
7
7
  readonly row: TableRow<T>;
8
8
  constructor(table: Table<T>, shape?: TableBodyShape<T>);
9
+ rendersSameAs(other: TableBody<T>): boolean;
9
10
  }
@@ -4,4 +4,5 @@ export declare class TableColumn<T> {
4
4
  readonly tableHeader: TableHeader<T>;
5
5
  readonly componentProps: TableColumnSupportedComponentProps;
6
6
  constructor(tableHeader: TableHeader<T>, shape?: TableColumnShape);
7
+ rendersSameAs(other: TableColumn<T>): boolean;
7
8
  }
@@ -6,4 +6,5 @@ export declare class TableHeader<T> {
6
6
  readonly componentProps: TableHeaderSupportedComponentProps;
7
7
  readonly columns: TableColumn<T>[];
8
8
  constructor(table: Table<T>, shape?: TableHeaderShape);
9
+ rendersSameAs(other: TableHeader<T>): boolean;
9
10
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Compares plain objects recursively and every other value by identity.
3
+ * Functions, arrays, class instances, React elements and ref objects (`{
4
+ * current }`) only count as equal when they are the same value, so a value
5
+ * created during render counts as a change.
6
+ */
7
+ export declare const isPlainObjectDeepEqual: (first: unknown, second: unknown) => boolean;
8
+ export default isPlainObjectDeepEqual;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mittwald/flow-react-components",
3
- "version": "1.6.0-next.42",
3
+ "version": "1.6.0-next.44",
4
4
  "type": "module",
5
5
  "description": "A React implementation of Flow, mittwald’s design system",
6
6
  "homepage": "https://flow.mittwald.de",
@@ -84,9 +84,9 @@
84
84
  "@internationalized/string-compiler": "^3.4.1",
85
85
  "@lezer/common": "^1.5.2",
86
86
  "@lezer/highlight": "^1.2.3",
87
- "@mittwald/flow-icons": "^1.6.0-next.42",
87
+ "@mittwald/flow-icons": "^1.6.0-next.44",
88
88
  "@mittwald/password-tools-js": "^3.0.0",
89
- "@mittwald/react-tunnel": "^1.6.0-next.42",
89
+ "@mittwald/react-tunnel": "^1.6.0-next.44",
90
90
  "@mittwald/react-use-promise": "^4.2.2",
91
91
  "@react-aria/form": "^3.2.1",
92
92
  "@react-aria/i18n": "^3.13.1",
@@ -139,13 +139,13 @@
139
139
  "@internationalized/date": "^3.12.3",
140
140
  "@lezer/generator": "^1.8.0",
141
141
  "@lezer/lr": "^1.4.10",
142
- "@mittwald/flow-components-base": "1.6.0-next.42",
143
- "@mittwald/flow-core": "1.6.0-next.42",
144
- "@mittwald/flow-design-tokens": "1.6.0-next.42",
145
- "@mittwald/flow-icons-base": "1.6.0-next.42",
142
+ "@mittwald/flow-components-base": "1.6.0-next.44",
143
+ "@mittwald/flow-core": "1.6.0-next.44",
144
+ "@mittwald/flow-design-tokens": "1.6.0-next.44",
145
+ "@mittwald/flow-icons-base": "1.6.0-next.44",
146
146
  "@mittwald/react-use-promise": "^4.2.2",
147
147
  "@mittwald/remote-dom-react": "1.2.2-mittwald.10",
148
- "@mittwald/typescript-config": "1.6.0-next.42",
148
+ "@mittwald/typescript-config": "1.6.0-next.44",
149
149
  "@nx/storybook": "^22.7.6",
150
150
  "@storybook/addon-a11y": "^10.5.10",
151
151
  "@storybook/addon-actions": "^9.0.8",
@@ -199,7 +199,7 @@
199
199
  },
200
200
  "peerDependencies": {
201
201
  "@internationalized/date": "^3.12.2",
202
- "@mittwald/flow-icons-pro": "1.6.0-next.42",
202
+ "@mittwald/flow-icons-pro": "1.6.0-next.44",
203
203
  "@mittwald/react-use-promise": "^4.2.2",
204
204
  "next": "^16.2.3",
205
205
  "react": "^19.2.0",
@@ -223,5 +223,5 @@
223
223
  "browserslist": [
224
224
  "baseline widely available"
225
225
  ],
226
- "gitHead": "69e1e2c039bdac7404e519d98bb508e21c16a8f3"
226
+ "gitHead": "d78a04592e71e0174113920f2c2eacec46a373a1"
227
227
  }