@handsontable/react-wrapper 17.1.0-rc9 → 18.0.0-rc1

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.
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useContext, createContext, useRef, useCallback, useMemo, useState, useDeferredValue, useImperativeHandle, forwardRef, Fragment, Children, useId } from 'react';
1
+ import React, { useEffect, useContext, useRef, useCallback, useMemo, createContext, useState, useDeferredValue, useImperativeHandle, forwardRef, Fragment, Children, useId } from 'react';
2
2
  import ReactDOM from 'react-dom';
3
3
  import Handsontable from 'handsontable/base';
4
4
  import { getRenderer } from 'handsontable/renderers/registry';
@@ -176,7 +176,7 @@ function _toPrimitive(t, r) {
176
176
  if ("object" != typeof i) return i;
177
177
  throw new TypeError("@@toPrimitive must return a primitive value.");
178
178
  }
179
- return (String )(t);
179
+ return ("string" === r ? String : Number)(t);
180
180
  }
181
181
  function _toPropertyKey(t) {
182
182
  var i = _toPrimitive(t, "string");
@@ -345,11 +345,18 @@ function createPortal(rElement) {
345
345
  if (!ownerDocument) {
346
346
  ownerDocument = document;
347
347
  }
348
- if (!bulkComponentContainer) {
349
- bulkComponentContainer = ownerDocument.createDocumentFragment();
348
+ var portalContainer = cachedContainer;
349
+
350
+ // A new container needs an anchor before React mounts into it. A cached
351
+ // container is already attached (typically inside its TD) and must be
352
+ // left in place to avoid wiping the DOM on every grid render.
353
+ if (!portalContainer) {
354
+ if (!bulkComponentContainer) {
355
+ bulkComponentContainer = ownerDocument.createDocumentFragment();
356
+ }
357
+ portalContainer = ownerDocument.createElement('DIV');
358
+ bulkComponentContainer.appendChild(portalContainer);
350
359
  }
351
- var portalContainer = cachedContainer !== null && cachedContainer !== void 0 ? cachedContainer : ownerDocument.createElement('DIV');
352
- bulkComponentContainer.appendChild(portalContainer);
353
360
  return {
354
361
  portal: /*#__PURE__*/ReactDOM.createPortal(rElement, portalContainer, portalKey),
355
362
  portalContainer: portalContainer
@@ -525,6 +532,9 @@ var HotTableContextProvider = function HotTableContextProvider(_ref) {
525
532
  var setHotColumnSettings = useCallback(function (columnSettings, columnIndex) {
526
533
  columnsSettings.current[columnIndex] = columnSettings;
527
534
  }, []);
535
+ var trimColumnSettings = useCallback(function (length) {
536
+ columnsSettings.current.length = length;
537
+ }, []);
528
538
  var componentRendererColumns = useRef(new Map());
529
539
  var renderedCellCache = useRef(new Map());
530
540
  var clearRenderedCellCache = useCallback(function () {
@@ -543,38 +553,36 @@ var HotTableContextProvider = function HotTableContextProvider(_ref) {
543
553
  var instanceGuid = instance.guid;
544
554
  var portalContainerKey = "".concat(instanceGuid, "-").concat(key);
545
555
  var portalKey = "".concat(key, "-").concat(instanceGuid);
546
- if (renderedCellCache.current.has(key)) {
547
- TD.innerHTML = renderedCellCache.current.get(key).innerHTML;
548
- }
549
556
  if (TD && !TD.getAttribute('ghost-table')) {
550
- var cachedPortal = portalCache.current.get(portalKey);
551
557
  var cachedPortalContainer = portalContainerCache.current.get(portalContainerKey);
552
- while (TD.firstChild) {
553
- TD.removeChild(TD.firstChild);
554
- }
555
-
556
- // if portal already exists, do not recreate
557
- if (cachedPortal && cachedPortalContainer) {
558
- TD.appendChild(cachedPortalContainer);
559
- } else {
560
- var rendererElement = /*#__PURE__*/React.createElement(Renderer, {
561
- instance: instance,
562
- TD: TD,
563
- row: row,
564
- col: col,
565
- prop: prop,
566
- value: value,
567
- cellProperties: cellProperties
568
- });
569
- var _createPortal = createPortal(rendererElement, TD.ownerDocument, portalKey, cachedPortalContainer),
570
- portal = _createPortal.portal,
571
- portalContainer = _createPortal.portalContainer;
572
- portalContainerCache.current.set(portalContainerKey, portalContainer);
558
+ // When the cached portal container is still attached to the same
559
+ // TD as the previous render, the DOM is already correct and must
560
+ // not be wiped. Wiping detaches the React-managed children, which
561
+ // forces a full remount of the renderer component on every grid
562
+ // render (see issue #10800).
563
+ var containerInPlace = !!cachedPortalContainer && cachedPortalContainer.parentNode === TD;
564
+ var rendererElement = /*#__PURE__*/React.createElement(Renderer, {
565
+ instance: instance,
566
+ TD: TD,
567
+ row: row,
568
+ col: col,
569
+ prop: prop,
570
+ value: value,
571
+ cellProperties: cellProperties
572
+ });
573
+ var _createPortal = createPortal(rendererElement, TD.ownerDocument, portalKey, cachedPortalContainer),
574
+ portal = _createPortal.portal,
575
+ portalContainer = _createPortal.portalContainer;
576
+ if (!containerInPlace) {
577
+ while (TD.firstChild) {
578
+ TD.removeChild(TD.firstChild);
579
+ }
573
580
  TD.appendChild(portalContainer);
574
- portalCache.current.set(portalKey, portal);
575
581
  }
582
+ portalContainerCache.current.set(portalContainerKey, portalContainer);
583
+ portalCache.current.set(portalKey, portal);
576
584
  }
577
- renderedCellCache.current.set("".concat(row, "-").concat(col), TD);
585
+ renderedCellCache.current.set(key, TD);
578
586
  return TD;
579
587
  };
580
588
  }, []);
@@ -592,13 +600,14 @@ var HotTableContextProvider = function HotTableContextProvider(_ref) {
592
600
  componentRendererColumns: componentRendererColumns.current,
593
601
  columnsSettings: columnsSettings.current,
594
602
  emitColumnSettings: setHotColumnSettings,
603
+ trimColumnSettings: trimColumnSettings,
595
604
  getRendererWrapper: getRendererWrapper,
596
605
  clearPortalCache: clearPortalCache,
597
606
  clearRenderedCellCache: clearRenderedCellCache,
598
607
  setRenderersPortalManagerRef: setRenderersPortalManagerRef,
599
608
  pushCellPortalsIntoPortalManager: pushCellPortalsIntoPortalManager
600
609
  };
601
- }, [setHotColumnSettings, getRendererWrapper, clearRenderedCellCache, setRenderersPortalManagerRef, pushCellPortalsIntoPortalManager]);
610
+ }, [setHotColumnSettings, trimColumnSettings, getRendererWrapper, clearRenderedCellCache, setRenderersPortalManagerRef, pushCellPortalsIntoPortalManager]);
602
611
  return /*#__PURE__*/React.createElement(HotTableContext.Provider, {
603
612
  value: contextImpl
604
613
  }, children);
@@ -667,11 +676,12 @@ function makeEditorClass(hooksRef, instanceRef) {
667
676
  args[_key] = arguments[_key];
668
677
  }
669
678
  if (!AbstractMethods.includes(propName)) {
670
- result = baseMethod.call.apply(baseMethod, [this].concat(args)); // call super
679
+ var _ref;
680
+ result = (_ref = baseMethod).call.apply(_ref, [this].concat(args)); // call super
671
681
  }
672
682
  if (MethodsMap[propName] && (_hooksRef$current = hooksRef.current) !== null && _hooksRef$current !== void 0 && _hooksRef$current[MethodsMap[propName]]) {
673
- var _ref;
674
- result = (_ref = hooksRef.current[MethodsMap[propName]]).call.apply(_ref, [this].concat(args));
683
+ var _ref2;
684
+ result = (_ref2 = hooksRef.current[MethodsMap[propName]]).call.apply(_ref2, [this].concat(args));
675
685
  }
676
686
  return result;
677
687
  }.bind(_this);
@@ -712,10 +722,10 @@ var EditorContext = /*#__PURE__*/createContext(undefined);
712
722
  * @param {Ref} hooksRef Reference for component-based editor overridden hooks object.
713
723
  * @param {RefObject} hotCustomEditorInstanceRef Reference to Handsontable-native editor instance.
714
724
  */
715
- var EditorContextProvider = function EditorContextProvider(_ref2) {
716
- var hooksRef = _ref2.hooksRef,
717
- hotCustomEditorInstanceRef = _ref2.hotCustomEditorInstanceRef,
718
- children = _ref2.children;
725
+ var EditorContextProvider = function EditorContextProvider(_ref3) {
726
+ var hooksRef = _ref3.hooksRef,
727
+ hotCustomEditorInstanceRef = _ref3.hotCustomEditorInstanceRef,
728
+ children = _ref3.children;
719
729
  return /*#__PURE__*/React.createElement(EditorContext.Provider, {
720
730
  value: {
721
731
  hooksRef: hooksRef,
@@ -784,9 +794,9 @@ function applyEditorPosition(el, editor, hot, td) {
784
794
  * @returns {UseHotEditorImpl} Editor API methods
785
795
  */
786
796
  function useHotEditor(overriddenHooks, deps) {
787
- var _ref3 = useContext(EditorContext),
788
- hooksRef = _ref3.hooksRef,
789
- hotCustomEditorInstanceRef = _ref3.hotCustomEditorInstanceRef;
797
+ var _ref4 = useContext(EditorContext),
798
+ hooksRef = _ref4.hooksRef,
799
+ hotCustomEditorInstanceRef = _ref4.hotCustomEditorInstanceRef;
790
800
  var _useState = useState(0),
791
801
  _useState2 = _slicedToArray(_useState, 2),
792
802
  rerenderTrigger = _useState2[0],
@@ -830,11 +840,13 @@ function useHotEditor(overriddenHooks, deps) {
830
840
  },
831
841
  get row() {
832
842
  var _hotCustomEditorInsta6;
833
- return (_hotCustomEditorInsta6 = hotCustomEditorInstanceRef.current) === null || _hotCustomEditorInsta6 === void 0 ? void 0 : _hotCustomEditorInsta6.row;
843
+ var row = (_hotCustomEditorInsta6 = hotCustomEditorInstanceRef.current) === null || _hotCustomEditorInsta6 === void 0 ? void 0 : _hotCustomEditorInsta6.row;
844
+ return row !== null && row !== void 0 ? row : undefined;
834
845
  },
835
846
  get col() {
836
847
  var _hotCustomEditorInsta7;
837
- return (_hotCustomEditorInsta7 = hotCustomEditorInstanceRef.current) === null || _hotCustomEditorInsta7 === void 0 ? void 0 : _hotCustomEditorInsta7.col;
848
+ var col = (_hotCustomEditorInsta7 = hotCustomEditorInstanceRef.current) === null || _hotCustomEditorInsta7 === void 0 ? void 0 : _hotCustomEditorInsta7.col;
849
+ return col !== null && col !== void 0 ? col : undefined;
838
850
  }
839
851
  };
840
852
  }, [rerenderTrigger, hotCustomEditorInstanceRef, deferredValue]);
@@ -844,23 +856,23 @@ function useHotEditor(overriddenHooks, deps) {
844
856
 
845
857
  // EditorComponent props - children typed to work with JSX syntax
846
858
 
847
- function EditorComponent(_ref4) {
848
- var _onPrepare = _ref4.onPrepare,
849
- _onClose = _ref4.onClose,
850
- _onOpen = _ref4.onOpen,
851
- _onFocus = _ref4.onFocus,
852
- children = _ref4.children,
853
- _ref4$shortcutsGroup = _ref4.shortcutsGroup,
854
- shortcutsGroup = _ref4$shortcutsGroup === void 0 ? "custom-editor" : _ref4$shortcutsGroup,
855
- shortcuts = _ref4.shortcuts;
859
+ function EditorComponent(_ref5) {
860
+ var _onPrepare = _ref5.onPrepare,
861
+ _onClose = _ref5.onClose,
862
+ _onOpen = _ref5.onOpen,
863
+ _onFocus = _ref5.onFocus,
864
+ children = _ref5.children,
865
+ _ref5$shortcutsGroup = _ref5.shortcutsGroup,
866
+ shortcutsGroup = _ref5$shortcutsGroup === void 0 ? "custom-editor" : _ref5$shortcutsGroup,
867
+ shortcuts = _ref5.shortcuts;
856
868
  var mainElementRef = useRef(null);
857
869
  var currentValue = useRef(undefined);
858
870
  var _useState5 = useState(),
859
871
  _useState6 = _slicedToArray(_useState5, 2),
860
872
  themeClassName = _useState6[0],
861
873
  setThemeClassName = _useState6[1];
862
- var _ref5 = useContext(EditorContext),
863
- hotCustomEditorInstanceRef = _ref5.hotCustomEditorInstanceRef;
874
+ var _ref6 = useContext(EditorContext),
875
+ hotCustomEditorInstanceRef = _ref6.hotCustomEditorInstanceRef;
864
876
  var registerShortcuts = useCallback(function () {
865
877
  var _hotCustomEditorInsta8, _hotCustomEditorInsta9, _hotCustomEditorInsta0;
866
878
  if (!((_hotCustomEditorInsta8 = hotCustomEditorInstanceRef.current) !== null && _hotCustomEditorInsta8 !== void 0 && _hotCustomEditorInsta8.hot)) return;
@@ -1064,7 +1076,7 @@ var HotColumn = function HotColumn(props) {
1064
1076
  }, editorPortal);
1065
1077
  };
1066
1078
 
1067
- var version="17.1.0-rc9";
1079
+ var version="18.0.0-rc1";
1068
1080
 
1069
1081
  /**
1070
1082
  * Component used to manage the renderer component portals.
@@ -2320,6 +2332,12 @@ var HotTableInner = /*#__PURE__*/forwardRef(function (props, ref) {
2320
2332
  * Initialize Handsontable after the component has mounted.
2321
2333
  */
2322
2334
  useEffect(function () {
2335
+ // React guarantees child effects run before parent effects on each
2336
+ // commit, so by the time this parent useEffect runs, every HotColumn
2337
+ // has already written its slot. Trim to drop any leftover slots from
2338
+ // a previous mount (e.g. StrictMode's double-invoke or HMR).
2339
+ var hotColumnCount = Children.toArray(props.children).filter(isHotColumn).length;
2340
+ context.trimColumnSettings(hotColumnCount);
2323
2341
  var newGlobalSettings = createNewGlobalSettings(true);
2324
2342
 
2325
2343
  // Update prevProps with the current props
@@ -2362,6 +2380,13 @@ var HotTableInner = /*#__PURE__*/forwardRef(function (props, ref) {
2362
2380
  useUpdateEffect(function () {
2363
2381
  clearCache();
2364
2382
  var hotInstance = getHotInstance();
2383
+
2384
+ // React guarantees child effects run before parent effects on each
2385
+ // commit, so by the time this parent useUpdateEffect runs, every
2386
+ // surviving HotColumn has already written its slot. Trim to drop
2387
+ // stale entries left behind by HotColumns that unmounted.
2388
+ var hotColumnCount = Children.toArray(props.children).filter(isHotColumn).length;
2389
+ context.trimColumnSettings(hotColumnCount);
2365
2390
  var newGlobalSettings = createNewGlobalSettings(false, prevProps.current);
2366
2391
 
2367
2392
  // Update prevProps with the current props
@@ -2399,7 +2424,11 @@ var HotTableInner = /*#__PURE__*/forwardRef(function (props, ref) {
2399
2424
  var editorPortal = createEditorPortal(getOwnerDocument(), props.editor);
2400
2425
  return /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("div", _extends({
2401
2426
  ref: hotElementRef
2402
- }, containerProps), hotColumnWrapped), /*#__PURE__*/React.createElement(RenderersPortalManager, {
2427
+ }, containerProps, {
2428
+ style: _objectSpread2({
2429
+ height: '100%'
2430
+ }, containerProps.style)
2431
+ }), hotColumnWrapped), /*#__PURE__*/React.createElement(RenderersPortalManager, {
2403
2432
  ref: context.setRenderersPortalManagerRef
2404
2433
  }), /*#__PURE__*/React.createElement(EditorContextProvider, {
2405
2434
  hooksRef: globalEditorHooksRef,
@@ -19,6 +19,13 @@ export interface HotTableContextImpl {
19
19
  * @param {Number} columnIndex Column index.
20
20
  */
21
21
  readonly emitColumnSettings: (columnSettings: Handsontable.ColumnSettings, columnIndex: number) => void;
22
+ /**
23
+ * Trim the column settings array to the given length. Used to drop slots
24
+ * left over from HotColumn children that have unmounted.
25
+ *
26
+ * @param {Number} length Target length for the column settings array.
27
+ */
28
+ readonly trimColumnSettings: (length: number) => void;
22
29
  /**
23
30
  * Return a renderer wrapper function for the provided renderer component.
24
31
  *
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
2
  import { HotTableProps, HotTableRef } from './types';
3
- declare const HotTableInner: React.ForwardRefExoticComponent<HotTableProps & React.RefAttributes<HotTableRef>>;
3
+ declare const HotTableInner: React.ForwardRefExoticComponent<Pick<HotTableProps, string | number> & React.RefAttributes<HotTableRef>>;
4
4
  export default HotTableInner;
5
5
  export { HotTableInner };
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@handsontable/react-wrapper",
3
- "version": "17.1.0-rc9",
3
+ "version": "18.0.0-rc1",
4
4
  "description": "Best Data Grid for React with Spreadsheet Look and Feel.",
5
5
  "author": "Handsoncode <hello@handsoncode.net> (https://handsoncode.net)",
6
6
  "homepage": "https://handsontable.com",
7
7
  "license": "SEE LICENSE IN LICENSE.txt",
8
+ "sideEffects": false,
8
9
  "main": "./commonjs/react-handsontable.js",
9
10
  "module": "./es/react-handsontable.mjs",
10
11
  "jsdelivr": "./dist/react-handsontable.min.js",
@@ -72,7 +73,7 @@
72
73
  "@types/react-dom": "^18.2.0",
73
74
  "@types/react-redux": "^7.1.7",
74
75
  "cross-env": "^7.0.3",
75
- "handsontable": "^17.0.0",
76
+ "handsontable": "^18.0.0",
76
77
  "jest": "^27.5.1",
77
78
  "jest-environment-jsdom": "^27.5.1",
78
79
  "prop-types": "^15.7.2",
@@ -87,7 +88,7 @@
87
88
  "uglify-js": "^3.4.9"
88
89
  },
89
90
  "peerDependencies": {
90
- "handsontable": "^17.0.0"
91
+ "handsontable": "^18.0.0"
91
92
  },
92
93
  "scripts": {
93
94
  "build": "npm run clean && npm run build:commonjs && npm run build:es && npm run build:umd && npm run build:min && npm run prepare:types",
@@ -107,6 +108,7 @@
107
108
  "jest": {
108
109
  "testEnvironment": "jsdom",
109
110
  "testURL": "http://localhost/",
111
+ "testTimeout": 60000,
110
112
  "transform": {
111
113
  "^.+\\.tsx?$": "babel-jest",
112
114
  "^.+\\.js$": "babel-jest"
package/types.d.ts CHANGED
@@ -38,12 +38,17 @@ export interface UseHotEditorImpl<T> {
38
38
  }
39
39
  /**
40
40
  * Helper type to expose GridSettings/ColumnSettings props with native renderers/editors separately
41
- * from component-based render prop.
41
+ * from component-based render prop. Uses conditional types so it works with both GridSettings
42
+ * and ColumnSettings (ColumnSettings' index signature can make it incompatible with strict Pick<>).
42
43
  */
43
- declare type ReplaceRenderersEditors<T extends Pick<Handsontable.GridSettings, 'renderer' | 'editor'>> = Omit<T, 'renderer' | 'editor'> & {
44
- hotRenderer?: T['renderer'];
44
+ declare type ReplaceRenderersEditors<T> = Omit<T, 'renderer' | 'editor'> & {
45
+ hotRenderer?: T extends {
46
+ renderer?: infer R;
47
+ } ? R : never;
45
48
  renderer?: ComponentType<HotRendererProps>;
46
- hotEditor?: T['editor'];
49
+ hotEditor?: T extends {
50
+ editor?: infer E;
51
+ } ? E : never;
47
52
  editor?: ComponentType | boolean;
48
53
  };
49
54
  /**