@linzjs/step-ag-grid 13.3.2 → 13.5.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 (30) hide show
  1. package/README.md +27 -16
  2. package/dist/index.css +17 -4
  3. package/dist/src/components/gridFilter/GridFilterDownloadCsvButton.d.ts +3 -0
  4. package/dist/src/components/gridFilter/index.d.ts +1 -0
  5. package/dist/src/components/gridRender/GridRenderGenericCell.d.ts +1 -0
  6. package/dist/src/contexts/GridContext.d.ts +2 -0
  7. package/dist/src/contexts/GridContextProvider.d.ts +7 -0
  8. package/dist/src/contexts/GridContextProvider.test.d.ts +1 -0
  9. package/dist/src/utils/util.d.ts +7 -0
  10. package/dist/src/utils/util.test.d.ts +1 -0
  11. package/dist/step-ag-grid.esm.js +299 -97
  12. package/dist/step-ag-grid.esm.js.map +1 -1
  13. package/package.json +4 -3
  14. package/src/components/Grid.tsx +4 -0
  15. package/src/components/GridCell.tsx +7 -0
  16. package/src/components/gridFilter/GridFilterDownloadCsvButton.tsx +43 -0
  17. package/src/components/gridFilter/GridFilterHeaderIconButton.tsx +1 -1
  18. package/src/components/gridFilter/GridFilterQuick.tsx +12 -10
  19. package/src/components/gridFilter/index.ts +1 -0
  20. package/src/components/gridPopoverEdit/GridPopoverMenu.tsx +1 -0
  21. package/src/components/gridRender/GridRenderGenericCell.tsx +1 -0
  22. package/src/contexts/GridContext.tsx +5 -0
  23. package/src/contexts/GridContextProvider.test.tsx +39 -0
  24. package/src/contexts/GridContextProvider.tsx +83 -3
  25. package/src/stories/grid/GridPopoverEditBearing.stories.tsx +22 -10
  26. package/src/stories/grid/GridPopoverEditDropDown.stories.tsx +20 -8
  27. package/src/stories/grid/GridReadOnly.stories.tsx +5 -2
  28. package/src/styles/Grid.scss +23 -4
  29. package/src/utils/util.test.ts +8 -0
  30. package/src/utils/util.ts +10 -0
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { LuiMiniSpinner, LuiIcon, LuiButton, LuiCheckboxInput, LuiButtonGroup } from '@linzjs/lui';
3
3
  import { AgGridReact } from 'ag-grid-react';
4
- import { negate, isEmpty, xorBy, last, difference, sortBy, findIndex, debounce, partition, omit, pick, groupBy, fromPairs, toPairs, delay, defer as defer$1, compact, remove, castArray, flatten, isEqual } from 'lodash-es';
4
+ import { negate, isEmpty, xorBy, last, difference, sortBy, findIndex, debounce, delay, partition, omit, pick, groupBy, fromPairs, toPairs, defer as defer$1, compact, remove, castArray, flatten, isEqual } from 'lodash-es';
5
5
  import * as React from 'react';
6
6
  import { createContext, useContext, useRef, useCallback, useState, useEffect, useMemo, forwardRef, useLayoutEffect, memo, useReducer, cloneElement, useImperativeHandle, Fragment as Fragment$1 } from 'react';
7
7
  import ReactDOM, { unstable_batchedUpdates, flushSync, createPortal } from 'react-dom';
@@ -244,6 +244,9 @@ var GridContext = createContext({
244
244
  doesExternalFilterPass: function () {
245
245
  console.error("no context provider for doesExternalFilterPass");
246
246
  return true;
247
+ },
248
+ downloadCsv: function () {
249
+ console.error("no context provider for downloadCsv");
247
250
  }
248
251
  });
249
252
  var useGridContext = function () { return useContext(GridContext); };
@@ -262,6 +265,144 @@ var GridUpdatingContext = createContext({
262
265
  updatedDep: 0
263
266
  });
264
267
 
268
+ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
269
+
270
+ function getDefaultExportFromCjs (x) {
271
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
272
+ }
273
+
274
+ function getAugmentedNamespace(n) {
275
+ if (n.__esModule) return n;
276
+ var f = n.default;
277
+ if (typeof f == "function") {
278
+ var a = function a () {
279
+ if (this instanceof a) {
280
+ var args = [null];
281
+ args.push.apply(args, arguments);
282
+ var Ctor = Function.bind.apply(f, args);
283
+ return new Ctor();
284
+ }
285
+ return f.apply(this, arguments);
286
+ };
287
+ a.prototype = f.prototype;
288
+ } else a = {};
289
+ Object.defineProperty(a, '__esModule', {value: true});
290
+ Object.keys(n).forEach(function (k) {
291
+ var d = Object.getOwnPropertyDescriptor(n, k);
292
+ Object.defineProperty(a, k, d.get ? d : {
293
+ enumerable: true,
294
+ get: function () {
295
+ return n[k];
296
+ }
297
+ });
298
+ });
299
+ return a;
300
+ }
301
+
302
+ function isHighSurrogate(codePoint) {
303
+ return codePoint >= 0xd800 && codePoint <= 0xdbff;
304
+ }
305
+
306
+ function isLowSurrogate(codePoint) {
307
+ return codePoint >= 0xdc00 && codePoint <= 0xdfff;
308
+ }
309
+
310
+ // Truncate string by size in bytes
311
+ var truncate$2 = function truncate(getLength, string, byteLength) {
312
+ if (typeof string !== "string") {
313
+ throw new Error("Input must be string");
314
+ }
315
+
316
+ var charLength = string.length;
317
+ var curByteLength = 0;
318
+ var codePoint;
319
+ var segment;
320
+
321
+ for (var i = 0; i < charLength; i += 1) {
322
+ codePoint = string.charCodeAt(i);
323
+ segment = string[i];
324
+
325
+ if (isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1))) {
326
+ i += 1;
327
+ segment += string[i];
328
+ }
329
+
330
+ curByteLength += getLength(segment);
331
+
332
+ if (curByteLength === byteLength) {
333
+ return string.slice(0, i + 1);
334
+ }
335
+ else if (curByteLength > byteLength) {
336
+ return string.slice(0, i - segment.length + 1);
337
+ }
338
+ }
339
+
340
+ return string;
341
+ };
342
+
343
+ var truncate$1 = truncate$2;
344
+ var getLength = Buffer.byteLength.bind(Buffer);
345
+ var truncateUtf8Bytes = truncate$1.bind(null, getLength);
346
+
347
+ /*jshint node:true*/
348
+
349
+ /**
350
+ * Replaces characters in strings that are illegal/unsafe for filenames.
351
+ * Unsafe characters are either removed or replaced by a substitute set
352
+ * in the optional `options` object.
353
+ *
354
+ * Illegal Characters on Various Operating Systems
355
+ * / ? < > \ : * | "
356
+ * https://kb.acronis.com/content/39790
357
+ *
358
+ * Unicode Control codes
359
+ * C0 0x00-0x1f & C1 (0x80-0x9f)
360
+ * http://en.wikipedia.org/wiki/C0_and_C1_control_codes
361
+ *
362
+ * Reserved filenames on Unix-based systems (".", "..")
363
+ * Reserved filenames in Windows ("CON", "PRN", "AUX", "NUL", "COM1",
364
+ * "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
365
+ * "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", and
366
+ * "LPT9") case-insesitively and with or without filename extensions.
367
+ *
368
+ * Capped at 255 characters in length.
369
+ * http://unix.stackexchange.com/questions/32795/what-is-the-maximum-allowed-filename-and-folder-size-with-ecryptfs
370
+ *
371
+ * @param {String} input Original filename
372
+ * @param {Object} options {replacement: String | Function }
373
+ * @return {String} Sanitized filename
374
+ */
375
+
376
+ var truncate = truncateUtf8Bytes;
377
+
378
+ var illegalRe = /[\/\?<>\\:\*\|"]/g;
379
+ var controlRe = /[\x00-\x1f\x80-\x9f]/g;
380
+ var reservedRe = /^\.+$/;
381
+ var windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
382
+ var windowsTrailingRe = /[\. ]+$/;
383
+
384
+ function sanitize(input, replacement) {
385
+ if (typeof input !== 'string') {
386
+ throw new Error('Input must be string');
387
+ }
388
+ var sanitized = input
389
+ .replace(illegalRe, replacement)
390
+ .replace(controlRe, replacement)
391
+ .replace(reservedRe, replacement)
392
+ .replace(windowsReservedRe, replacement)
393
+ .replace(windowsTrailingRe, replacement);
394
+ return truncate(sanitized, 255);
395
+ }
396
+
397
+ var sanitizeFilename = function (input, options) {
398
+ var replacement = (options && options.replacement) || '';
399
+ var output = sanitize(input, replacement);
400
+ if (replacement === '') {
401
+ return output;
402
+ }
403
+ return sanitize(output, '');
404
+ };
405
+
265
406
  var isNotEmpty = negate(isEmpty);
266
407
  var wait$2 = function (timeoutMs) {
267
408
  return new Promise(function (resolve) {
@@ -293,7 +434,16 @@ var hasParentClass = function (className, child) {
293
434
  var stringByteLengthIsInvalid = function (str, maxBytes) {
294
435
  return new TextEncoder().encode(str).length > maxBytes;
295
436
  };
296
- var fnOrVar = function (fn, param) { return (typeof fn === "function" ? fn(param) : fn); };
437
+ var fnOrVar = function (fn, param) { return (typeof fn === "function" ? fn(param) : fn); };
438
+ /**
439
+ * Trim filename and replaces troublesome characters.
440
+ *
441
+ * e.g. " LT 1235/543 &%//*$ " => "LT_1235-543_&%-$"
442
+ * e.g. " @filename here!!!" => "@filename_here!!!"
443
+ */
444
+ var sanitiseFileName = function (filename) {
445
+ return sanitizeFilename(filename.trim().replaceAll(/(\/|\\)+/g, "-")).replaceAll(/\s+/g, "_");
446
+ };
297
447
 
298
448
  var GridNoRowsOverlay = function (params) {
299
449
  var _a;
@@ -619,6 +769,9 @@ var Grid = function (_a) {
619
769
  colId: "selection",
620
770
  editable: false,
621
771
  minWidth: 42,
772
+ headerComponentParams: {
773
+ exportable: false
774
+ },
622
775
  maxWidth: 42,
623
776
  suppressSizeToFit: true,
624
777
  checkboxSelection: true,
@@ -713,7 +866,7 @@ var Grid = function (_a) {
713
866
  sizeColumnsToFit();
714
867
  }
715
868
  }, [columnDefs === null || columnDefs === void 0 ? void 0 : columnDefs.length, sizeColumnsToFit]);
716
- return (jsx("div", __assign({ "data-testid": dataTestId, className: clsx("Grid-container", theme, staleGrid && "Grid-sortIsStale", gridReady && params.rowData && "Grid-ready") }, { children: jsx("div", __assign({ style: { flex: 1 } }, { children: jsx(AgGridReact, { animateRows: params.animateRows, rowClassRules: params.rowClassRules, getRowId: function (params) { return "".concat(params.data.id); }, suppressRowClickSelection: true, rowSelection: rowSelection, suppressBrowserResizeObserver: true, colResizeDefault: "shift", onFirstDataRendered: (_c = params.onFirstDataRendered) !== null && _c !== void 0 ? _c : sizeColumnsToFit, onGridSizeChanged: (_d = params.onGridSizeChanged) !== null && _d !== void 0 ? _d : sizeColumnsToFit, suppressColumnVirtualisation: suppressColumnVirtualization, suppressClickEdit: true, onCellKeyPress: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, onCellEditingStarted: refreshSelectedRows, domLayout: params.domLayout, columnDefs: columnDefs, rowData: params.rowData, noRowsOverlayComponent: GridNoRowsOverlay, noRowsOverlayComponentParams: { rowData: params.rowData, noRowsOverlayText: params.noRowsOverlayText }, onModelUpdated: onModelUpdated, onGridReady: onGridReady, onSortChanged: ensureSelectedRowIsVisible, postSortRows: (_e = params.postSortRows) !== null && _e !== void 0 ? _e : postSortRows, onSelectionChanged: synchroniseExternalStateToGridSelection, onColumnMoved: params.onColumnMoved, alwaysShowVerticalScroll: params.alwaysShowVerticalScroll, isExternalFilterPresent: isExternalFilterPresent, doesExternalFilterPass: doesExternalFilterPass }) })) })));
869
+ return (jsx("div", __assign({ "data-testid": dataTestId, className: clsx("Grid-container", theme, staleGrid && "Grid-sortIsStale", gridReady && params.rowData && "Grid-ready") }, { children: jsx("div", __assign({ style: { flex: 1 } }, { children: jsx(AgGridReact, { animateRows: params.animateRows, rowClassRules: params.rowClassRules, getRowId: function (params) { return "".concat(params.data.id); }, suppressRowClickSelection: true, rowSelection: rowSelection, suppressBrowserResizeObserver: true, colResizeDefault: "shift", onFirstDataRendered: (_c = params.onFirstDataRendered) !== null && _c !== void 0 ? _c : sizeColumnsToFit, onGridSizeChanged: (_d = params.onGridSizeChanged) !== null && _d !== void 0 ? _d : sizeColumnsToFit, suppressColumnVirtualisation: suppressColumnVirtualization, suppressClickEdit: true, onCellKeyPress: onCellKeyPress, onCellClicked: onCellClicked, onCellDoubleClicked: onCellDoubleClick, onCellEditingStarted: refreshSelectedRows, domLayout: params.domLayout, columnDefs: columnDefs, rowData: params.rowData, noRowsOverlayComponent: GridNoRowsOverlay, noRowsOverlayComponentParams: { rowData: params.rowData, noRowsOverlayText: params.noRowsOverlayText }, onModelUpdated: onModelUpdated, onGridReady: onGridReady, onSortChanged: ensureSelectedRowIsVisible, postSortRows: (_e = params.postSortRows) !== null && _e !== void 0 ? _e : postSortRows, onSelectionChanged: synchroniseExternalStateToGridSelection, onColumnMoved: params.onColumnMoved, alwaysShowVerticalScroll: params.alwaysShowVerticalScroll, isExternalFilterPresent: isExternalFilterPresent, doesExternalFilterPass: doesExternalFilterPass, maintainColumnOrder: true }) })) })));
717
870
  };
718
871
 
719
872
  var GridPopoverContext = createContext({
@@ -859,6 +1012,9 @@ var GridCell = function (props, custom) {
859
1012
  // This is so that e.g. bearings can be searched for by DMS or raw number.
860
1013
  var valueFormatter = props.valueFormatter;
861
1014
  var filterValueGetter = generateFilterGetter(props.field, props.filterValueGetter, valueFormatter);
1015
+ var exportable = props.exportable;
1016
+ // Can't leave this here ag-grid will complain
1017
+ delete props.exportable;
862
1018
  return __assign(__assign(__assign(__assign(__assign(__assign({ colId: props.field, sortable: !!((props === null || props === void 0 ? void 0 : props.field) || (props === null || props === void 0 ? void 0 : props.valueGetter)), resizable: true, editable: (_a = props.editable) !== null && _a !== void 0 ? _a : false }, ((custom === null || custom === void 0 ? void 0 : custom.editor) && {
863
1019
  cellClassRules: GridCellMultiSelectClassRules,
864
1020
  editable: (_b = props.editable) !== null && _b !== void 0 ? _b : true,
@@ -877,7 +1033,7 @@ var GridCell = function (props, custom) {
877
1033
  return "".concat(params.value);
878
1034
  else
879
1035
  return JSON.stringify(params.value);
880
- } }), props), { cellRenderer: GridCellRenderer, cellRendererParams: __assign({ originalCellRenderer: props.cellRenderer }, props.cellRendererParams) });
1036
+ } }), props), { cellRenderer: GridCellRenderer, cellRendererParams: __assign({ originalCellRenderer: props.cellRenderer }, props.cellRendererParams), headerComponentParams: __assign({ exportable: exportable }, props.headerComponentParams) });
881
1037
  };
882
1038
  var GenericCellEditorComponentWrapper = function (editor) {
883
1039
  var obj = { editor: editor };
@@ -913,7 +1069,7 @@ var GridCellMultiEditor = function (props, cellEditorSelector) {
913
1069
 
914
1070
  var GridFilterHeaderIconButton = forwardRef(function columnsButton(_a, ref) {
915
1071
  var icon = _a.icon, title = _a.title, onClick = _a.onClick, buttonProps = _a.buttonProps, _b = _a.disabled, disabled = _b === void 0 ? false : _b, _c = _a.size, size = _c === void 0 ? "md" : _c;
916
- return (jsx(LuiButton, __assign({}, buttonProps, { type: "button", level: "tertiary", className: "GridFilterHeaderIconButton lui-button-icon-only", ref: ref, "aria-label": title, title: title, onClick: onClick, disabled: disabled }, { children: jsx(LuiIcon, { name: icon, alt: "Menu", size: size }) })));
1072
+ return (jsx(LuiButton, __assign({}, buttonProps, { type: "button", level: "tertiary", className: "lui-button-icon-only", ref: ref, "aria-label": title, title: title, onClick: onClick, disabled: disabled }, { children: jsx(LuiIcon, { name: icon, alt: "Menu", size: size }) })));
917
1073
  });
918
1074
 
919
1075
  // Generate className following BEM methodology: http://getbem.com/naming/
@@ -2863,9 +3019,9 @@ var GridFilterQuick = function (_a) {
2863
3019
  useEffect(function () {
2864
3020
  setQuickFilter(quickFilterValue);
2865
3021
  }, [quickFilterValue, setQuickFilter]);
2866
- return (jsx("input", { "aria-label": "Search", className: "Grid-quickFilterBox", type: "text", placeholder: quickFilterPlaceholder !== null && quickFilterPlaceholder !== void 0 ? quickFilterPlaceholder : "Search...", value: quickFilterValue, onChange: function (event) {
2867
- setQuickFilterValue(event.target.value);
2868
- } }));
3022
+ return (jsx("div", __assign({ className: "GridFilterQuick-container" }, { children: jsx("input", { "aria-label": "Search", className: "GridFilterQuick-input", type: "text", placeholder: quickFilterPlaceholder !== null && quickFilterPlaceholder !== void 0 ? quickFilterPlaceholder : "Search...", value: quickFilterValue, onChange: function (event) {
3023
+ setQuickFilterValue(event.target.value);
3024
+ } }) })));
2869
3025
  };
2870
3026
 
2871
3027
  var GridFilters = function (_a) {
@@ -2873,39 +3029,79 @@ var GridFilters = function (_a) {
2873
3029
  return jsx("div", __assign({ className: "Grid-container-filters" }, { children: children }));
2874
3030
  };
2875
3031
 
2876
- var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
3032
+ /**
3033
+ * Cancels timeouts on scope being destroyed.
3034
+ *
3035
+ * This could almost be a debounce, but debounce tracks by function reference, this tracks by hook reference.
3036
+ * This could have been implemented using debounce if the callers function was wrapped in useCallback,
3037
+ * but there's no way to enforce that, so it would lead to bugs.
3038
+ */
3039
+ var useTimeoutHook = function () {
3040
+ var timeout = useRef();
3041
+ /**
3042
+ * Clear any pending timeouts.
3043
+ */
3044
+ var clearTimeouts = function () {
3045
+ if (timeout.current) {
3046
+ var tc = timeout.current;
3047
+ timeout.current = undefined;
3048
+ clearTimeout(tc);
3049
+ }
3050
+ };
3051
+ /**
3052
+ * Call this when your action has completed.
3053
+ */
3054
+ var invoke = useCallback(function (fn, waitTimeMs) {
3055
+ clearTimeouts();
3056
+ timeout.current = setTimeout(fn, waitTimeMs);
3057
+ }, []);
3058
+ /**
3059
+ * Clear timeout on loss of scope.
3060
+ */
3061
+ useEffect(function () {
3062
+ return function () { return clearTimeouts(); };
3063
+ }, []);
3064
+ return invoke;
3065
+ };
2877
3066
 
2878
- function getDefaultExportFromCjs (x) {
2879
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
2880
- }
3067
+ /**
3068
+ * Defers state change up to a minimum time since last state change.
3069
+ */
3070
+ var useStateDeferred = function (initialValue) {
3071
+ var startTime = useRef(0);
3072
+ var timeoutHook = useTimeoutHook();
3073
+ var _a = useState(initialValue), value = _a[0], _setValue = _a[1];
3074
+ var setValue = useCallback(function (newValue) {
3075
+ startTime.current = Date.now();
3076
+ _setValue(newValue);
3077
+ }, []);
3078
+ var setValueDeferred = useCallback(function (newValue, minimumWaitTimeMs) {
3079
+ var waitTimeMs = Math.max(minimumWaitTimeMs - (Date.now() - startTime.current), 0);
3080
+ timeoutHook(function () { return _setValue(newValue); }, waitTimeMs);
3081
+ }, [timeoutHook]);
3082
+ return [value, setValue, setValueDeferred];
3083
+ };
2881
3084
 
2882
- function getAugmentedNamespace(n) {
2883
- if (n.__esModule) return n;
2884
- var f = n.default;
2885
- if (typeof f == "function") {
2886
- var a = function a () {
2887
- if (this instanceof a) {
2888
- var args = [null];
2889
- args.push.apply(args, arguments);
2890
- var Ctor = Function.bind.apply(f, args);
2891
- return new Ctor();
2892
- }
2893
- return f.apply(this, arguments);
2894
- };
2895
- a.prototype = f.prototype;
2896
- } else a = {};
2897
- Object.defineProperty(a, '__esModule', {value: true});
2898
- Object.keys(n).forEach(function (k) {
2899
- var d = Object.getOwnPropertyDescriptor(n, k);
2900
- Object.defineProperty(a, k, d.get ? d : {
2901
- enumerable: true,
2902
- get: function () {
2903
- return n[k];
2904
- }
2905
- });
2906
- });
2907
- return a;
2908
- }
3085
+ var GridFilterDownloadCsvButton = function (csvExportParams) {
3086
+ var _a;
3087
+ var downloadCsv = useContext(GridContext).downloadCsv;
3088
+ var _b = useStateDeferred(false), downloading = _b[0], setDownloading = _b[1], setDownloadingDeferred = _b[2];
3089
+ var handleDownloadClick = function () {
3090
+ setDownloading(true);
3091
+ // Defer the download such that the component disabled state can update
3092
+ delay(function () {
3093
+ downloadCsv(csvExportParams);
3094
+ // Defer re-enablement as the browser takes time to process the download
3095
+ setDownloadingDeferred(false, 4000);
3096
+ }, 100);
3097
+ };
3098
+ return downloading ? (jsx(LuiMiniSpinner, { size: 22, divProps: (_a = {
3099
+ role: "status"
3100
+ },
3101
+ _a["aria-label"] = "Downloading...",
3102
+ _a.style = { width: 42, display: "flex", justifyContent: "center" },
3103
+ _a) })) : (jsx(GridFilterHeaderIconButton, { icon: "ic_save_download", title: "Download CSV", onClick: handleDownloadClick, disabled: downloading }));
3104
+ };
2909
3105
 
2910
3106
  /* global setTimeout, clearTimeout */
2911
3107
 
@@ -4255,7 +4451,7 @@ var GridRenderPopoutMenuCell = function (props) {
4255
4451
  * Popout burger menu
4256
4452
  */
4257
4453
  var GridPopoverMenu = function (colDef, custom) {
4258
- return GridCell(__assign(__assign({ minWidth: 48, maxWidth: 48, width: 40, editable: colDef.editable != null ? colDef.editable : true, cellStyle: { justifyContent: "center" }, cellRenderer: GridRenderPopoutMenuCell }, colDef), { cellRendererParams: {
4454
+ return GridCell(__assign(__assign({ minWidth: 48, maxWidth: 48, width: 40, editable: colDef.editable != null ? colDef.editable : true, exportable: false, cellStyle: { justifyContent: "center" }, cellRenderer: GridRenderPopoutMenuCell }, colDef), { cellRendererParams: {
4259
4455
  // Menus open on single click, this parameter is picked up in Grid.tsx
4260
4456
  singleClickEdit: true
4261
4457
  } }), __assign({ editor: GridFormPopoverMenu }, custom));
@@ -4652,6 +4848,11 @@ var GridContextProvider = function (props) {
4652
4848
  gridApi.redrawRows(rowNodes ? { rowNodes: rowNodes } : undefined);
4653
4849
  });
4654
4850
  }, [gridApiOp]);
4851
+ // waitForExternallySelectedItemsToBeInSync can't use the state as it won't be updated during function execution
4852
+ var externallySelectedItemsAreInSyncRef = useRef(false);
4853
+ useEffect(function () {
4854
+ externallySelectedItemsAreInSyncRef.current = externallySelectedItemsAreInSync;
4855
+ }, [externallySelectedItemsAreInSync]);
4655
4856
  var waitForExternallySelectedItemsToBeInSync = useCallback(function () { return __awaiter(void 0, void 0, void 0, function () {
4656
4857
  var i;
4657
4858
  return __generator(this, function (_a) {
@@ -4660,7 +4861,7 @@ var GridContextProvider = function (props) {
4660
4861
  i = 0;
4661
4862
  _a.label = 1;
4662
4863
  case 1:
4663
- if (!(i < 5000 / 200 && !externallySelectedItemsAreInSync)) return [3 /*break*/, 4];
4864
+ if (!(i < 5000 / 200 && !externallySelectedItemsAreInSyncRef.current)) return [3 /*break*/, 4];
4664
4865
  return [4 /*yield*/, wait$2(200)];
4665
4866
  case 2:
4666
4867
  _a.sent();
@@ -4671,7 +4872,7 @@ var GridContextProvider = function (props) {
4671
4872
  case 4: return [2 /*return*/];
4672
4873
  }
4673
4874
  });
4674
- }); }, [externallySelectedItemsAreInSync]);
4875
+ }); }, []);
4675
4876
  var onFilterChanged = useMemo(function () {
4676
4877
  return debounce(function () {
4677
4878
  gridApi && gridApi.onFilterChanged();
@@ -4700,6 +4901,16 @@ var GridContextProvider = function (props) {
4700
4901
  columnApi.setColumnsVisible(invisibleColumnIds, false);
4701
4902
  }
4702
4903
  }, [invisibleColumnIds, columnApi, getColumns]);
4904
+ /**
4905
+ * Download visible columns as a CSV
4906
+ */
4907
+ var downloadCsv = useCallback(function (csvExportParams) {
4908
+ if (!gridApi || !columnApi)
4909
+ return;
4910
+ var fileName = (csvExportParams === null || csvExportParams === void 0 ? void 0 : csvExportParams.fileName) && sanitiseFileName(csvExportParams.fileName);
4911
+ var columnKeys = columnApi === null || columnApi === void 0 ? void 0 : columnApi.getColumnState().filter(function (cs) { var _a, _b; return !cs.hide && ((_b = (_a = gridApi.getColumnDef(cs.colId)) === null || _a === void 0 ? void 0 : _a.headerComponentParams) === null || _b === void 0 ? void 0 : _b.exportable) !== false; }).map(function (cs) { return cs.colId; });
4912
+ gridApi.exportDataAsCsv(__assign(__assign({ columnKeys: columnKeys, processCellCallback: downloadCsvUseValueFormattersProcessCellCallback }, csvExportParams), { fileName: fileName }));
4913
+ }, [columnApi, gridApi]);
4703
4914
  return (jsx(GridContext.Provider, __assign({ value: {
4704
4915
  getColumns: getColumns,
4705
4916
  invisibleColumnIds: invisibleColumnIds,
@@ -4732,8 +4943,52 @@ var GridContextProvider = function (props) {
4732
4943
  addExternalFilter: addExternalFilter,
4733
4944
  removeExternalFilter: removeExternalFilter,
4734
4945
  isExternalFilterPresent: isExternalFilterPresent,
4735
- doesExternalFilterPass: doesExternalFilterPass
4946
+ doesExternalFilterPass: doesExternalFilterPass,
4947
+ downloadCsv: downloadCsv
4736
4948
  } }, { children: props.children })));
4949
+ };
4950
+ /**
4951
+ * Aggrid defaults to using getters and ignores formatters.
4952
+ * step-ag-grid by default has a valueFormatter for every column that defaults to the getter if no valueFormatter
4953
+ * This function uses valueFormatter by default
4954
+ */
4955
+ var downloadCsvUseValueFormattersProcessCellCallback = function (params) {
4956
+ var _a, _b;
4957
+ var encodeToString = function (value) {
4958
+ // Convert nullish values to blank
4959
+ if (value === "-" || value === "–" || value == null) {
4960
+ return "";
4961
+ }
4962
+ if (typeof value === "string") {
4963
+ return value;
4964
+ }
4965
+ return JSON.stringify(value);
4966
+ };
4967
+ // Try to use valueFormatter
4968
+ var colDef = (_a = params === null || params === void 0 ? void 0 : params.column) === null || _a === void 0 ? void 0 : _a.getColDef();
4969
+ if (!colDef)
4970
+ return encodeToString(params.value);
4971
+ // All columns in step-ag-grid have a default valueFormatter
4972
+ // If you have custom a renderer you need to define your own valueFormatter to produce the text value
4973
+ var valueFormatter = colDef.valueFormatter;
4974
+ // If no valueFormatter then value _must_ be a string
4975
+ if (valueFormatter == null) {
4976
+ if (params.value != null && typeof params.value !== "string") {
4977
+ console.error("downloadCsv: valueFormatter missing and getValue is not a string, colId: ".concat(colDef.colId));
4978
+ }
4979
+ return encodeToString(params.value);
4980
+ }
4981
+ // We don't have access to registered functions, so we can't call them
4982
+ if (typeof valueFormatter !== "function") {
4983
+ console.error("downloadCsv: String type (registered) value formatters are unsupported in downloadCsv, colId: ".concat(colDef.colId));
4984
+ return encodeToString(params.value);
4985
+ }
4986
+ var result = valueFormatter(__assign(__assign({}, params), { data: (_b = params.node) === null || _b === void 0 ? void 0 : _b.data, colDef: colDef }));
4987
+ if (params.value != null && typeof result !== "string") {
4988
+ console.error("downloadCsv: valueFormatter is returning non string values, colDef:\", colId: ".concat(colDef.colId));
4989
+ }
4990
+ // We add an extra encodeToString here just in case valueFormatter is returning non string values
4991
+ return encodeToString(result);
4737
4992
  };
4738
4993
 
4739
4994
  var GridUpdatingContextProvider = function (props) {
@@ -4794,59 +5049,6 @@ var usePrevious = function (value) {
4794
5049
  return ref.current;
4795
5050
  };
4796
5051
 
4797
- /**
4798
- * Cancels timeouts on scope being destroyed.
4799
- *
4800
- * This could almost be a debounce, but debounce tracks by function reference, this tracks by hook reference.
4801
- * This could have been implemented using debounce if the callers function was wrapped in useCallback,
4802
- * but there's no way to enforce that, so it would lead to bugs.
4803
- */
4804
- var useTimeoutHook = function () {
4805
- var timeout = useRef();
4806
- /**
4807
- * Clear any pending timeouts.
4808
- */
4809
- var clearTimeouts = function () {
4810
- if (timeout.current) {
4811
- var tc = timeout.current;
4812
- timeout.current = undefined;
4813
- clearTimeout(tc);
4814
- }
4815
- };
4816
- /**
4817
- * Call this when your action has completed.
4818
- */
4819
- var invoke = useCallback(function (fn, waitTimeMs) {
4820
- clearTimeouts();
4821
- timeout.current = setTimeout(fn, waitTimeMs);
4822
- }, []);
4823
- /**
4824
- * Clear timeout on loss of scope.
4825
- */
4826
- useEffect(function () {
4827
- return function () { return clearTimeouts(); };
4828
- }, []);
4829
- return invoke;
4830
- };
4831
-
4832
- /**
4833
- * Defers state change up to a minimum time since last state change.
4834
- */
4835
- var useStateDeferred = function (initialValue) {
4836
- var startTime = useRef(0);
4837
- var timeoutHook = useTimeoutHook();
4838
- var _a = useState(initialValue), value = _a[0], _setValue = _a[1];
4839
- var setValue = useCallback(function (newValue) {
4840
- startTime.current = Date.now();
4841
- _setValue(newValue);
4842
- }, []);
4843
- var setValueDeferred = useCallback(function (newValue, minimumWaitTimeMs) {
4844
- var waitTimeMs = Math.max(minimumWaitTimeMs - (Date.now() - startTime.current), 0);
4845
- timeoutHook(function () { return _setValue(newValue); }, waitTimeMs);
4846
- }, [timeoutHook]);
4847
- return [value, setValue, setValueDeferred];
4848
- };
4849
-
4850
5052
  // Kept this less than one second, so I don't have issues with waitFor as it defaults to 1s
4851
5053
  var minimumInProgressTimeMs = 950;
4852
5054
  var ActionButton = function (_a) {
@@ -25000,5 +25202,5 @@ var clickActionButton = function (text, container) { return __awaiter(void 0, vo
25000
25202
  });
25001
25203
  }); };
25002
25204
 
25003
- export { ActionButton, ComponentLoadingWrapper, ControlledMenu, Editor, FocusableItem, FormError, GenericCellEditorComponentWrapper, Grid, GridCell, GridCellMultiEditor, GridCellMultiSelectClassRules, GridCellRenderer, GridContext, GridContextProvider, GridFilterButtons, GridFilterColumnsToggle, GridFilterHeaderIconButton, GridFilterQuick, GridFilters, GridFormDropDown, GridFormEditBearing, GridFormMessage, GridFormMultiSelect, GridFormPopoverMenu, GridFormSubComponentTextArea, GridFormSubComponentTextInput, GridFormTextArea, GridFormTextInput, GridHeaderSelect, GridIcon, GridLoadableCell, GridNoRowsOverlay, GridPopoutEditMultiSelect, GridPopoverContext, GridPopoverContextProvider, GridPopoverEditBearing, GridPopoverEditBearingCorrection, GridPopoverEditBearingCorrectionEditorParams, GridPopoverEditBearingEditorParams, GridPopoverEditDropDown, GridPopoverMenu, GridPopoverMessage, GridPopoverTextArea, GridPopoverTextInput, GridRenderPopoutMenuCell, GridSubComponentContext, GridUpdatingContext, GridUpdatingContextProvider, GridWrapper, Menu, MenuButton, MenuDivider, MenuGroup, MenuHeader, MenuHeaderItem, MenuHeaderString, MenuItem, MenuRadioGroup, MenuSeparator, MenuSeparatorString, PopoutMenuSeparator, SubMenu, TextAreaInput, TextInputFormatted, bearingCorrectionRangeValidator, bearingCorrectionValueFormatter, bearingNumberParser, bearingRangeValidator, bearingStringValidator, bearingValueFormatter, clickActionButton, clickMenuOption, clickMultiSelectOption, closeMenu, closePopover, convertDDToDMS, countRows, deselectRow, editCell, findActionButton, findCell, findCellContains, findMenuOption, findMultiSelectOption, findOpenPopover, findParentWithClass, findRow, fnOrVar, generateFilterGetter, getMultiSelectOptions, hasParentClass, isCellReadOnly, isFloat, isNotEmpty, openAndClickMenuOption, openAndFindMenuOption, queryMenuOption, queryRow, selectCell, selectRow, stringByteLengthIsInvalid, suppressCellKeyboardEvents, typeInputByLabel, typeInputByPlaceholder, typeOnlyInput, typeOtherInput, typeOtherTextArea, useDeferredPromise, useGenerateOrDefaultId, useGridContext, useGridFilter, useGridPopoverContext, useGridPopoverHook, useMenuState, usePostSortRowsHook, validateMenuOptions, wait$2 as wait };
25205
+ export { ActionButton, ComponentLoadingWrapper, ControlledMenu, Editor, FocusableItem, FormError, GenericCellEditorComponentWrapper, Grid, GridCell, GridCellMultiEditor, GridCellMultiSelectClassRules, GridCellRenderer, GridContext, GridContextProvider, GridFilterButtons, GridFilterColumnsToggle, GridFilterDownloadCsvButton, GridFilterHeaderIconButton, GridFilterQuick, GridFilters, GridFormDropDown, GridFormEditBearing, GridFormMessage, GridFormMultiSelect, GridFormPopoverMenu, GridFormSubComponentTextArea, GridFormSubComponentTextInput, GridFormTextArea, GridFormTextInput, GridHeaderSelect, GridIcon, GridLoadableCell, GridNoRowsOverlay, GridPopoutEditMultiSelect, GridPopoverContext, GridPopoverContextProvider, GridPopoverEditBearing, GridPopoverEditBearingCorrection, GridPopoverEditBearingCorrectionEditorParams, GridPopoverEditBearingEditorParams, GridPopoverEditDropDown, GridPopoverMenu, GridPopoverMessage, GridPopoverTextArea, GridPopoverTextInput, GridRenderPopoutMenuCell, GridSubComponentContext, GridUpdatingContext, GridUpdatingContextProvider, GridWrapper, Menu, MenuButton, MenuDivider, MenuGroup, MenuHeader, MenuHeaderItem, MenuHeaderString, MenuItem, MenuRadioGroup, MenuSeparator, MenuSeparatorString, PopoutMenuSeparator, SubMenu, TextAreaInput, TextInputFormatted, bearingCorrectionRangeValidator, bearingCorrectionValueFormatter, bearingNumberParser, bearingRangeValidator, bearingStringValidator, bearingValueFormatter, clickActionButton, clickMenuOption, clickMultiSelectOption, closeMenu, closePopover, convertDDToDMS, countRows, deselectRow, downloadCsvUseValueFormattersProcessCellCallback, editCell, findActionButton, findCell, findCellContains, findMenuOption, findMultiSelectOption, findOpenPopover, findParentWithClass, findRow, fnOrVar, generateFilterGetter, getMultiSelectOptions, hasParentClass, isCellReadOnly, isFloat, isNotEmpty, openAndClickMenuOption, openAndFindMenuOption, queryMenuOption, queryRow, sanitiseFileName, selectCell, selectRow, stringByteLengthIsInvalid, suppressCellKeyboardEvents, typeInputByLabel, typeInputByPlaceholder, typeOnlyInput, typeOtherInput, typeOtherTextArea, useDeferredPromise, useGenerateOrDefaultId, useGridContext, useGridFilter, useGridPopoverContext, useGridPopoverHook, useMenuState, usePostSortRowsHook, validateMenuOptions, wait$2 as wait };
25004
25206
  //# sourceMappingURL=step-ag-grid.esm.js.map