@linzjs/step-ag-grid 31.2.5 → 32.1.0

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 (69) hide show
  1. package/dist/index.css +5 -1
  2. package/dist/src/components/GridCell.d.ts +1 -1
  3. package/dist/src/components/GridLoadableCell.d.ts +1 -1
  4. package/dist/src/components/GridNoRowsOverlay.d.ts +1 -1
  5. package/dist/src/components/GridPopoverHook.d.ts +1 -1
  6. package/dist/src/components/gridFilter/GridFilterDownloadCsvButton.d.ts +1 -1
  7. package/dist/src/components/gridFilter/GridFilterQuick.d.ts +1 -1
  8. package/dist/src/components/gridFilter/GridFilters.d.ts +1 -1
  9. package/dist/src/components/gridForm/GridFormDropDown.d.ts +1 -1
  10. package/dist/src/components/gridForm/GridFormEditBearing.d.ts +1 -1
  11. package/dist/src/components/gridForm/GridFormInlineTextInput.d.ts +1 -1
  12. package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +2 -2
  13. package/dist/src/components/gridForm/GridFormPopoverMenu.d.ts +1 -1
  14. package/dist/src/components/gridForm/GridFormTextInput.d.ts +1 -1
  15. package/dist/src/components/gridHeader/GridHeaderSelect.d.ts +1 -1
  16. package/dist/src/components/gridHook/useGridContextMenu.d.ts +1 -1
  17. package/dist/src/components/gridHook/useGridCopy.d.ts +1 -1
  18. package/dist/src/components/gridHook/useGridRangeSelection.d.ts +1 -1
  19. package/dist/src/components/gridRender/GridRenderPopoutMenuCell.d.ts +1 -1
  20. package/dist/src/components/types.d.ts +2 -0
  21. package/dist/src/contexts/GridPopoverContextProvider.d.ts +1 -1
  22. package/dist/src/contexts/GridUpdatingContextProvider.d.ts +1 -1
  23. package/dist/src/lui/FormError.d.ts +1 -1
  24. package/dist/src/lui/TextAreaInput.d.ts +1 -1
  25. package/dist/src/react-menu3/components/ControlledMenu.d.ts +1 -1
  26. package/dist/src/react-menu3/components/FocusableItem.d.ts +1 -1
  27. package/dist/src/react-menu3/components/Menu.d.ts +1 -1
  28. package/dist/src/react-menu3/components/MenuGroup.d.ts +1 -1
  29. package/dist/src/react-menu3/components/MenuHeader.d.ts +1 -1
  30. package/dist/src/react-menu3/components/MenuList.d.ts +1 -1
  31. package/dist/src/react-menu3/components/MenuRadioGroup.d.ts +1 -1
  32. package/dist/src/react-menu3/components/SubMenu.d.ts +1 -1
  33. package/dist/src/utils/useSharedInterval.d.ts +5 -0
  34. package/dist/step-ag-grid.cjs +259 -105
  35. package/dist/step-ag-grid.cjs.map +1 -1
  36. package/dist/step-ag-grid.esm.js +260 -106
  37. package/dist/step-ag-grid.esm.js.map +1 -1
  38. package/package.json +29 -30
  39. package/src/components/Grid.tsx +6 -3
  40. package/src/components/gridForm/GridFormPopoverMenu.tsx +6 -2
  41. package/src/components/gridHook/useGridRangeSelection.ts +1 -1
  42. package/src/components/gridPopoverEdit/GridEditBoolean.tsx +64 -26
  43. package/src/components/gridPopoverEdit/GridPopoverMenu.tsx +3 -0
  44. package/src/components/types.ts +2 -0
  45. package/src/contexts/GridContextProvider.tsx +18 -5
  46. package/src/react-app-env.d.ts +4 -1
  47. package/src/stories/grid/GridFilterColumnsMultiSelect.stories.tsx +1 -1
  48. package/src/stories/grid/GridNonEditableRow.stories.tsx +2 -2
  49. package/src/stories/grid/GridPopoutEditBoolean.stories.tsx +48 -9
  50. package/src/stories/grid/GridReadOnly.stories.tsx +69 -0
  51. package/src/stories/grid/gridFormInteraction/GridFormDropDownInteraction.stories.tsx +1 -1
  52. package/src/stories/grid/gridFormInteraction/GridFormEditBearingCorrectionInteraction.stories.tsx +1 -1
  53. package/src/stories/grid/gridFormInteraction/GridFormEditBearingInteraction.stories.tsx +1 -1
  54. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectGridInteraction.stories.tsx +1 -1
  55. package/src/stories/grid/gridFormInteraction/GridFormMultiSelectInteraction.stories.tsx +1 -1
  56. package/src/stories/grid/gridFormInteraction/GridFormPopoverMenuInteraction.stories.tsx +48 -1
  57. package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +1 -1
  58. package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +1 -1
  59. package/src/stories/grid/gridFormStatic/GridFormDropDown.stories.tsx +1 -1
  60. package/src/stories/grid/gridFormStatic/GridFormEditBearing.stories.tsx +1 -1
  61. package/src/stories/grid/gridFormStatic/GridFormEditBearingCorrection.stories.tsx +1 -1
  62. package/src/stories/grid/gridFormStatic/GridFormMessage.stories.tsx +1 -1
  63. package/src/stories/grid/gridFormStatic/GridFormMultiSelect.stories.tsx +1 -1
  64. package/src/stories/grid/gridFormStatic/GridFormPopoverMenu.stories.tsx +1 -1
  65. package/src/stories/grid/gridFormStatic/GridFormTextArea.stories.tsx +1 -1
  66. package/src/stories/grid/gridFormStatic/GridFormTextInput.stories.tsx +1 -1
  67. package/src/styles/GridLoadableCell.scss +5 -1
  68. package/src/utils/useSharedInterval.ts +57 -0
  69. package/src/utils/util.ts +4 -2
@@ -58456,8 +58456,10 @@ function requireLib () {
58456
58456
  }
58457
58457
 
58458
58458
  var libExports = requireLib();
58459
- var natsort = /*@__PURE__*/getDefaultExportFromCjs(libExports);
58459
+ var natsortModule = /*@__PURE__*/getDefaultExportFromCjs(libExports);
58460
58460
 
58461
+ // natsort is a CJS module that sets exports.default, so interop wraps it in an extra layer
58462
+ const natsort = natsortModule.default ?? natsortModule;
58461
58463
  const isNotEmpty = lodashEs.negate(lodashEs.isEmpty);
58462
58464
  const wait = (timeoutMs) => new Promise((resolve) => {
58463
58465
  setTimeout(resolve, timeoutMs);
@@ -58499,7 +58501,7 @@ const fnOrVar = (fn, param) => (typeof fn === 'function' ? fn(param) : fn);
58499
58501
  const sanitiseFileName = (filename) => {
58500
58502
  const valid = filename
58501
58503
  .trim()
58502
- .replaceAll(/(\/|\\)+/g, '-')
58504
+ .replaceAll(/([\/\\])+/g, '-')
58503
58505
  .replaceAll(/\s+/g, '_')
58504
58506
  .replaceAll(/[^\w\-āēīōūĀĒĪŌŪ.]/g, '');
58505
58507
  const parts = valid.split('.');
@@ -59022,14 +59024,6 @@ const useMenuChange = (onMenuChange, isOpen) => {
59022
59024
  }, [onMenuChange, isOpen]);
59023
59025
  };
59024
59026
 
59025
- //#region src/utils.ts
59026
- const PRE_ENTER = 0;
59027
- const ENTERING = 1;
59028
- const ENTERED = 2;
59029
- const PRE_EXIT = 3;
59030
- const EXITING = 4;
59031
- const EXITED = 5;
59032
- const UNMOUNTED = 6;
59033
59027
  const STATUS = [
59034
59028
  "preEnter",
59035
59029
  "entering",
@@ -59040,67 +59034,76 @@ const STATUS = [
59040
59034
  "unmounted"
59041
59035
  ];
59042
59036
  const getState = (status) => ({
59043
- _s: status,
59037
+ $: status,
59044
59038
  status: STATUS[status],
59045
- isEnter: status < PRE_EXIT,
59046
- isMounted: status !== UNMOUNTED,
59047
- isResolved: status === ENTERED || status > EXITING
59039
+ isEnter: status < 3,
59040
+ isMounted: status !== 6,
59041
+ isResolved: status === 2 || status > 4
59048
59042
  });
59049
- const startOrEnd = (unmounted) => unmounted ? UNMOUNTED : EXITED;
59043
+ const startOrEnd = (unmounted) => unmounted ? 6 : 5;
59050
59044
  const getEndStatus = (status, unmountOnExit) => {
59051
59045
  switch (status) {
59052
- case ENTERING:
59053
- case PRE_ENTER: return ENTERED;
59054
- case EXITING:
59055
- case PRE_EXIT: return startOrEnd(unmountOnExit);
59046
+ case 1:
59047
+ case 0: return 2;
59048
+ case 4:
59049
+ case 3: return startOrEnd(unmountOnExit);
59056
59050
  }
59057
59051
  };
59058
59052
  const getTimeout = (timeout) => typeof timeout === "object" ? [timeout.enter, timeout.exit] : [timeout, timeout];
59059
- const _setTimeout = (...args) => setTimeout(...args);
59060
- const nextTick = (transitState, status) => _setTimeout(() => {
59061
- isNaN(document.body.offsetTop) || transitState(status + 1);
59062
- }, 0);
59053
+ const nextTick = (callback, config) => {
59054
+ config.r = requestAnimationFrame(() => {
59055
+ config.r = requestAnimationFrame(callback);
59056
+ });
59057
+ };
59063
59058
 
59064
59059
  //#region src/useTransitionState.ts
59065
- const updateState = (status, setState, latestState, timeoutId, onChange) => {
59066
- clearTimeout(timeoutId.current);
59060
+ const updateState = (status, setState, ref, onChange) => {
59061
+ clearTimeout(ref.t);
59062
+ cancelAnimationFrame(ref.r);
59067
59063
  const state = getState(status);
59068
59064
  setState(state);
59069
- latestState.current = state;
59070
- onChange && onChange({ current: state });
59065
+ ref.s = state;
59066
+ onChange?.({ current: state });
59071
59067
  };
59072
59068
  const useTransitionState = ({ enter = true, exit = true, preEnter, preExit, timeout, initialEntered, mountOnEnter, unmountOnExit, onStateChange: onChange } = {}) => {
59073
- const [state, setState] = React13.useState(() => getState(initialEntered ? ENTERED : startOrEnd(mountOnEnter)));
59074
- const latestState = React13.useRef(state);
59075
- const timeoutId = React13.useRef(0);
59069
+ const [state, setState] = React13.useState(() => getState(initialEntered ? 2 : startOrEnd(mountOnEnter)));
59070
+ const [ref] = React13.useState({
59071
+ s: state,
59072
+ r: 0
59073
+ });
59076
59074
  const [enterTimeout, exitTimeout] = getTimeout(timeout);
59077
59075
  const endTransition = React13.useCallback(() => {
59078
- const status = getEndStatus(latestState.current._s, unmountOnExit);
59079
- status && updateState(status, setState, latestState, timeoutId, onChange);
59080
- }, [onChange, unmountOnExit]);
59076
+ const status = getEndStatus(ref.s.$, unmountOnExit);
59077
+ if (status) updateState(status, setState, ref, onChange);
59078
+ }, [
59079
+ onChange,
59080
+ unmountOnExit,
59081
+ ref
59082
+ ]);
59081
59083
  return [
59082
59084
  state,
59083
59085
  React13.useCallback((toEnter) => {
59084
59086
  const transitState = (status) => {
59085
- updateState(status, setState, latestState, timeoutId, onChange);
59087
+ updateState(status, setState, ref, onChange);
59086
59088
  switch (status) {
59087
- case ENTERING:
59088
- if (enterTimeout >= 0) timeoutId.current = _setTimeout(endTransition, enterTimeout);
59089
+ case 1:
59090
+ if (enterTimeout >= 0) ref.t = setTimeout(endTransition, enterTimeout);
59089
59091
  break;
59090
- case EXITING:
59091
- if (exitTimeout >= 0) timeoutId.current = _setTimeout(endTransition, exitTimeout);
59092
+ case 4:
59093
+ if (exitTimeout >= 0) ref.t = setTimeout(endTransition, exitTimeout);
59092
59094
  break;
59093
- case PRE_ENTER:
59094
- case PRE_EXIT:
59095
- timeoutId.current = nextTick(transitState, status);
59095
+ case 0:
59096
+ case 3:
59097
+ nextTick(() => transitState(status + 1), ref);
59096
59098
  break;
59097
59099
  }
59098
59100
  };
59099
- const enterStage = latestState.current.isEnter;
59101
+ const enterStage = ref.s.isEnter;
59100
59102
  if (typeof toEnter !== "boolean") toEnter = !enterStage;
59101
- if (toEnter) !enterStage && transitState(enter ? preEnter ? PRE_ENTER : ENTERING : ENTERED);
59102
- else enterStage && transitState(exit ? preExit ? PRE_EXIT : EXITING : startOrEnd(unmountOnExit));
59103
+ if (toEnter) !enterStage && transitState(enter ? preEnter ? 0 : 1 : 2);
59104
+ else enterStage && transitState(exit ? preExit ? 3 : 4 : startOrEnd(unmountOnExit));
59103
59105
  }, [
59106
+ ref,
59104
59107
  endTransition,
59105
59108
  onChange,
59106
59109
  enter,
@@ -61422,9 +61425,11 @@ maxInitialWidth,
61422
61425
  }
61423
61426
  else {
61424
61427
  const colDefEditable = colDef.editable;
61425
- const editable = combineEditables(params.loading !== true && params.readOnly !== true, params.defaultColDef?.editable, colDefEditable);
61428
+ const ignoreGridReadOnly = colDef.ignoreGridReadOnly === true;
61429
+ const editable = combineEditables(params.loading !== true, ignoreGridReadOnly ? true : params.readOnly !== true, params.defaultColDef?.editable, colDefEditable);
61426
61430
  return {
61427
- ...colDef,
61431
+ // ag-grid warns on unrecognised colDef properties, so it can't be left on the returned colDef
61432
+ ...lodashEs.omit(colDef, 'ignoreGridReadOnly'),
61428
61433
  editable,
61429
61434
  cellClassRules: {
61430
61435
  ...colDef.cellClassRules,
@@ -62695,20 +62700,25 @@ const regexpCache = new Map();
62695
62700
  const sanitizeArray = (input, inputName) => {
62696
62701
  if (!Array.isArray(input)) {
62697
62702
  switch (typeof input) {
62698
- case 'string':
62703
+ case 'string': {
62699
62704
  input = [input];
62700
62705
  break;
62701
- case 'undefined':
62706
+ }
62707
+
62708
+ case 'undefined': {
62702
62709
  input = [];
62703
62710
  break;
62704
- default:
62711
+ }
62712
+
62713
+ default: {
62705
62714
  throw new TypeError(`Expected '${inputName}' to be a string or an array, but got a type of '${typeof input}'`);
62715
+ }
62706
62716
  }
62707
62717
  }
62708
62718
 
62709
62719
  return input.filter(string => {
62710
62720
  if (typeof string !== 'string') {
62711
- if (typeof string === 'undefined') {
62721
+ if (string === undefined) {
62712
62722
  return false;
62713
62723
  }
62714
62724
 
@@ -62725,7 +62735,8 @@ const makeRegexp = (pattern, options) => {
62725
62735
  ...options,
62726
62736
  };
62727
62737
 
62728
- const cacheKey = pattern + JSON.stringify(options);
62738
+ const flags = 's' + (options.caseSensitive ? '' : 'i'); // Always dotAll, optionally case-insensitive
62739
+ const cacheKey = pattern + '|' + flags;
62729
62740
 
62730
62741
  if (regexpCache.has(cacheKey)) {
62731
62742
  return regexpCache.get(cacheKey);
@@ -62737,9 +62748,19 @@ const makeRegexp = (pattern, options) => {
62737
62748
  pattern = pattern.slice(1);
62738
62749
  }
62739
62750
 
62740
- pattern = escapeStringRegexp(pattern).replace(/\\\*/g, '[\\s\\S]*');
62751
+ // Handle escapes: first preserve escaped chars, then convert * to wildcards
62752
+ pattern = pattern
62753
+ .replaceAll(String.raw`\*`, '__ESCAPED_STAR__') // \* -> placeholder
62754
+ .replaceAll('\\\\', '__ESCAPED_BACKSLASH__') // \\ -> placeholder
62755
+ .replaceAll(/\\(.)/g, '$1'); // Other escapes like \<space> -> <space>
62756
+
62757
+ pattern = escapeStringRegexp(pattern).replaceAll(String.raw`\*`, '.*'); // * -> .*
62758
+
62759
+ pattern = pattern
62760
+ .replaceAll('__ESCAPED_STAR__', String.raw`\*`) // Restore escaped *
62761
+ .replaceAll('__ESCAPED_BACKSLASH__', '\\\\'); // Restore escaped \
62741
62762
 
62742
- const regexp = new RegExp(`^${pattern}$`, options.caseSensitive ? '' : 'i');
62763
+ const regexp = new RegExp(`^${pattern}$`, flags);
62743
62764
  regexp.negated = negated;
62744
62765
  regexpCache.set(cacheKey, regexp);
62745
62766
 
@@ -62756,39 +62777,75 @@ const baseMatcher = (inputs, patterns, options, firstMatchOnly) => {
62756
62777
 
62757
62778
  patterns = patterns.map(pattern => makeRegexp(pattern, options));
62758
62779
 
62780
+ // Partition patterns for faster processing
62781
+ const negatedPatterns = patterns.filter(pattern => pattern.negated);
62782
+ const positivePatterns = patterns.filter(pattern => !pattern.negated);
62783
+
62759
62784
  const {allPatterns} = {};
62760
62785
  const result = [];
62761
62786
 
62762
- for (const input of inputs) {
62763
- // String is included only if it matches at least one non-negated pattern supplied.
62764
- // Note: the `allPatterns` option requires every non-negated pattern to be matched once.
62765
- // Matching a negated pattern excludes the string.
62766
- let matches;
62767
- const didFit = [...patterns].fill(false);
62787
+ // Special handling for multiple negations with allPatterns and isMatch
62788
+ if (allPatterns && firstMatchOnly && negatedPatterns.length > 1 && positivePatterns.length === 0) {
62789
+ // Multiple negations only: ALL inputs must satisfy constraints (none should match any negation)
62790
+ for (const input of inputs) {
62791
+ for (const pattern of negatedPatterns) {
62792
+ if (pattern.test(input)) {
62793
+ return []; // Any input matching a negation means no match
62794
+ }
62795
+ }
62796
+ }
62768
62797
 
62769
- for (const [index, pattern] of patterns.entries()) {
62770
- if (pattern.test(input)) {
62771
- didFit[index] = true;
62772
- matches = !pattern.negated;
62798
+ return inputs.slice(0, 1); // All inputs passed negation constraints
62799
+ }
62773
62800
 
62774
- if (!matches) {
62775
- break;
62776
- }
62801
+ for (const input of inputs) {
62802
+ // Check negated patterns first (immediate exclusion)
62803
+ let excludedByNegation = false;
62804
+ for (const pattern of negatedPatterns) {
62805
+ if (pattern.test(input)) {
62806
+ excludedByNegation = true;
62807
+ break;
62777
62808
  }
62778
62809
  }
62779
62810
 
62780
- if (
62781
- !(
62782
- matches === false
62783
- || (matches === undefined && patterns.some(pattern => !pattern.negated))
62784
- || (allPatterns && didFit.some((yes, index) => !yes && !patterns[index].negated))
62785
- )
62786
- ) {
62811
+ if (excludedByNegation) {
62812
+ continue; // Skip this input
62813
+ }
62814
+
62815
+ // Check positive patterns
62816
+ if (positivePatterns.length === 0) {
62817
+ // No positive patterns - include if no negations matched (already checked above)
62787
62818
  result.push(input);
62819
+ } else if (allPatterns) {
62820
+ // AND logic: include if ALL positive patterns match
62821
+ const matchedPositive = Array.from({length: positivePatterns.length}, () => false);
62822
+ for (const [index, pattern] of positivePatterns.entries()) {
62823
+ if (pattern.test(input)) {
62824
+ matchedPositive[index] = true;
62825
+ }
62826
+ }
62788
62827
 
62789
- {
62790
- break;
62828
+ // All positive patterns must match
62829
+ if (matchedPositive.every(Boolean)) {
62830
+ result.push(input);
62791
62831
  }
62832
+ } else {
62833
+ // OR logic: include if any positive pattern matches
62834
+ let matchedAny = false;
62835
+ for (const pattern of positivePatterns) {
62836
+ if (pattern.test(input)) {
62837
+ matchedAny = true;
62838
+ break; // Short-circuit on first match
62839
+ }
62840
+ }
62841
+
62842
+ if (matchedAny) {
62843
+ result.push(input);
62844
+ }
62845
+ }
62846
+
62847
+ if (result.length > 0) {
62848
+ break;
62792
62849
  }
62793
62850
  }
62794
62851
 
@@ -62796,7 +62853,7 @@ const baseMatcher = (inputs, patterns, options, firstMatchOnly) => {
62796
62853
  };
62797
62854
 
62798
62855
  function isMatch(inputs, patterns, options) {
62799
- return baseMatcher(inputs, patterns, options).length > 0;
62856
+ return baseMatcher(inputs, patterns, options, true).length > 0;
62800
62857
  }
62801
62858
 
62802
62859
  /**
@@ -63746,7 +63803,11 @@ const GridFormPopoverMenu = (props) => {
63746
63803
  invalid: () => subComponentSelected && !subComponentIsValid.current,
63747
63804
  save,
63748
63805
  });
63749
- return popoverWrapper(jsxRuntime.jsx(ComponentLoadingWrapper, { loading: !options, className: 'GridFormPopupMenu', children: jsxRuntime.jsx(jsxRuntime.Fragment, { children: lodashEs.isEmpty(options) ? (jsxRuntime.jsx(MenuItem, { className: 'GridPopoverMenu-noOptions', disabled: true, children: "No actions" }, `GridPopoverMenu-empty`)) : (options?.map((item, index) => item.label === '__isMenuSeparator__' ? (jsxRuntime.jsx(MenuDivider, {}, `$$divider_${index}`)) : (!item.hidden && (jsxRuntime.jsxs(React13.Fragment, { children: [item.subMenu ? (jsxRuntime.jsx(SubMenu, { disabled: !!item.disabled, label: item.label, title: item.disabled && typeof item.disabled !== 'boolean' ? item.disabled : '', children: jsxRuntime.jsx(item.subMenu, {}) })) : (jsxRuntime.jsx(MenuItem, { onClick: (e) => void onMenuItemClick(e, item), disabled: !!item.disabled, title: item.disabled && typeof item.disabled !== 'boolean' ? item.disabled : '', children: item.label })), item.subComponent && subComponentSelected === item && (jsxRuntime.jsx(FocusableItem, { className: 'LuiDeprecatedForms', children: () => (jsxRuntime.jsx(GridSubComponentContext.Provider, { value: {
63806
+ // isEmpty(options) alone isn't enough: options can be a non-empty array where every item is
63807
+ // hidden (e.g. all actions unavailable in a given mode), which must also show "No actions"
63808
+ // rather than rendering an empty menu body.
63809
+ const hasVisibleOption = options?.some((item) => !item.hidden && item.label !== __isMenuSeparator__);
63810
+ return popoverWrapper(jsxRuntime.jsx(ComponentLoadingWrapper, { loading: !options, className: 'GridFormPopupMenu', children: jsxRuntime.jsx(jsxRuntime.Fragment, { children: !hasVisibleOption ? (jsxRuntime.jsx(MenuItem, { className: 'GridPopoverMenu-noOptions', disabled: true, children: "No actions" }, `GridPopoverMenu-empty`)) : (options?.map((item, index) => item.label === '__isMenuSeparator__' ? (jsxRuntime.jsx(MenuDivider, {}, `$$divider_${index}`)) : (!item.hidden && (jsxRuntime.jsxs(React13.Fragment, { children: [item.subMenu ? (jsxRuntime.jsx(SubMenu, { disabled: !!item.disabled, label: item.label, title: item.disabled && typeof item.disabled !== 'boolean' ? item.disabled : '', children: jsxRuntime.jsx(item.subMenu, {}) })) : (jsxRuntime.jsx(MenuItem, { onClick: (e) => void onMenuItemClick(e, item), disabled: !!item.disabled, title: item.disabled && typeof item.disabled !== 'boolean' ? item.disabled : '', children: item.label })), item.subComponent && subComponentSelected === item && (jsxRuntime.jsx(FocusableItem, { className: 'LuiDeprecatedForms', children: () => (jsxRuntime.jsx(GridSubComponentContext.Provider, { value: {
63750
63811
  context: {},
63751
63812
  data,
63752
63813
  value: subSelectedValue,
@@ -63893,9 +63954,57 @@ const GridButton = (colDef, editor) => {
63893
63954
  });
63894
63955
  };
63895
63956
 
63957
+ const isNode = typeof process !== 'undefined' && process.env.NODE_ENV === 'test';
63958
+ let sharedIntervalId = null;
63959
+ const callbacks = new Set();
63960
+ const startSharedInterval = () => {
63961
+ if (isNode || sharedIntervalId !== null) {
63962
+ return;
63963
+ }
63964
+ sharedIntervalId = setInterval(() => {
63965
+ callbacks.forEach((cb) => {
63966
+ try {
63967
+ cb();
63968
+ }
63969
+ catch (e) {
63970
+ console.error(e);
63971
+ }
63972
+ });
63973
+ }, 100);
63974
+ };
63975
+ const stopSharedInterval = () => {
63976
+ if (sharedIntervalId === null) {
63977
+ return;
63978
+ }
63979
+ clearInterval(sharedIntervalId);
63980
+ sharedIntervalId = null;
63981
+ };
63982
+ /**
63983
+ * Like useInterval, but all callers share a single 100ms interval.
63984
+ * Each callback is isolated in a try/catch so a throwing callback won't prevent others from running.
63985
+ */
63986
+ const useSharedInterval = (callback) => {
63987
+ const savedCallback = React13.useRef(callback);
63988
+ useIsomorphicLayoutEffect$1(() => {
63989
+ savedCallback.current = callback;
63990
+ }, [callback]);
63991
+ React13.useEffect(() => {
63992
+ const cb = () => savedCallback.current();
63993
+ callbacks.add(cb);
63994
+ startSharedInterval();
63995
+ return () => {
63996
+ callbacks.delete(cb);
63997
+ if (callbacks.size === 0) {
63998
+ stopSharedInterval();
63999
+ }
64000
+ };
64001
+ }, []);
64002
+ };
64003
+
63896
64004
  const BooleanCellRenderer = (props) => {
63897
- const { onValueChange, value, api, node, column, colDef, data } = props;
64005
+ const { onValueChange, value, api, node, column, colDef, data, eGridCell } = props;
63898
64006
  const inputRef = React13.useRef(null);
64007
+ const { updatingCells, redrawRows, resetFocusedCellAfterCellEditing, prePopupOps } = useGridContext();
63899
64008
  React13.useEffect(() => {
63900
64009
  const checkFocus = (event) => {
63901
64010
  if (event.rowIndex === node.rowIndex && event.column === column) {
@@ -63903,34 +64012,62 @@ const BooleanCellRenderer = (props) => {
63903
64012
  }
63904
64013
  };
63905
64014
  api.addEventListener('cellFocused', checkFocus);
63906
- return () => {
63907
- api.removeEventListener('cellFocused', checkFocus);
63908
- };
64015
+ return () => void api.removeEventListener('cellFocused', checkFocus);
63909
64016
  }, [api, column, node.rowIndex]);
63910
64017
  const isDisabled = !fnOrVar(colDef?.editable, props);
63911
- return (jsxRuntime.jsx("div", { className: clsx('ag-wrapper ag-input-wrapper ag-checkbox-input-wrapper', {
64018
+ const toggleCheckbox = React13.useCallback(() => {
64019
+ if (!onValueChange) {
64020
+ return;
64021
+ }
64022
+ const params = props?.colDef?.cellEditorParams;
64023
+ if (!params) {
64024
+ return;
64025
+ }
64026
+ const nodes = [];
64027
+ api.forEachNode((n) => {
64028
+ if (n.data.id === data.id) {
64029
+ nodes.push(n);
64030
+ }
64031
+ });
64032
+ prePopupOps();
64033
+ const checked = !value;
64034
+ onValueChange(checked);
64035
+ const field = props.colDef?.field ?? props.colDef?.colId ?? '';
64036
+ const selectedRows = nodes.map((node) => node.data);
64037
+ void updatingCells({ selectedRows, field }, async () => {
64038
+ redrawRows(nodes);
64039
+ return params.onClick({ selectedRows, selectedRowIds: selectedRows.map((r) => r.id), checked });
64040
+ }).finally(() => {
64041
+ resetFocusedCellAfterCellEditing();
64042
+ });
64043
+ }, [
64044
+ api,
64045
+ data.id,
64046
+ onValueChange,
64047
+ prePopupOps,
64048
+ props.colDef?.cellEditorParams,
64049
+ props.colDef?.colId,
64050
+ props.colDef?.field,
64051
+ redrawRows,
64052
+ resetFocusedCellAfterCellEditing,
64053
+ updatingCells,
64054
+ value,
64055
+ ]);
64056
+ useSharedInterval(() => {
64057
+ if (isDisabled) {
64058
+ return;
64059
+ }
64060
+ const cell = eGridCell?.querySelector('.ag-cell-focus .grid-edit-boolean input.ag-checkbox-input:not(:disabled)');
64061
+ if (cell && cell.ownerDocument.activeElement !== cell) {
64062
+ cell.focus();
64063
+ }
64064
+ });
64065
+ return (jsxRuntime.jsx("div", { className: clsx('grid-edit-boolean ag-wrapper ag-input-wrapper ag-checkbox-input-wrapper', {
63912
64066
  'ag-checked': props.value,
63913
64067
  'ag-disabled': isDisabled,
63914
64068
  }), children: jsxRuntime.jsx("input", { type: "checkbox", className: "ag-input-field-input ag-checkbox-input", disabled: isDisabled, ref: inputRef, checked: value, onChange: () => { }, onClick: (e) => {
63915
64069
  e.stopPropagation();
63916
- // cell has to be in edit mode
63917
- // if in non-edit mode clickInputWhenContainingCellClicked will click to put it in edit mode
63918
- if (!onValueChange)
63919
- return;
63920
- const params = props?.colDef?.cellEditorParams;
63921
- if (!params)
63922
- return;
63923
- // The data cannot be relied upon if grid changed whilst editing, data will be stale
63924
- // So I get the data from the node itself which will be up to date.
63925
- const selectedRows = [];
63926
- api.forEachNode((n) => {
63927
- if (n.data.id === data.id) {
63928
- selectedRows.push(n.data);
63929
- }
63930
- });
63931
- const checked = !value;
63932
- onValueChange(checked);
63933
- void params.onClick({ selectedRows, selectedRowIds: selectedRows.map((r) => r.id), checked });
64070
+ toggleCheckbox();
63934
64071
  } }) }));
63935
64072
  };
63936
64073
  const GridEditBoolean = (colDef, editorProps) => {
@@ -63944,7 +64081,7 @@ const GridEditBoolean = (colDef, editorProps) => {
63944
64081
  singleClickEdit: true,
63945
64082
  resizable: false,
63946
64083
  editable: true,
63947
- cellClass: 'GridCellAlignCenter',
64084
+ cellClass: 'GridCellAlignCenter GridCellEditableWithNoPopup',
63948
64085
  headerClass: 'GridHeaderAlignCenter',
63949
64086
  ...colDef,
63950
64087
  });
@@ -64039,6 +64176,9 @@ const GridPopoverMenu = (colDef, custom) => GridCell({
64039
64176
  editable: colDef.editable != null ? colDef.editable : true,
64040
64177
  exportable: false,
64041
64178
  cellStyle: { flex: 1, justifyContent: 'center' },
64179
+ suppressMovable: true,
64180
+ lockPosition: 'right',
64181
+ resizable: false,
64042
64182
  cellRenderer: GridRenderPopoutMenuCell,
64043
64183
  // Menus open on single click, this parameter is picked up in Grid.tsx
64044
64184
  singleClickEdit: true,
@@ -64532,15 +64672,19 @@ const GridContextProvider = (props) => {
64532
64672
  if (!gridApi) {
64533
64673
  return true;
64534
64674
  }
64675
+ /**
64676
+ * @returns true if editable with a popup, false if editable without popup, null if not editable
64677
+ */
64535
64678
  const focusedCellIsEditable = () => {
64536
64679
  const focusedCell = gridApi.isDestroyed() ? null : gridApi.getFocusedCell();
64537
64680
  const nextColumn = focusedCell?.column;
64538
64681
  const nextColDef = nextColumn?.getColDef();
64539
64682
  const rowNode = focusedCell && gridApi.getDisplayedRowAtIndex(focusedCell?.rowIndex);
64683
+ const popupable = String(nextColDef?.cellClass).indexOf("GridCellEditableWithNoPopup") === -1;
64540
64684
  return (!!(rowNode && nextColumn && nextColDef) &&
64541
64685
  nextColumn.isCellEditable(rowNode) &&
64542
64686
  !nextColDef.cellEditorParams?.preventAutoEdit &&
64543
- !nextColDef.cellRendererParams?.editAction);
64687
+ !nextColDef.cellRendererParams?.editAction) ? popupable : null;
64544
64688
  };
64545
64689
  // Just in case I've missed something, we don't want the loop to hang everything
64546
64690
  for (let maxIterations = 0; maxIterations < 50; maxIterations++) {
@@ -64573,7 +64717,10 @@ const GridContextProvider = (props) => {
64573
64717
  break;
64574
64718
  }
64575
64719
  }
64576
- if (focusedCellIsEditable()) {
64720
+ // checkbox cells are editable but don't have a popup
64721
+ // we need to end bulk editing and just select the cell
64722
+ const editable = focusedCellIsEditable();
64723
+ if (editable !== null) {
64577
64724
  const focusedCell = gridApi.getFocusedCell();
64578
64725
  if (focusedCell) {
64579
64726
  const rowNode = gridApi.getDisplayedRowAtIndex(focusedCell.rowIndex);
@@ -64581,8 +64728,15 @@ const GridContextProvider = (props) => {
64581
64728
  if (rowId == null) {
64582
64729
  return false;
64583
64730
  }
64584
- await startCellEditing({ rowId, colId: focusedCell.column.getColId() });
64585
- return true;
64731
+ if (editable) {
64732
+ await startCellEditing({ rowId, colId: focusedCell.column.getColId() });
64733
+ // Continue bulk edit
64734
+ return true;
64735
+ }
64736
+ else {
64737
+ // This will terminate bulk editing as there was no popup editor
64738
+ return false;
64739
+ }
64586
64740
  }
64587
64741
  }
64588
64742
  }