@iobroker/gui-components 10.3.2 → 10.3.4

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.
@@ -27,15 +27,47 @@ export declare function renderAliasLink(that: ObjectBrowserClass, id: string, in
27
27
  /**
28
28
  * Renders a leaf.
29
29
  */
30
- export declare function renderLeaf(that: ObjectBrowserClass, item: TreeItem, isExpanded: boolean | undefined, counter: {
30
+ export declare function renderLeaf(that: ObjectBrowserClass, item: TreeItem, isExpanded: boolean | undefined,
31
+ /**
32
+ * Kept for the public `ObjectBrowserClass.renderLeaf`. `renderItem` counts the items now, so
33
+ * that a row which is served from the memo is counted as well
34
+ */
35
+ _counter?: {
31
36
  count: number;
32
37
  }): {
33
38
  row: JSX.Element;
34
39
  details: JSX.Element | null;
35
40
  };
36
41
  /**
37
- * Renders an item.
42
+ * Renders ONE row - without its children.
43
+ *
44
+ * The table renders only the rows around the visible area (see `ObjectBrowserClass.getWindow`), so
45
+ * the row of an item is built on its own and not as a part of the recursion over the tree.
46
+ */
47
+ export declare function renderRow(that: ObjectBrowserClass, root: TreeItem, isExpanded: boolean | undefined): JSX.Element;
48
+ /**
49
+ * Renders an item and all its expanded children.
50
+ *
51
+ * Kept for the public `ObjectBrowserClass.renderLeaf`. The table itself walks the flat list of
52
+ * `flattenItems` and renders only the rows of the window.
38
53
  */
39
54
  export declare function renderItem(that: ObjectBrowserClass, root: TreeItem, isExpanded: boolean | undefined, counter?: {
40
55
  count: number;
41
56
  }): (JSX.Element | null)[];
57
+ /** One row of the table, in the order in which the table shows it */
58
+ export interface FlatItem {
59
+ item: TreeItem;
60
+ /** An open folder looks different from a closed one, and only an open one shows its children */
61
+ isExpanded: boolean;
62
+ }
63
+ /**
64
+ * Walks the tree in EXACTLY the order in which `renderItem` renders it and returns the rows as one
65
+ * flat list.
66
+ *
67
+ * The table needs that list to render only the rows around the visible area and to know where a row
68
+ * that is not rendered at the moment would be - `scrollToItem` and the keyboard navigation used to
69
+ * read that from the DOM. Every change of the order in `renderItem` belongs here as well.
70
+ */
71
+ export declare function flattenItems(that: ObjectBrowserClass, root: TreeItem, result?: FlatItem[], counter?: {
72
+ count: number;
73
+ }): FlatItem[];
@@ -196,13 +196,13 @@ export function renderColumnValue(that, id, item, narrowStyleWithDetails) {
196
196
  if (!that.states[id]) {
197
197
  if (obj.type === 'state') {
198
198
  // we are waiting for state
199
- that.recordStates.add(id);
199
+ that.recordState(id, id);
200
200
  that.states[id] = { val: null };
201
201
  that.subscribe(id);
202
202
  }
203
203
  return null;
204
204
  }
205
- that.recordStates.add(id);
205
+ that.recordState(id, id);
206
206
  const state = that.states[id];
207
207
  let info = item.data.state;
208
208
  if (!info) {
@@ -330,10 +330,16 @@ export function renderAliasLink(that, id, index, customStyle) {
330
330
  /**
331
331
  * Renders a leaf.
332
332
  */
333
- export function renderLeaf(that, item, isExpanded, counter) {
333
+ export function renderLeaf(that, item, isExpanded,
334
+ /**
335
+ * Kept for the public `ObjectBrowserClass.renderLeaf`. `renderItem` counts the items now, so
336
+ * that a row which is served from the memo is counted as well
337
+ */
338
+ _counter) {
334
339
  const id = item.data.id;
335
- counter.count++;
336
340
  isExpanded = isExpanded === undefined ? that.state.expanded.includes(id) : isExpanded;
341
+ // the row records the states it shows anew (see `ObjectBrowserClass.recordState`)
342
+ delete that.rowStates[id];
337
343
  // icon
338
344
  let iconFolder;
339
345
  const obj = item.data.obj;
@@ -519,13 +525,13 @@ export function renderLeaf(that, item, isExpanded, counter) {
519
525
  ids[name] = _id;
520
526
  if (!that.states[_id]) {
521
527
  if (that.objects[_id]?.type === 'state') {
522
- that.recordStates.add(_id);
528
+ that.recordState(id, _id);
523
529
  that.states[_id] = { val: null };
524
530
  that.subscribe(_id);
525
531
  }
526
532
  }
527
533
  else {
528
- that.recordStates.add(_id);
534
+ that.recordState(id, _id);
529
535
  }
530
536
  });
531
537
  // calculate color
@@ -935,7 +941,7 @@ export function renderLeaf(that, item, isExpanded, counter) {
935
941
  // `renderItem` pushes this panel into the array of rows, so it needs its own key
936
942
  , {
937
943
  // `renderItem` pushes this panel into the array of rows, so it needs its own key
938
- key: `details_${id}`, elevation: 0, sx: styles.cellDetails },
944
+ key: `details_${id}`, "data-details-of": id, elevation: 0, sx: styles.cellDetails },
939
945
  React.createElement("div", { style: {
940
946
  ...styles.cellDetailsHeader,
941
947
  ...(hasLines ? styles.cellDetailsHeaderWithLines : undefined),
@@ -1035,32 +1041,64 @@ export function renderLeaf(that, item, isExpanded, counter) {
1035
1041
  return { row, details: colDetails };
1036
1042
  }
1037
1043
  /**
1038
- * Renders an item.
1044
+ * One row of the table.
1045
+ *
1046
+ * Building a row is expensive: it assembles up to a dozen cells and merges its styles for every
1047
+ * one of them (`Utils.getStyle` returns a fresh object each time, which the style engine has to
1048
+ * serialize anew). Until now a single state change rebuilt EVERY open row, because the browser
1049
+ * answers a value with `forceUpdate()` on the whole table - with a few hundred rows open that
1050
+ * blocked the main thread for a fifth of a second per value, and on an installation whose adapters
1051
+ * report all the time the table never stood still.
1052
+ *
1053
+ * The memo keeps the row as it is unless one of the states it shows changed or something happened
1054
+ * that concerns every row (filter, columns, theme, selection, a rebuilt tree - all of which count up
1055
+ * the epoch).
1039
1056
  */
1040
- export function renderItem(that, root, isExpanded, counter) {
1041
- const items = [];
1042
- counter = counter || { count: 0 };
1043
- const result = renderLeaf(that, root, isExpanded, counter);
1044
- let leaf;
1057
+ const ObjectBrowserRow = React.memo(function ObjectBrowserRow(props) {
1058
+ return props.build();
1059
+ }, (prev, next) => prev.epoch === next.epoch && prev.stateVersion === next.stateVersion && prev.isExpanded === next.isExpanded);
1060
+ /**
1061
+ * Renders ONE row - without its children.
1062
+ *
1063
+ * The table renders only the rows around the visible area (see `ObjectBrowserClass.getWindow`), so
1064
+ * the row of an item is built on its own and not as a part of the recursion over the tree.
1065
+ */
1066
+ export function renderRow(that, root, isExpanded) {
1067
+ const id = root.data.id;
1045
1068
  const DragWrapper = that.props.DragWrapper;
1046
- if (that.props.dragEnabled && DragWrapper) {
1047
- if (root.data.sumVisibility) {
1048
- leaf = (React.createElement(DragWrapper, { key: root.data.id, item: root, style: styles.draggable }, result.row));
1069
+ // Everything the row needs is built inside the memo: it does not run while the row is reused
1070
+ const build = () => {
1071
+ const result = renderLeaf(that, root, isExpanded);
1072
+ let leaf;
1073
+ if (that.props.dragEnabled && DragWrapper) {
1074
+ if (root.data.sumVisibility) {
1075
+ leaf = (React.createElement(DragWrapper, { key: id, item: root, style: styles.draggable }, result.row));
1076
+ }
1077
+ else {
1078
+ // change cursor
1079
+ leaf = (React.createElement("div", { key: id, style: styles.nonDraggable }, result.row));
1080
+ }
1049
1081
  }
1050
1082
  else {
1051
- // change cursor
1052
- leaf = (React.createElement("div", { key: root.data.id, style: styles.nonDraggable }, result.row));
1083
+ leaf = result.row;
1053
1084
  }
1054
- }
1055
- else {
1056
- leaf = result.row;
1057
- }
1058
- if (root.data.id && leaf) {
1059
- items.push(leaf);
1060
- }
1061
- if (result.details) {
1062
- items.push(result.details);
1063
- }
1085
+ return (React.createElement(React.Fragment, null,
1086
+ id ? leaf : null,
1087
+ result.details));
1088
+ };
1089
+ return (React.createElement(ObjectBrowserRow, { key: id || '__root__', build: build, epoch: that.renderEpoch, stateVersion: that.rowStateVersion[id] || 0, isExpanded: isExpanded === undefined ? binarySearch(that.state.expanded, id) : isExpanded }));
1090
+ }
1091
+ /**
1092
+ * Renders an item and all its expanded children.
1093
+ *
1094
+ * Kept for the public `ObjectBrowserClass.renderLeaf`. The table itself walks the flat list of
1095
+ * `flattenItems` and renders only the rows of the window.
1096
+ */
1097
+ export function renderItem(that, root, isExpanded, counter) {
1098
+ const items = [];
1099
+ counter = counter || { count: 0 };
1100
+ counter.count++;
1101
+ items.push(renderRow(that, root, isExpanded));
1064
1102
  isExpanded = isExpanded === undefined ? binarySearch(that.state.expanded, root.data.id) : isExpanded;
1065
1103
  if (!root.data.id || isExpanded) {
1066
1104
  if (!that.state.foldersFirst) {
@@ -1105,4 +1143,45 @@ export function renderItem(that, root, isExpanded, counter) {
1105
1143
  }
1106
1144
  return items;
1107
1145
  }
1146
+ /**
1147
+ * Walks the tree in EXACTLY the order in which `renderItem` renders it and returns the rows as one
1148
+ * flat list.
1149
+ *
1150
+ * The table needs that list to render only the rows around the visible area and to know where a row
1151
+ * that is not rendered at the moment would be - `scrollToItem` and the keyboard navigation used to
1152
+ * read that from the DOM. Every change of the order in `renderItem` belongs here as well.
1153
+ */
1154
+ export function flattenItems(that, root, result, counter) {
1155
+ result = result || [];
1156
+ counter = counter || { count: 0 };
1157
+ counter.count++;
1158
+ const isExpanded = binarySearch(that.state.expanded, root.data.id);
1159
+ result.push({ item: root, isExpanded });
1160
+ if ((!root.data.id || isExpanded) && root.children) {
1161
+ // do not render too many items in column editor mode
1162
+ const allowed = (item) => (!that.state.columnsSelectorShow || counter.count < 15) && !!item.data.sumVisibility;
1163
+ if (!that.state.foldersFirst) {
1164
+ for (const item of root.children) {
1165
+ if (allowed(item)) {
1166
+ flattenItems(that, item, result, counter);
1167
+ }
1168
+ }
1169
+ }
1170
+ else {
1171
+ // first only folders
1172
+ for (const item of root.children) {
1173
+ if (item.children && allowed(item)) {
1174
+ flattenItems(that, item, result, counter);
1175
+ }
1176
+ }
1177
+ // then items
1178
+ for (const item of root.children) {
1179
+ if (!item.children && allowed(item)) {
1180
+ flattenItems(that, item, result, counter);
1181
+ }
1182
+ }
1183
+ }
1184
+ }
1185
+ return result;
1186
+ }
1108
1187
  //# sourceMappingURL=renderLeaf.js.map