@etsoo/materialui 1.4.35 → 1.4.37

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/lib/DataGridEx.js CHANGED
@@ -91,7 +91,7 @@ export function DataGridEx(props) {
91
91
  });
92
92
  }
93
93
  else if (sortable && field != null) {
94
- const active = orderBy != null && orderBy[field] != null;
94
+ const active = orderBy?.has(field);
95
95
  sortLabel = (_jsx(TableSortLabel, { active: active, direction: sortAsc ? "asc" : "desc", onClick: (_event) => {
96
96
  if (active)
97
97
  column.sortAsc = !sortAsc;
@@ -165,7 +165,7 @@ export function DataGridEx(props) {
165
165
  });
166
166
  // New sort
167
167
  const handleSort = (field, asc) => {
168
- reset({ queryPaging: { orderBy: { [field]: asc ?? true } } });
168
+ reset({ queryPaging: { orderBy: new Map([[field, !(asc ?? true)]]) } });
169
169
  };
170
170
  // Reset
171
171
  const reset = (add) => {
@@ -1,6 +1,7 @@
1
1
  import { GridLoadDataProps, GridLoaderStates } from "@etsoo/react";
2
2
  import { DataTypes } from "@etsoo/shared";
3
3
  import { GridDataCacheType } from "./GridDataCacheType";
4
+ import { QueryPagingData } from "@etsoo/appscript";
4
5
  /**
5
6
  * Grid utilities
6
7
  */
@@ -13,7 +14,7 @@ export declare namespace GridUtils {
13
14
  * @returns Request data
14
15
  */
15
16
  function createLoader<F extends DataTypes.BasicTemplate>(props: GridLoadDataProps, template?: F, cacheKey?: string): DataTypes.BasicTemplateType<F> & {
16
- queryPaging: import("@etsoo/appscript").QueryPagingData;
17
+ queryPaging: QueryPagingData;
17
18
  };
18
19
  /**
19
20
  * Get cache data
@@ -40,4 +41,11 @@ export declare namespace GridUtils {
40
41
  * @param searchData Search data
41
42
  */
42
43
  function mergeSearchData<T, F extends DataTypes.BasicTemplate>(state: GridLoaderStates<T>, searchData?: DataTypes.BasicTemplateType<F>): void;
44
+ /**
45
+ * Setup paging keysets
46
+ * @param data Paging data
47
+ * @param lastItem Last item of the query
48
+ * @param idField Id field
49
+ */
50
+ function setupPagingKeysets<T>(data: QueryPagingData, lastItem: T | undefined, idField: keyof T & string): void;
43
51
  }
package/lib/GridUtils.js CHANGED
@@ -83,4 +83,26 @@ export var GridUtils;
83
83
  Object.assign(state.data, searchData);
84
84
  }
85
85
  GridUtils.mergeSearchData = mergeSearchData;
86
+ /**
87
+ * Setup paging keysets
88
+ * @param data Paging data
89
+ * @param lastItem Last item of the query
90
+ * @param idField Id field
91
+ */
92
+ function setupPagingKeysets(data, lastItem, idField) {
93
+ // If the id field is not set for ordering, add it with descending
94
+ if (data.orderBy == null) {
95
+ data.orderBy = new Map([[idField, true]]);
96
+ }
97
+ else if (!data.orderBy.has(idField)) {
98
+ data.orderBy.set(idField, true);
99
+ }
100
+ // Set the paging keysets
101
+ if (lastItem) {
102
+ const keysets = [];
103
+ data.orderBy.forEach((_value, key) => keysets.push(Reflect.get(lastItem, key)));
104
+ data.keysets = keysets;
105
+ }
106
+ }
107
+ GridUtils.setupPagingKeysets = setupPagingKeysets;
86
108
  })(GridUtils || (GridUtils = {}));
package/lib/MUGlobal.js CHANGED
@@ -158,7 +158,10 @@ export class MUGlobal {
158
158
  const mediaRaw = theme.breakpoints.up(key);
159
159
  const mediaQuery = mediaRaw.substring(mediaRaw.indexOf("("));
160
160
  if (window.matchMedia(mediaQuery).matches) {
161
- return parseInt(theme.spacing(value), 10);
161
+ let space = parseInt(theme.spacing(value), 10);
162
+ if (isNaN(space))
163
+ space = 8 * value;
164
+ return space;
162
165
  }
163
166
  }
164
167
  }
@@ -61,7 +61,7 @@ export type ResponsibleContainerProps<T extends object, F extends DataTypes.Basi
61
61
  /**
62
62
  * Load data callback
63
63
  */
64
- loadData: (data: GridJsonData & DataTypes.BasicTemplateType<F>) => PromiseLike<T[] | null | undefined>;
64
+ loadData: (data: GridJsonData & DataTypes.BasicTemplateType<F>, lastItem?: T) => PromiseLike<T[] | null | undefined>;
65
65
  /**
66
66
  * Methods
67
67
  */
@@ -44,9 +44,9 @@ export function ResponsibleContainer(props) {
44
44
  // Has fields
45
45
  const hasFields = fields != null && fields.length > 0;
46
46
  // Load data
47
- const localLoadData = (props) => {
47
+ const localLoadData = (props, lastItem) => {
48
48
  state.mounted = true;
49
- return loadData(GridUtils.createLoader(props, fieldTemplate, cacheKey));
49
+ return loadData(GridUtils.createLoader(props, fieldTemplate, cacheKey), lastItem);
50
50
  };
51
51
  // Search data
52
52
  const searchData = GridUtils.getSearchData(cacheKey);
@@ -7,6 +7,10 @@ export interface SearchBarProps {
7
7
  * Style class name
8
8
  */
9
9
  className?: string;
10
+ /**
11
+ * Item gap
12
+ */
13
+ itemGap?: number;
10
14
  /**
11
15
  * Item width
12
16
  */
package/lib/SearchBar.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Button, Drawer, IconButton, Stack, useTheme } from "@mui/material";
2
+ import { Button, Drawer, IconButton, Stack } from "@mui/material";
3
3
  import React from "react";
4
4
  import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
5
5
  import { DomUtils } from "@etsoo/shared";
@@ -54,12 +54,9 @@ const setChildState = (child, enabled) => {
54
54
  */
55
55
  export function SearchBar(props) {
56
56
  // Destruct
57
- const { className, fields, onSubmit, itemWidth = 160 } = props;
57
+ const { className, fields, onSubmit, itemGap = 6, itemWidth = 160 } = props;
58
58
  // Labels
59
59
  const labels = Labels.CommonPage;
60
- // Spacing
61
- const theme = useTheme();
62
- const gap = parseFloat(theme.spacing(1));
63
60
  // Menu index
64
61
  const [index, updateIndex] = React.useState();
65
62
  // Drawer open / close
@@ -68,7 +65,7 @@ export function SearchBar(props) {
68
65
  const state = React.useRef({ hasMore: true, lastMaxWidth: 9999 }).current;
69
66
  // Watch container
70
67
  const { dimensions } = useDimensions(1, (target, rect) => {
71
- // Same logic from resetButtonRef
68
+ // Same logic from resetButtonRefe);
72
69
  if (rect.width === state.lastMaxWidth ||
73
70
  (!state.hasMore && rect.width > state.lastMaxWidth))
74
71
  return false;
@@ -114,7 +111,7 @@ export function SearchBar(props) {
114
111
  // More button rect
115
112
  const buttonMoreRect = buttonMore.getBoundingClientRect();
116
113
  // Total
117
- const totalButtonWidth = resetButtonRect.width + buttonMoreRect.width + 3 * gap;
114
+ const totalButtonWidth = resetButtonRect.width + buttonMoreRect.width + 3 * itemGap;
118
115
  // Cache
119
116
  container.setAttribute(cachedWidthName, totalButtonWidth.toString());
120
117
  maxWidth -= totalButtonWidth;
@@ -136,7 +133,7 @@ export function SearchBar(props) {
136
133
  }
137
134
  else {
138
135
  const childD = child.getBoundingClientRect();
139
- childWidth = childD.width + gap;
136
+ childWidth = childD.width + itemGap;
140
137
  child.setAttribute(cachedWidthName, childWidth.toString());
141
138
  }
142
139
  // No gap here, child width includes the gap
@@ -224,7 +221,7 @@ export function SearchBar(props) {
224
221
  return (_jsxs(React.Fragment, { children: [_jsx("form", { id: "SearchBarForm", className: className, onChange: handleForm, ref: (form) => {
225
222
  if (form)
226
223
  state.form = form;
227
- }, children: _jsxs(Stack, { ref: dimensions[0][0], className: "SearchBarContainer", justifyContent: "center", alignItems: "center", direction: "row", spacing: 1, width: "100%", overflow: "hidden", paddingTop: "6px", sx: {
224
+ }, children: _jsxs(Stack, { ref: dimensions[0][0], className: "SearchBarContainer", justifyContent: "center", alignItems: "center", direction: "row", spacing: `${itemGap}px`, width: "100%", overflow: "hidden", paddingTop: "6px", sx: {
228
225
  "& > :not(style)": {
229
226
  flexBasis: "auto",
230
227
  flexGrow: 0,
package/lib/TableEx.js CHANGED
@@ -171,7 +171,7 @@ export function TableEx(props) {
171
171
  };
172
172
  // New sort
173
173
  const handleSort = (field, asc) => {
174
- reset({ queryPaging: { orderBy: { [field]: asc ?? true } } });
174
+ reset({ queryPaging: { orderBy: new Map([[field, !(asc ?? true)]]) } });
175
175
  };
176
176
  // Set items for rerenderer
177
177
  const setItems = (callback) => {
@@ -218,8 +218,7 @@ export function TableEx(props) {
218
218
  // Sortable
219
219
  let sortLabel;
220
220
  if (sortable && field != null) {
221
- const active = queryPaging.orderBy != null &&
222
- queryPaging.orderBy[field] != null;
221
+ const active = queryPaging.orderBy?.has(field);
223
222
  sortLabel = (_jsx(TableSortLabel, { active: active, direction: sortAsc ? "asc" : "desc", onClick: (_event) => {
224
223
  if (active)
225
224
  column.sortAsc = !sortAsc;
@@ -33,8 +33,8 @@ export function DataGridPage(props) {
33
33
  const onSubmit = (data, _reset) => {
34
34
  setStates({ data });
35
35
  };
36
- const localLoadData = (props) => {
37
- return loadData(GridUtils.createLoader(props, fieldTemplate, cacheKey));
36
+ const localLoadData = (props, lastItem) => {
37
+ return loadData(GridUtils.createLoader(props, fieldTemplate, cacheKey), lastItem);
38
38
  };
39
39
  // Search data
40
40
  const searchData = GridUtils.getSearchData(cacheKey);
@@ -24,7 +24,7 @@ export type SearchPageProps<T extends object, F extends DataTypes.BasicTemplate>
24
24
  /**
25
25
  * Load data callback
26
26
  */
27
- loadData: (data: GridJsonData & DataTypes.BasicTemplateType<F>) => PromiseLike<T[] | null | undefined>;
27
+ loadData: (data: GridJsonData & DataTypes.BasicTemplateType<F>, lastItem?: T) => PromiseLike<T[] | null | undefined>;
28
28
  /**
29
29
  * Page props
30
30
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etsoo/materialui",
3
- "version": "1.4.35",
3
+ "version": "1.4.37",
4
4
  "description": "TypeScript Material-UI Implementation",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",
@@ -35,9 +35,9 @@
35
35
  "@emotion/css": "^11.13.5",
36
36
  "@emotion/react": "^11.13.5",
37
37
  "@emotion/styled": "^11.13.5",
38
- "@etsoo/appscript": "^1.5.76",
38
+ "@etsoo/appscript": "^1.5.77",
39
39
  "@etsoo/notificationbase": "^1.1.54",
40
- "@etsoo/react": "^1.8.3",
40
+ "@etsoo/react": "^1.8.6",
41
41
  "@etsoo/shared": "^1.2.55",
42
42
  "@mui/icons-material": "^6.1.9",
43
43
  "@mui/material": "^6.1.9",
@@ -73,6 +73,6 @@
73
73
  "@vitejs/plugin-react": "^4.3.4",
74
74
  "jsdom": "^25.0.1",
75
75
  "typescript": "^5.7.2",
76
- "vitest": "^2.1.6"
76
+ "vitest": "^2.1.8"
77
77
  }
78
78
  }
@@ -258,7 +258,7 @@ export function DataGridEx<T extends object>(props: DataGridExProps<T>) {
258
258
  states
259
259
  });
260
260
  } else if (sortable && field != null) {
261
- const active = orderBy != null && orderBy[field] != null;
261
+ const active = orderBy?.has(field);
262
262
 
263
263
  sortLabel = (
264
264
  <TableSortLabel
@@ -438,7 +438,7 @@ export function DataGridEx<T extends object>(props: DataGridExProps<T>) {
438
438
 
439
439
  // New sort
440
440
  const handleSort = (field: string, asc?: boolean) => {
441
- reset({ queryPaging: { orderBy: { [field]: asc ?? true } } });
441
+ reset({ queryPaging: { orderBy: new Map([[field, !(asc ?? true)]]) } });
442
442
  };
443
443
 
444
444
  // Reset
package/src/GridUtils.ts CHANGED
@@ -5,6 +5,7 @@ import {
5
5
  } from "@etsoo/react";
6
6
  import { DataTypes } from "@etsoo/shared";
7
7
  import { GridDataCacheType } from "./GridDataCacheType";
8
+ import { QueryPagingData } from "@etsoo/appscript";
8
9
 
9
10
  /**
10
11
  * Grid utilities
@@ -98,4 +99,32 @@ export namespace GridUtils {
98
99
  state.data ??= {};
99
100
  Object.assign(state.data, searchData);
100
101
  }
102
+
103
+ /**
104
+ * Setup paging keysets
105
+ * @param data Paging data
106
+ * @param lastItem Last item of the query
107
+ * @param idField Id field
108
+ */
109
+ export function setupPagingKeysets<T>(
110
+ data: QueryPagingData,
111
+ lastItem: T | undefined,
112
+ idField: keyof T & string
113
+ ) {
114
+ // If the id field is not set for ordering, add it with descending
115
+ if (data.orderBy == null) {
116
+ data.orderBy = new Map<string, boolean>([[idField, true]]);
117
+ } else if (!data.orderBy.has(idField)) {
118
+ data.orderBy.set(idField, true);
119
+ }
120
+
121
+ // Set the paging keysets
122
+ if (lastItem) {
123
+ const keysets: unknown[] = [];
124
+ data.orderBy.forEach((_value, key) =>
125
+ keysets.push(Reflect.get(lastItem, key))
126
+ );
127
+ data.keysets = keysets;
128
+ }
129
+ }
101
130
  }
package/src/MUGlobal.ts CHANGED
@@ -187,7 +187,9 @@ export class MUGlobal {
187
187
  const mediaRaw = theme.breakpoints.up(key as Breakpoint);
188
188
  const mediaQuery = mediaRaw.substring(mediaRaw.indexOf("("));
189
189
  if (window.matchMedia(mediaQuery).matches) {
190
- return parseInt(theme.spacing(value), 10);
190
+ let space = parseInt(theme.spacing(value), 10);
191
+ if (isNaN(space)) space = 8 * value;
192
+ return space;
191
193
  }
192
194
  }
193
195
  }
@@ -123,7 +123,8 @@ export type ResponsibleContainerProps<
123
123
  * Load data callback
124
124
  */
125
125
  loadData: (
126
- data: GridJsonData & DataTypes.BasicTemplateType<F>
126
+ data: GridJsonData & DataTypes.BasicTemplateType<F>,
127
+ lastItem?: T
127
128
  ) => PromiseLike<T[] | null | undefined>;
128
129
 
129
130
  /**
@@ -238,9 +239,12 @@ export function ResponsibleContainer<
238
239
  const hasFields = fields != null && fields.length > 0;
239
240
 
240
241
  // Load data
241
- const localLoadData = (props: GridLoadDataProps) => {
242
+ const localLoadData = (props: GridLoadDataProps, lastItem?: T) => {
242
243
  state.mounted = true;
243
- return loadData(GridUtils.createLoader<F>(props, fieldTemplate, cacheKey));
244
+ return loadData(
245
+ GridUtils.createLoader<F>(props, fieldTemplate, cacheKey),
246
+ lastItem
247
+ );
244
248
  };
245
249
 
246
250
  // Search data
package/src/SearchBar.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { Button, Drawer, IconButton, Stack, useTheme } from "@mui/material";
1
+ import { Button, Drawer, IconButton, Stack } from "@mui/material";
2
2
  import React from "react";
3
3
  import MoreHorizIcon from "@mui/icons-material/MoreHoriz";
4
4
  import { DomUtils } from "@etsoo/shared";
@@ -14,6 +14,11 @@ export interface SearchBarProps {
14
14
  */
15
15
  className?: string;
16
16
 
17
+ /**
18
+ * Item gap
19
+ */
20
+ itemGap?: number;
21
+
17
22
  /**
18
23
  * Item width
19
24
  */
@@ -83,15 +88,11 @@ const setChildState = (child: Element, enabled: boolean) => {
83
88
  */
84
89
  export function SearchBar(props: SearchBarProps) {
85
90
  // Destruct
86
- const { className, fields, onSubmit, itemWidth = 160 } = props;
91
+ const { className, fields, onSubmit, itemGap = 6, itemWidth = 160 } = props;
87
92
 
88
93
  // Labels
89
94
  const labels = Labels.CommonPage;
90
95
 
91
- // Spacing
92
- const theme = useTheme();
93
- const gap = parseFloat(theme.spacing(1));
94
-
95
96
  // Menu index
96
97
  const [index, updateIndex] = React.useState<number>();
97
98
 
@@ -110,7 +111,7 @@ export function SearchBar(props: SearchBarProps) {
110
111
  const { dimensions } = useDimensions(
111
112
  1,
112
113
  (target, rect) => {
113
- // Same logic from resetButtonRef
114
+ // Same logic from resetButtonRefe);
114
115
  if (
115
116
  rect.width === state.lastMaxWidth ||
116
117
  (!state.hasMore && rect.width > state.lastMaxWidth)
@@ -170,7 +171,7 @@ export function SearchBar(props: SearchBarProps) {
170
171
 
171
172
  // Total
172
173
  const totalButtonWidth =
173
- resetButtonRect.width + buttonMoreRect.width + 3 * gap;
174
+ resetButtonRect.width + buttonMoreRect.width + 3 * itemGap;
174
175
 
175
176
  // Cache
176
177
  container.setAttribute(cachedWidthName, totalButtonWidth.toString());
@@ -196,7 +197,7 @@ export function SearchBar(props: SearchBarProps) {
196
197
  childWidth = Number.parseFloat(cachedWidth);
197
198
  } else {
198
199
  const childD = child.getBoundingClientRect();
199
- childWidth = childD.width + gap;
200
+ childWidth = childD.width + itemGap;
200
201
  child.setAttribute(cachedWidthName, childWidth.toString());
201
202
  }
202
203
 
@@ -312,7 +313,7 @@ export function SearchBar(props: SearchBarProps) {
312
313
  justifyContent="center"
313
314
  alignItems="center"
314
315
  direction="row"
315
- spacing={1}
316
+ spacing={`${itemGap}px`}
316
317
  width="100%"
317
318
  overflow="hidden"
318
319
  paddingTop="6px"
@@ -336,7 +337,6 @@ export function SearchBar(props: SearchBarProps) {
336
337
  {fields.map((item, index) => (
337
338
  <React.Fragment key={index}>{item}</React.Fragment>
338
339
  ))}
339
-
340
340
  <IconButton
341
341
  title={labels.more}
342
342
  size="medium"
package/src/TableEx.tsx CHANGED
@@ -306,7 +306,7 @@ export function TableEx<
306
306
 
307
307
  // New sort
308
308
  const handleSort = (field: string, asc?: boolean) => {
309
- reset({ queryPaging: { orderBy: { [field]: asc ?? true } } });
309
+ reset({ queryPaging: { orderBy: new Map([[field, !(asc ?? true)]]) } });
310
310
  };
311
311
 
312
312
  // Set items for rerenderer
@@ -400,9 +400,7 @@ export function TableEx<
400
400
  // Sortable
401
401
  let sortLabel: React.ReactNode;
402
402
  if (sortable && field != null) {
403
- const active =
404
- queryPaging.orderBy != null &&
405
- queryPaging.orderBy[field] != null;
403
+ const active = queryPaging.orderBy?.has(field);
406
404
 
407
405
  sortLabel = (
408
406
  <TableSortLabel
@@ -76,8 +76,11 @@ export function DataGridPage<
76
76
  setStates({ data });
77
77
  };
78
78
 
79
- const localLoadData = (props: GridLoadDataProps) => {
80
- return loadData(GridUtils.createLoader<F>(props, fieldTemplate, cacheKey));
79
+ const localLoadData = (props: GridLoadDataProps, lastItem?: T) => {
80
+ return loadData(
81
+ GridUtils.createLoader<F>(props, fieldTemplate, cacheKey),
82
+ lastItem
83
+ );
81
84
  };
82
85
 
83
86
  // Search data
@@ -35,7 +35,8 @@ export type SearchPageProps<
35
35
  * Load data callback
36
36
  */
37
37
  loadData: (
38
- data: GridJsonData & DataTypes.BasicTemplateType<F>
38
+ data: GridJsonData & DataTypes.BasicTemplateType<F>,
39
+ lastItem?: T
39
40
  ) => PromiseLike<T[] | null | undefined>;
40
41
 
41
42
  /**