@bryntum/grid-react-thin 7.3.2 → 7.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.
Files changed (43) hide show
  1. package/README.md +4 -4
  2. package/lib/BryntumAIFilterField.d.ts +1 -1
  3. package/lib/BryntumAIFilterField.js +1 -1
  4. package/lib/BryntumAIFilterField.js.map +1 -1
  5. package/lib/BryntumChecklistFilterCombo.d.ts +1 -1
  6. package/lib/BryntumChecklistFilterCombo.js +1 -1
  7. package/lib/BryntumChecklistFilterCombo.js.map +1 -1
  8. package/lib/BryntumGrid.js +1 -0
  9. package/lib/BryntumGrid.js.map +1 -1
  10. package/lib/BryntumGridBase.js +1 -0
  11. package/lib/BryntumGridBase.js.map +1 -1
  12. package/lib/BryntumGridChartDesigner.d.ts +1 -1
  13. package/lib/BryntumGridChartDesigner.js +1 -1
  14. package/lib/BryntumGridChartDesigner.js.map +1 -1
  15. package/lib/BryntumGridFieldFilterPicker.d.ts +1 -1
  16. package/lib/BryntumGridFieldFilterPicker.js +1 -1
  17. package/lib/BryntumGridFieldFilterPicker.js.map +1 -1
  18. package/lib/BryntumGridFieldFilterPickerGroup.d.ts +1 -1
  19. package/lib/BryntumGridFieldFilterPickerGroup.js +1 -1
  20. package/lib/BryntumGridFieldFilterPickerGroup.js.map +1 -1
  21. package/lib/BryntumGroupBar.d.ts +1 -1
  22. package/lib/BryntumGroupBar.js +1 -1
  23. package/lib/BryntumGroupBar.js.map +1 -1
  24. package/lib/BryntumTreeCombo.d.ts +1 -1
  25. package/lib/BryntumTreeCombo.js +1 -1
  26. package/lib/BryntumTreeCombo.js.map +1 -1
  27. package/lib/BryntumTreeGrid.js +1 -0
  28. package/lib/BryntumTreeGrid.js.map +1 -1
  29. package/lib/WrapperHelper.js +30 -10
  30. package/lib/WrapperHelper.js.map +1 -1
  31. package/license.pdf +0 -0
  32. package/package.json +1 -1
  33. package/src/BryntumAIFilterField.tsx +7 -5
  34. package/src/BryntumChecklistFilterCombo.tsx +7 -5
  35. package/src/BryntumGrid.tsx +2 -1
  36. package/src/BryntumGridBase.tsx +2 -1
  37. package/src/BryntumGridChartDesigner.tsx +6 -5
  38. package/src/BryntumGridFieldFilterPicker.tsx +7 -6
  39. package/src/BryntumGridFieldFilterPickerGroup.tsx +7 -6
  40. package/src/BryntumGroupBar.tsx +6 -5
  41. package/src/BryntumTreeCombo.tsx +7 -5
  42. package/src/BryntumTreeGrid.tsx +2 -1
  43. package/src/WrapperHelper.tsx +72 -21
@@ -50,6 +50,7 @@ type ReactComponentState = {
50
50
  type HTMLElementWithData = HTMLElement & {
51
51
  elementData?: any
52
52
  lastPortalId?: string | null
53
+ jsxContainer?: HTMLElement | null
53
54
  didSetTextContent?: boolean
54
55
  };
55
56
 
@@ -515,9 +516,11 @@ function processCellContent(this: { reactComponent: any; isExporting: boolean; $
515
516
  else {
516
517
  // Create new portal container
517
518
  const portalContainer = DomHelper.append(renderElement, {
518
- tag : 'div',
519
- className : portalContainerClass,
520
- dataset : { portalId } // for reference in tests
519
+ tag : 'div',
520
+ className : portalContainerClass,
521
+ dataset : { portalId }, // for reference in tests
522
+ // We don't want portal container to be reused in the DomConfig flip for this cell
523
+ retainElement : true
521
524
  });
522
525
  renderElement.portalContainer = portalContainer;
523
526
 
@@ -542,9 +545,27 @@ function processCellContent(this: { reactComponent: any; isExporting: boolean; $
542
545
  cellElementData.hasPortal = true;
543
546
  portal.generation = record.generation;
544
547
  }
545
- else if (!rendererHtml && cellElementData.hasPortal) {
546
- cellElement.portalContainer.remove();
547
- cellElementData.hasPortal = false;
548
+ else if (cellElementData.hasPortal) {
549
+ // The previous JSX cell content can be replaced by DomConfig object so in that case,
550
+ // we want to delete the previously created portal for JSX. See:
551
+ // https://github.com/bryntum/support/issues/7862
552
+ const
553
+ container = renderElement.portalContainer || renderElement.querySelector(`.${portalContainerClass}`),
554
+ ownedPortalId = container?.dataset.portalId,
555
+ portal = ownedPortalId && state.portals.get(ownedPortalId);
556
+
557
+ if (portal && portal.containerInfo === container) {
558
+ deletePortal(component, ownedPortalId);
559
+ flushSync(() => updateGeneration(component, true));
560
+ }
561
+ else {
562
+ // No live portal owns this container. Since retainElement is true,
563
+ // we have to remove it manually
564
+ container?.remove();
565
+ }
566
+
567
+ renderElement.portalContainer = null;
568
+ cellElementData.hasPortal = false;
548
569
  }
549
570
  }
550
571
 
@@ -1121,9 +1142,16 @@ function processEventContent(this: any, {
1121
1142
  }) as Element,
1122
1143
  portal = ReactDOM.createPortal(jsx, jsxContainer);
1123
1144
 
1124
- // Store eventGeneration on the portal to easily determine later if it needs to be updated
1145
+ // currentData resolves renderData in vertical mode, where eventGeneration lives one level
1146
+ // deeper than elementData; reading elementData directly would always yield undefined.
1125
1147
  // @ts-expect-error
1126
- portal.eventGeneration = wrap.elementData.eventGeneration;
1148
+ portal.eventGeneration = currentData.eventGeneration;
1149
+
1150
+ // Track the container so we can detect below when the event element is replaced while its
1151
+ // generation is unchanged (orientation switch, constrainDragToTimeline: false drag proxy), leaving
1152
+ // the portal in a detached container. https://github.com/bryntum/support/issues/13266
1153
+ // @ts-expect-error
1154
+ portal.jsxContainer = jsxContainer;
1127
1155
 
1128
1156
  // Store the React element so that we can reuse it when reusing wrap element
1129
1157
  wrap.lastJSX = jsx;
@@ -1150,8 +1178,14 @@ function processEventContent(this: any, {
1150
1178
 
1151
1179
  // Recurring events need re-creating always:
1152
1180
  // - https://github.com/bryntum/support/issues/10595
1153
- // Re-create portal only if the underlying eventRecord changed its generation or if exporting
1154
- if (!portal || isRecurring || isExporting || portal.eventGeneration !== wrap.elementData.eventGeneration || this.$isExpanding) {
1181
+ // Re-create portal only if the underlying eventRecord changed its generation or if exporting.
1182
+ // Use currentData.eventGeneration (not wrap.elementData.eventGeneration): in vertical mode
1183
+ // eventGeneration lives in elementData.renderData, so wrap.elementData.eventGeneration is always
1184
+ // undefined and stale portals would never be detected.
1185
+ // Also re-create when the container is detached: generation can be unchanged while the event element
1186
+ // is replaced (orientation switch, constrainDragToTimeline: false drag proxy).
1187
+ // https://github.com/bryntum/support/issues/13266
1188
+ if (!portal || isRecurring || isExporting || portal.eventGeneration !== currentData.eventGeneration || this.$isExpanding || !parent?.contains(portal.jsxContainer)) {
1155
1189
  if ((scrolling || !isDragging) && !isEditing) {
1156
1190
  updateContent();
1157
1191
  }
@@ -1173,7 +1207,7 @@ function processEventContent(this: any, {
1173
1207
  */
1174
1208
  function processResourceHeader(
1175
1209
  { jsx, targetElement }:
1176
- { jsx: ReactElement; targetElement: HTMLElement }): void {
1210
+ { jsx: ReactElement; targetElement: HTMLElementWithData }): void {
1177
1211
 
1178
1212
  if (!jsx) {
1179
1213
  return;
@@ -1186,19 +1220,36 @@ function processResourceHeader(
1186
1220
  { portals } = state,
1187
1221
  portalId = `resource-header-${targetElement.dataset.resourceId}`;
1188
1222
 
1189
- // Delete portal if we already have one
1190
- if (portals.has(portalId)) {
1191
- portals.delete(portalId);
1192
-
1193
- // Update the generation (force React re-rendering) in this tick
1194
- updateGeneration(reactComponent, true);
1223
+ // Render the JSX into a retained container instead of straight into the header cell. DomSync owns the
1224
+ // header cell and, on every re-sync (e.g. when resizing the resource columns), it releases any DOM children
1225
+ // it did not create itself - adding the `b-released` class which hides the React-rendered content. A
1226
+ // `b-jsx-container` flagged with `retainElement` is left untouched by DomSync. The container is given
1227
+ // `display: contents` so it stays transparent to the header cell's flex layout.
1228
+ // The container is cached on the cell rather than queried from the DOM each pass.
1229
+ let jsxContainer = targetElement.jsxContainer;
1230
+
1231
+ if (!jsxContainer) {
1232
+ // Clear any plain content DomSync may have placed in the cell before hosting the portal
1233
+ targetElement.textContent = '';
1234
+ targetElement.jsxContainer = jsxContainer = DomHelper.createElement({
1235
+ className : 'b-jsx-container',
1236
+ parent : targetElement,
1237
+ retainElement : true,
1238
+ style : { display : 'contents' }
1239
+ }) as HTMLElement;
1240
+ }
1241
+ // When a header cell is reused for a different resource, drop the now stale portal
1242
+ else if (targetElement.lastPortalId && targetElement.lastPortalId !== portalId) {
1243
+ portals.delete(targetElement.lastPortalId);
1195
1244
  }
1196
1245
 
1197
- portals.set(portalId, ReactDOM.createPortal(jsx, targetElement));
1198
-
1199
- // Trigger the React portals re-rendering in the next animation frame
1200
- updateGeneration(reactComponent);
1246
+ // (Re)create the portal so resource record changes are reflected. Setting the same
1247
+ // id replaces any existing portal, so no explicit delete is needed beforehand.
1248
+ portals.set(portalId, ReactDOM.createPortal(jsx, jsxContainer));
1249
+ targetElement.lastPortalId = portalId;
1201
1250
 
1251
+ // Force React to re-render the portals in this tick
1252
+ updateGeneration(reactComponent, true);
1202
1253
  }
1203
1254
 
1204
1255
  /**