@linzjs/step-ag-grid 6.1.0 → 7.0.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 (44) hide show
  1. package/dist/index.css +12 -2
  2. package/dist/index.js +389 -213
  3. package/dist/index.js.map +1 -1
  4. package/dist/src/components/GridPopoverHook.d.ts +1 -1
  5. package/dist/src/components/gridForm/GridFormMultiSelect.d.ts +17 -12
  6. package/dist/src/components/gridPopoverEdit/GridPopoutEditMultiSelect.d.ts +1 -1
  7. package/dist/src/contexts/GridSubComponentContext.d.ts +1 -0
  8. package/dist/src/lui/ActionButton.d.ts +2 -1
  9. package/dist/src/lui/FormError.d.ts +2 -1
  10. package/dist/src/lui/TextAreaInput.d.ts +1 -1
  11. package/dist/src/lui/TextInputFormatted.d.ts +1 -1
  12. package/dist/src/react-menu3/utils/utils.d.ts +1 -0
  13. package/dist/src/utils/textMatcher.d.ts +13 -0
  14. package/dist/src/utils/textValidator.d.ts +3 -2
  15. package/dist/src/utils/util.d.ts +2 -1
  16. package/dist/step-ag-grid.esm.js +390 -215
  17. package/dist/step-ag-grid.esm.js.map +1 -1
  18. package/package.json +9 -8
  19. package/src/components/GridPopoverHook.tsx +7 -1
  20. package/src/components/gridForm/GridFormDropDown.tsx +15 -10
  21. package/src/components/gridForm/GridFormMultiSelect.tsx +290 -188
  22. package/src/components/gridForm/GridFormPopoverMenu.tsx +1 -0
  23. package/src/components/gridForm/GridFormSubComponentTextArea.tsx +2 -2
  24. package/src/components/gridForm/GridFormSubComponentTextInput.tsx +3 -5
  25. package/src/components/gridForm/GridFormTextArea.tsx +13 -13
  26. package/src/components/gridForm/GridFormTextInput.tsx +3 -8
  27. package/src/components/gridPopoverEdit/GridPopoutEditMultiSelect.ts +3 -3
  28. package/src/contexts/GridSubComponentContext.ts +2 -0
  29. package/src/lui/ActionButton.tsx +26 -8
  30. package/src/lui/FormError.scss +7 -0
  31. package/src/lui/FormError.tsx +4 -13
  32. package/src/lui/TextAreaInput.tsx +1 -1
  33. package/src/lui/TextInputFormatted.tsx +1 -1
  34. package/src/react-menu3/components/ControlledMenu.tsx +3 -2
  35. package/src/react-menu3/components/MenuList.tsx +2 -12
  36. package/src/react-menu3/hooks/useItems.ts +5 -2
  37. package/src/react-menu3/utils/utils.ts +15 -0
  38. package/src/stories/components/ActionButton.stories.tsx +9 -0
  39. package/src/stories/grid/GridPopoutEditMultiSelect.stories.tsx +93 -17
  40. package/src/styles/Grid.scss +12 -0
  41. package/src/styles/lui-overrides.scss +0 -2
  42. package/src/utils/textMatcher.ts +31 -0
  43. package/src/utils/textValidator.ts +6 -3
  44. package/src/utils/util.ts +6 -7
@@ -1,6 +1,6 @@
1
1
  import { useMemo, useLayoutEffect, useEffect, createContext, memo, forwardRef, useRef, useContext, useState, useCallback, useReducer, cloneElement, useImperativeHandle, Fragment } from 'react';
2
2
  import { unstable_batchedUpdates, flushSync, createPortal } from 'react-dom';
3
- import { findIndex, debounce, isEmpty, delay, sortBy, last, difference, castArray, remove, flatten, xorBy, fromPairs, pick, omit, toPairs, isEqual } from 'lodash-es';
3
+ import { findIndex, debounce, negate, isEmpty, delay, sortBy, last, difference, castArray, remove, flatten, xorBy, pick, groupBy, toPairs, omit } from 'lodash-es';
4
4
  import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
5
5
  import { AgGridReact } from 'ag-grid-react';
6
6
  import { LuiMiniSpinner, LuiIcon, LuiCheckboxInput, LuiButton } from '@linzjs/lui';
@@ -178,7 +178,22 @@ function commonProps(isDisabled, isHovering) {
178
178
  tabIndex: isHovering ? 0 : -1
179
179
  };
180
180
  }
181
- var indexOfNode = function (nodeList, node) { return findIndex(nodeList, node); };
181
+ var indexOfNode = function (nodeList, node) { return findIndex(nodeList, node); };
182
+ var focusFirstInput = function (container) {
183
+ if (!(container instanceof Element))
184
+ return false;
185
+ var inputs = container.querySelectorAll("input[type='text'],textarea");
186
+ var input = inputs[0];
187
+ if (input instanceof HTMLElement) {
188
+ input.focus();
189
+ // Text areas should start at end
190
+ if (input instanceof HTMLTextAreaElement) {
191
+ input.selectionStart = input.value.length;
192
+ }
193
+ return true;
194
+ }
195
+ return false;
196
+ };
182
197
 
183
198
  /******************************************************************************
184
199
  Copyright (c) Microsoft Corporation.
@@ -353,8 +368,9 @@ var useItems = function (menuRef, focusRef) {
353
368
  if (index >= items.length)
354
369
  index = 0;
355
370
  newItem = items[index];
371
+ focusFirstInput(newItem);
356
372
  break;
357
- case HoverActionTypes.DECREASE:
373
+ case HoverActionTypes.DECREASE: {
358
374
  sortItems();
359
375
  index = hoverIndex;
360
376
  if (index < 0)
@@ -363,7 +379,9 @@ var useItems = function (menuRef, focusRef) {
363
379
  if (index < 0)
364
380
  index = items.length - 1;
365
381
  newItem = items[index];
382
+ focusFirstInput(newItem);
366
383
  break;
384
+ }
367
385
  default:
368
386
  if (process.env.NODE_ENV !== "production")
369
387
  throw new Error("[React-Menu] Unknown hover action type: ".concat(actionType));
@@ -1199,26 +1217,13 @@ var MenuList = function (_a) {
1199
1217
  else if (captureFocus) {
1200
1218
  // Use a timeout here because if set focus immediately, page might scroll unexpectedly.
1201
1219
  var id_1 = setTimeout(function () {
1202
- var _a, _b, _c;
1220
+ var _a, _b;
1203
1221
  // If focus has already been set to a children element, don't set focus on menu or item
1204
1222
  if (!menuRef.current.contains(document.activeElement)) {
1205
1223
  // Handle popover portal focus
1206
1224
  var popupElement = (_a = focusRef.current) === null || _a === void 0 ? void 0 : _a.nextSibling;
1207
- if (popupElement instanceof Element) {
1208
- var input = popupElement.querySelectorAll("input,textarea")[0];
1209
- if (input) {
1210
- input.focus();
1211
- // Text areas should start at end
1212
- if (input instanceof HTMLTextAreaElement) {
1213
- input.selectionStart = input.value.length;
1214
- }
1215
- }
1216
- else {
1217
- (_b = focusRef.current) === null || _b === void 0 ? void 0 : _b.focus();
1218
- }
1219
- }
1220
- else {
1221
- (_c = focusRef.current) === null || _c === void 0 ? void 0 : _c.focus();
1225
+ if (!focusFirstInput(popupElement)) {
1226
+ (_b = focusRef.current) === null || _b === void 0 ? void 0 : _b.focus();
1222
1227
  }
1223
1228
  setItemFocus();
1224
1229
  }
@@ -1276,10 +1281,7 @@ var MenuList = function (_a) {
1276
1281
  return (jsxs("ul", __assign({ role: "menu", "aria-label": ariaLabel }, mergeProps({ onKeyDown: onKeyDown, onAnimationEnd: onAnimationEnd }, restProps), commonProps(isDisabled), { ref: useCombinedRef(externalRef, menuRef), className: useBEM({ block: menuClass, modifiers: modifiers, className: menuClassName }), style: __assign(__assign(__assign(__assign({}, menuStyle), overflowStyle), dontShrinkOps), { margin: 0, display: state === "closed" ? "none" : undefined, position: "absolute", left: menuPosition.x, top: menuPosition.y }) }, { children: [jsx("div", { ref: focusRef, tabIndex: -1, style: { position: "absolute", left: 0, top: 0 } }), arrow && (jsx("div", { className: _arrowClass, style: __assign(__assign({}, arrowStyle), { position: "absolute", left: arrowPosition.x, top: arrowPosition.y }), ref: arrowRef })), jsx(MenuListContext.Provider, __assign({ value: listContext }, { children: jsx(MenuListItemContext.Provider, __assign({ value: itemContext }, { children: jsx(HoverItemContext.Provider, __assign({ value: hoverItem }, { children: children })) })) }))] })));
1277
1282
  };
1278
1283
 
1279
- // Typed version of lodash !isEmpty
1280
- var isNotEmpty = function (obj) {
1281
- return !isEmpty(obj);
1282
- };
1284
+ var isNotEmpty = negate(isEmpty);
1283
1285
  var wait = function (timeoutMs) {
1284
1286
  return new Promise(function (resolve) {
1285
1287
  setTimeout(resolve, timeoutMs);
@@ -1290,15 +1292,16 @@ var isFloat = function (value) {
1290
1292
  return regexp.test(value);
1291
1293
  };
1292
1294
  var hasParentClass = function (className, child) {
1293
- var node = child;
1294
- while (node) {
1295
+ for (var node = child; node; node = node.parentNode) {
1295
1296
  // When nodes are in portals they aren't type node anymore hence treating it as any here
1296
1297
  if (node.classList && node.classList.contains(className)) {
1297
1298
  return true;
1298
1299
  }
1299
- node = node.parentNode;
1300
1300
  }
1301
1301
  return false;
1302
+ };
1303
+ var stringByteLengthIsInvalid = function (str, maxBytes) {
1304
+ return new TextEncoder().encode(str).length > maxBytes;
1302
1305
  };
1303
1306
 
1304
1307
  var EventHandlersContext = createContext({
@@ -1394,7 +1397,8 @@ var ControlledMenuFr = function (_a, externalRef) {
1394
1397
  if (activeElement !== firstInputEl && activeElement !== lastInputEl)
1395
1398
  return;
1396
1399
  var isTextArea = activeElement.nodeName === "TEXTAREA";
1397
- var suppressEnterAutoSave = activeElement.getAttribute("data-disableEnterAutoSave") || isTextArea;
1400
+ var suppressEnterAutoSave = activeElement.getAttribute("data-disableenterautoSave") || isTextArea;
1401
+ var allowTabToSave = activeElement.getAttribute("data-allowtabtoSave");
1398
1402
  var invokeSave = function (reason) {
1399
1403
  var _a, _b;
1400
1404
  if (!(saveButtonRef === null || saveButtonRef === void 0 ? void 0 : saveButtonRef.current))
@@ -1405,7 +1409,7 @@ var ControlledMenuFr = function (_a, externalRef) {
1405
1409
  switch (activeElement.nodeName) {
1406
1410
  case "TEXTAREA":
1407
1411
  case "INPUT": {
1408
- if (activeElement === lastInputEl && activeElement === firstInputEl) {
1412
+ if ((activeElement === lastInputEl && activeElement === firstInputEl) || allowTabToSave) {
1409
1413
  if (ev.key === "Tab") {
1410
1414
  // Can't forward/backwards tab out of popup
1411
1415
  ev.preventDefault();
@@ -2424,7 +2428,8 @@ var GridSubComponentContext = createContext({
2424
2428
  console.error("GridSubComponentContext triggerSave no context");
2425
2429
  return [2 /*return*/];
2426
2430
  });
2427
- }); }
2431
+ }); },
2432
+ context: null
2428
2433
  });
2429
2434
 
2430
2435
  function r(e){var t,f,n="";if("string"==typeof e||"number"==typeof e)n+=e;else if("object"==typeof e)if(Array.isArray(e))for(t=0;t<e.length;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=" "),n+=f);else for(t in e)e[t]&&(n&&(n+=" "),n+=t);return n}function clsx(){for(var e,t,f=0,n="";f<arguments.length;)(e=arguments[f++])&&(t=r(e))&&(n&&(n+=" "),n+=t);return n}
@@ -2822,11 +2827,11 @@ function styleInject(css, ref) {
2822
2827
  }
2823
2828
  }
2824
2829
 
2825
- var css_248z$7 = ".AgGridGenericCellRenderer-icon{margin-right:4px}.AgGridGenericCellRenderer-ic_infoIcon{fill:#3a7cdf;margin-right:4px}.AgGridGenericCellRenderer-ic_warningIcon{fill:#ea6a2e;margin-right:4px}";
2826
- styleInject(css_248z$7);
2830
+ var css_248z$8 = ".AgGridGenericCellRenderer-icon{margin-right:4px}.AgGridGenericCellRenderer-ic_infoIcon{fill:#3a7cdf;margin-right:4px}.AgGridGenericCellRenderer-ic_warningIcon{fill:#ea6a2e;margin-right:4px}";
2831
+ styleInject(css_248z$8);
2827
2832
 
2828
- var css_248z$6 = ".GridLoadableCell-container{align-items:center;display:flex}";
2829
- styleInject(css_248z$6);
2833
+ var css_248z$7 = ".GridLoadableCell-container{align-items:center;display:flex}";
2834
+ styleInject(css_248z$7);
2830
2835
 
2831
2836
  var GridLoadableCell = function (props) {
2832
2837
  if (props.isLoading) {
@@ -3000,11 +3005,11 @@ var GridRenderPopoutMenuCell = function (props) {
3000
3005
  return (jsx(GridLoadableCell, __assign({ isLoading: isLoading, className: disabled ? "GridPopoutMenu-burgerDisabled" : "GridPopoutMenu-burger" }, { children: jsx(LuiIcon, { name: "ic_more_vert", alt: "More actions", size: "md" }) })));
3001
3006
  };
3002
3007
 
3003
- var css_248z$5 = ".GridMultiSelect-containerSmall .GridFormMultiSelect-options{max-height:130px;overflow-y:auto}.GridMultiSelect-containerMedium .GridFormMultiSelect-options{max-height:190px;overflow-y:auto}.GridMultiSelect-containerLarge .GridFormMultiSelect-options{max-height:320px;overflow-y:auto}.GridMultiSelect-containerUnlimited .GridFormMultiSelect-options{overflow-y:auto}";
3004
- styleInject(css_248z$5);
3008
+ var css_248z$6 = ".GridMultiSelect-containerSmall .GridFormMultiSelect-options{max-height:130px;overflow-y:auto}.GridMultiSelect-containerMedium .GridFormMultiSelect-options{max-height:190px;overflow-y:auto}.GridMultiSelect-containerLarge .GridFormMultiSelect-options{max-height:320px;overflow-y:auto}.GridMultiSelect-containerUnlimited .GridFormMultiSelect-options{overflow-y:auto}";
3009
+ styleInject(css_248z$6);
3005
3010
 
3006
- var css_248z$4 = ".GridPopoverEditDropDown-containerSmall .GridFormDropDown-options{max-height:120px;overflow-y:auto}.GridPopoverEditDropDown-containerMedium .GridFormDropDown-options{max-height:220px;overflow-y:auto}.GridPopoverEditDropDown-containerLarge .GridFormDropDown-options{max-height:400px;overflow-y:auto}.GridPopoverEditDropDown-containerUnlimited .GridFormDropDown-options{overflow-y:auto}";
3007
- styleInject(css_248z$4);
3011
+ var css_248z$5 = ".GridPopoverEditDropDown-containerSmall .GridFormDropDown-options{max-height:120px;overflow-y:auto}.GridPopoverEditDropDown-containerMedium .GridFormDropDown-options{max-height:220px;overflow-y:auto}.GridPopoverEditDropDown-containerLarge .GridFormDropDown-options{max-height:400px;overflow-y:auto}.GridPopoverEditDropDown-containerUnlimited .GridFormDropDown-options{overflow-y:auto}";
3012
+ styleInject(css_248z$5);
3008
3013
 
3009
3014
  /* global setTimeout, clearTimeout */
3010
3015
 
@@ -3094,14 +3099,16 @@ var GridFormDropDown = function (props) {
3094
3099
  var optionsInitialising = useRef(false);
3095
3100
  var _d = useState(null), options = _d[0], setOptions = _d[1];
3096
3101
  var subComponentIsValid = useRef(false);
3097
- var _e = useState(), subSelectedValue = _e[0], setSubSelectedValue = _e[1];
3102
+ var subComponentInitialValue = useRef(null);
3103
+ var _e = useState(null), subSelectedValue = _e[0], setSubSelectedValue = _e[1];
3098
3104
  var _f = useState(null), selectedSubComponent = _f[0], setSelectedSubComponent = _f[1];
3099
3105
  var selectItemHandler = useCallback(function (value, subComponentValue) { return __awaiter(void 0, void 0, void 0, function () {
3100
3106
  var hasChanged;
3101
3107
  return __generator(this, function (_a) {
3102
3108
  switch (_a.label) {
3103
3109
  case 0:
3104
- hasChanged = selectedRows.some(function (row) { return row[field] !== value; });
3110
+ hasChanged = selectedRows.some(function (row) { return row[field] !== value; }) ||
3111
+ (subComponentValue !== undefined && subComponentInitialValue.current !== JSON.stringify(subComponentValue));
3105
3112
  if (!hasChanged) return [3 /*break*/, 3];
3106
3113
  if (!props.onSelectedItem) return [3 /*break*/, 2];
3107
3114
  return [4 /*yield*/, props.onSelectedItem({ selectedRows: selectedRows, value: value, subComponentValue: subComponentValue })];
@@ -3277,15 +3284,10 @@ var GridFormDropDown = function (props) {
3277
3284
  var _a;
3278
3285
  return item.value === MenuSeparatorString ? (jsx(MenuDivider, {}, "$$divider_".concat(index))) : item.value === MenuHeaderString ? (jsx(MenuHeader, { children: item.label }, "$$header_".concat(index))) : filteredValues.includes(item.value) ? null : (jsxs("div", { children: [jsxs(MenuItem, __assign({ disabled: !!item.disabled, title: item.disabled && typeof item.disabled !== "boolean" ? item.disabled : "", value: item.value, onClick: function (e) {
3279
3286
  if (item.subComponent) {
3280
- if (selectedSubComponent === item) {
3281
- // toggle selection off
3282
- setSelectedSubComponent(null);
3283
- subComponentIsValid.current = true;
3284
- }
3285
- else {
3286
- // toggle selection on
3287
- setSelectedSubComponent(item);
3288
- }
3287
+ // toggle selection
3288
+ setSelectedSubComponent(selectedSubComponent === item ? null : item);
3289
+ subComponentIsValid.current = true;
3290
+ subComponentInitialValue.current = null;
3289
3291
  e.keepOpen = true;
3290
3292
  }
3291
3293
  else {
@@ -3296,10 +3298,15 @@ var GridFormDropDown = function (props) {
3296
3298
  : CloseReason.CLICK).then();
3297
3299
  }
3298
3300
  } }, { children: [(_a = item.label) !== null && _a !== void 0 ? _a : (item.value == null ? "<".concat(item.value, ">") : "".concat(item.value)), item.subComponent ? "..." : ""] }), "".concat(fieldToString(field), "-").concat(index)), item.subComponent && selectedSubComponent === item && (jsx(FocusableItem, __assign({ className: "LuiDeprecatedForms" }, { children: function (ref) { return (jsx(GridSubComponentContext.Provider, __assign({ value: {
3301
+ context: { options: options },
3299
3302
  data: data,
3300
3303
  value: subSelectedValue,
3301
3304
  setValue: function (value) {
3302
3305
  setSubSelectedValue(value);
3306
+ if (subComponentInitialValue.current === null) {
3307
+ // copy the default value of the sub-component so we can change detect on save
3308
+ subComponentInitialValue.current = JSON.stringify(value);
3309
+ }
3303
3310
  },
3304
3311
  setValid: function (valid) {
3305
3312
  subComponentIsValid.current = valid;
@@ -3314,154 +3321,326 @@ var GridFormDropDown = function (props) {
3314
3321
  })] }) }))] }));
3315
3322
  };
3316
3323
 
3324
+ var css_248z$4 = ".helpText{color:#6b6966;font-size:.75rem;font-weight:400}";
3325
+ styleInject(css_248z$4);
3326
+
3327
+ var FormError = function (props) {
3328
+ return (jsxs(Fragment$1, { children: [props.error && (jsx("span", __assign({ className: "LuiTextInput-error", style: { paddingLeft: 0 } }, { children: props.error }))), props.helpText && !props.error && jsx("span", __assign({ className: "helpText" }, { children: props.helpText }))] }));
3329
+ };
3330
+
3331
+ function escapeStringRegexp(string) {
3332
+ if (typeof string !== 'string') {
3333
+ throw new TypeError('Expected a string');
3334
+ }
3335
+
3336
+ // Escape characters with special meaning either inside or outside character sets.
3337
+ // Use a simple backslash escape when it’s always valid, and a `\xnn` escape when the simpler form would be disallowed by Unicode patterns’ stricter grammar.
3338
+ return string
3339
+ .replace(/[|\\{}()[\]^$+*?.]/g, '\\$&')
3340
+ .replace(/-/g, '\\x2d');
3341
+ }
3342
+
3343
+ const regexpCache = new Map();
3344
+
3345
+ const sanitizeArray = (input, inputName) => {
3346
+ if (!Array.isArray(input)) {
3347
+ switch (typeof input) {
3348
+ case 'string':
3349
+ input = [input];
3350
+ break;
3351
+ case 'undefined':
3352
+ input = [];
3353
+ break;
3354
+ default:
3355
+ throw new TypeError(`Expected '${inputName}' to be a string or an array, but got a type of '${typeof input}'`);
3356
+ }
3357
+ }
3358
+
3359
+ return input.filter(string => {
3360
+ if (typeof string !== 'string') {
3361
+ if (typeof string === 'undefined') {
3362
+ return false;
3363
+ }
3364
+
3365
+ throw new TypeError(`Expected '${inputName}' to be an array of strings, but found a type of '${typeof string}' in the array`);
3366
+ }
3367
+
3368
+ return true;
3369
+ });
3370
+ };
3371
+
3372
+ const makeRegexp = (pattern, options) => {
3373
+ options = {
3374
+ caseSensitive: false,
3375
+ ...options,
3376
+ };
3377
+
3378
+ const cacheKey = pattern + JSON.stringify(options);
3379
+
3380
+ if (regexpCache.has(cacheKey)) {
3381
+ return regexpCache.get(cacheKey);
3382
+ }
3383
+
3384
+ const negated = pattern[0] === '!';
3385
+
3386
+ if (negated) {
3387
+ pattern = pattern.slice(1);
3388
+ }
3389
+
3390
+ pattern = escapeStringRegexp(pattern).replace(/\\\*/g, '[\\s\\S]*');
3391
+
3392
+ const regexp = new RegExp(`^${pattern}$`, options.caseSensitive ? '' : 'i');
3393
+ regexp.negated = negated;
3394
+ regexpCache.set(cacheKey, regexp);
3395
+
3396
+ return regexp;
3397
+ };
3398
+
3399
+ const baseMatcher = (inputs, patterns, options, firstMatchOnly) => {
3400
+ inputs = sanitizeArray(inputs, 'inputs');
3401
+ patterns = sanitizeArray(patterns, 'patterns');
3402
+
3403
+ if (patterns.length === 0) {
3404
+ return [];
3405
+ }
3406
+
3407
+ patterns = patterns.map(pattern => makeRegexp(pattern, options));
3408
+
3409
+ const {allPatterns} = options || {};
3410
+ const result = [];
3411
+
3412
+ for (const input of inputs) {
3413
+ // String is included only if it matches at least one non-negated pattern supplied.
3414
+ // Note: the `allPatterns` option requires every non-negated pattern to be matched once.
3415
+ // Matching a negated pattern excludes the string.
3416
+ let matches;
3417
+ const didFit = [...patterns].fill(false);
3418
+
3419
+ for (const [index, pattern] of patterns.entries()) {
3420
+ if (pattern.test(input)) {
3421
+ didFit[index] = true;
3422
+ matches = !pattern.negated;
3423
+
3424
+ if (!matches) {
3425
+ break;
3426
+ }
3427
+ }
3428
+ }
3429
+
3430
+ if (
3431
+ !(
3432
+ matches === false
3433
+ || (matches === undefined && patterns.some(pattern => !pattern.negated))
3434
+ || (allPatterns && didFit.some((yes, index) => !yes && !patterns[index].negated))
3435
+ )
3436
+ ) {
3437
+ result.push(input);
3438
+
3439
+ if (firstMatchOnly) {
3440
+ break;
3441
+ }
3442
+ }
3443
+ }
3444
+
3445
+ return result;
3446
+ };
3447
+
3448
+ function isMatch(inputs, patterns, options) {
3449
+ return baseMatcher(inputs, patterns, options, true).length > 0;
3450
+ }
3451
+
3452
+ /**
3453
+ * Text matching with wildcards and multiple matchers.
3454
+ *
3455
+ * "L" => L*
3456
+ * "L*" => L*
3457
+ * "*L*" => *L*
3458
+ * "*L" => *L
3459
+ * "A B" => A* and B*
3460
+ * "A B, C" => (A* and B*) or C*
3461
+ *
3462
+ * Returns ture if there's a text match.
3463
+ */
3464
+ var textMatch = function (text, filter) {
3465
+ if (text == null)
3466
+ return true;
3467
+ var superFilters = filter
3468
+ .split(",")
3469
+ .map(function (sf) { return sf.trim(); })
3470
+ .filter(function (sf) { return sf; });
3471
+ var values = text.replaceAll(",", " ").trim().split(/\s+/);
3472
+ return (isEmpty(superFilters) || // Not filtered
3473
+ superFilters.some(function (superFilter) {
3474
+ var subFilters = superFilter.split(/\s+/).map(function (s) { return s.replaceAll(/([!?])/g, "\\$1"); });
3475
+ return subFilters.every(function (subFilter) { return values.some(function (value) { return isMatch(value, subFilter); }); });
3476
+ }));
3477
+ };
3478
+
3317
3479
  var GridFormMultiSelect = function (props) {
3318
3480
  var _a = useGridPopoverContext(), selectedRows = _a.selectedRows, data = _a.data;
3319
- var initialiseValues = useMemo(function () {
3320
- var r = props.initialSelectedValues && props.initialSelectedValues(selectedRows);
3321
- // convert array of strings to object<value,null>
3322
- return Array.isArray(r) ? fromPairs(r.map(function (v) { return [v, null]; })) : r;
3323
- }, [props, selectedRows]);
3324
- var subComponentIsValid = useRef({});
3325
- var _b = useState(function () { return initialiseValues !== null && initialiseValues !== void 0 ? initialiseValues : {}; }), selectedValues = _b[0], setSelectedValues = _b[1];
3326
- var _c = useState(""), filter = _c[0], setFilter = _c[1];
3327
- var _d = useState([]), filteredValues = _d[0], setFilteredValues = _d[1];
3481
+ var subComponentIsValidRef = useRef({});
3328
3482
  var optionsInitialising = useRef(false);
3329
- var _e = useState(), options = _e[0], setOptions = _e[1];
3483
+ var _b = useState(""), filter = _b[0], setFilter = _b[1];
3484
+ var _c = useState(""), initialValues = _c[0], setInitialValues = _c[1];
3485
+ var _d = useState(), options = _d[0], setOptions = _d[1];
3330
3486
  var invalid = useCallback(function () {
3331
- var validations = pick(subComponentIsValid.current, Object.keys(selectedValues));
3332
- return Object.values(validations).some(function (v) { return !v; });
3333
- }, [selectedValues]);
3487
+ if (!options)
3488
+ return true;
3489
+ var selectedValues = options.filter(function (o) { return o.checked; }).map(function (o) { return o.value; });
3490
+ var subValidations = pick(subComponentIsValidRef.current, selectedValues);
3491
+ if (Object.values(subValidations).some(function (v) { return !v; }))
3492
+ return true;
3493
+ return (props.invalid &&
3494
+ props.invalid(selectedRows, options.filter(function (o) { return o.checked; })));
3495
+ }, [options, props, selectedRows]);
3334
3496
  var save = useCallback(function (selectedRows) { return __awaiter(void 0, void 0, void 0, function () {
3335
- var validations, notValid, menuOptionSubValueResult;
3336
3497
  return __generator(this, function (_a) {
3337
- switch (_a.label) {
3338
- case 0:
3339
- if (!props.onSave) return [3 /*break*/, 2];
3340
- if (!options) {
3341
- console.error("options not initialised");
3342
- return [2 /*return*/, false];
3343
- }
3344
- validations = pick(subComponentIsValid.current, Object.keys(selectedValues));
3345
- notValid = Object.values(validations).some(function (v) { return !v; });
3346
- if (notValid)
3347
- return [2 /*return*/, false];
3348
- menuOptionSubValueResult = toPairs(selectedValues).map(function (_a) {
3349
- var value = _a[0], subValue = _a[1];
3350
- var o = __assign({}, options.find(function (o) { return o.value == value; }));
3351
- o.subValue = subValue;
3352
- return o;
3353
- });
3354
- if (isEqual(initialiseValues, selectedValues)) {
3355
- // No changes to save
3356
- return [2 /*return*/, true];
3357
- }
3358
- return [4 /*yield*/, props.onSave(selectedRows, menuOptionSubValueResult)];
3359
- case 1: return [2 /*return*/, _a.sent()];
3360
- case 2: return [2 /*return*/, true];
3361
- }
3498
+ if (!options || !props.onSave)
3499
+ return [2 /*return*/, true];
3500
+ // Any changes to save?
3501
+ if (initialValues === JSON.stringify(options))
3502
+ return [2 /*return*/, true];
3503
+ return [2 /*return*/, props.onSave(selectedRows, options.filter(function (o) { return o.checked; }))];
3362
3504
  });
3363
- }); }, [initialiseValues, options, props, selectedValues]);
3505
+ }); }, [initialValues, options, props]);
3364
3506
  // Load up options list if it's async function
3365
3507
  useEffect(function () {
3366
- var _a;
3367
3508
  if (options || optionsInitialising.current)
3368
3509
  return;
3369
3510
  optionsInitialising.current = true;
3370
- var optionsConf = (_a = props.options) !== null && _a !== void 0 ? _a : [];
3371
3511
  (function () { return __awaiter(void 0, void 0, void 0, function () {
3372
- var optionsList;
3373
- return __generator(this, function (_a) {
3374
- switch (_a.label) {
3512
+ var optionsList, _a;
3513
+ return __generator(this, function (_b) {
3514
+ switch (_b.label) {
3375
3515
  case 0:
3376
- if (!(typeof optionsConf == "function")) return [3 /*break*/, 2];
3377
- return [4 /*yield*/, optionsConf(selectedRows)];
3516
+ if (!(typeof props.options === "function")) return [3 /*break*/, 2];
3517
+ return [4 /*yield*/, props.options(selectedRows)];
3378
3518
  case 1:
3379
- optionsConf = _a.sent();
3380
- _a.label = 2;
3519
+ _a = _b.sent();
3520
+ return [3 /*break*/, 3];
3381
3521
  case 2:
3382
- optionsList = optionsConf === null || optionsConf === void 0 ? void 0 : optionsConf.map(function (item) {
3383
- if (item == null || typeof item === "string" || typeof item === "number") {
3384
- item = { value: item, label: item };
3385
- }
3386
- return item;
3387
- });
3388
- if (props.filtered) {
3389
- // This is needed otherwise when filter input is rendered and sets autofocus
3390
- // the mouse up of the double click edit triggers the cell to cancel editing
3391
- delay(function () { return setOptions(optionsList); }, 100);
3392
- }
3393
- else {
3394
- setOptions(optionsList);
3395
- }
3522
+ _a = props.options;
3523
+ _b.label = 3;
3524
+ case 3:
3525
+ optionsList = _a;
3526
+ setInitialValues(JSON.stringify(optionsList));
3527
+ setOptions(optionsList);
3396
3528
  optionsInitialising.current = false;
3397
3529
  return [2 /*return*/];
3398
3530
  }
3399
3531
  });
3400
3532
  }); })();
3401
- }, [props.filtered, props.options, options, selectedRows]);
3402
- useEffect(function () {
3403
- if (!props.filtered || options == null)
3533
+ }, [options, props, selectedRows]);
3534
+ /**
3535
+ * Groups options into their header groups
3536
+ */
3537
+ var headerGroups = useMemo(function () {
3538
+ return options &&
3539
+ groupBy(options.filter(function (o) { return textMatch(o.label, filter) && o.value; }), "filter");
3540
+ }, [filter, options]);
3541
+ var headers = useMemo(function () { var _a; return (_a = props.headers) !== null && _a !== void 0 ? _a : [{ header: "" }]; }, [props.headers]);
3542
+ var _e = useGridPopoverHook({
3543
+ className: props.className,
3544
+ invalid: invalid,
3545
+ save: save
3546
+ }), popoverWrapper = _e.popoverWrapper, triggerSave = _e.triggerSave;
3547
+ return popoverWrapper(jsx(ComponentLoadingWrapper, __assign({ loading: !options, className: "GridFormMultiSelect-container" }, { children: options && (jsxs(Fragment$1, { children: [props.filtered && (jsx(FilterInput, __assign({}, { headerGroups: headerGroups, options: options, setOptions: setOptions, filter: filter, setFilter: setFilter }, { filterHelpText: props.filterHelpText, onSelectFilter: props.onSelectFilter, filterPlaceholder: props.filterPlaceholder }))), headerGroups && (jsx("div", __assign({ className: "GridFormMultiSelect-options" }, { children: headers.map(function (header) {
3548
+ var subOptions = headerGroups["".concat(header.filter)];
3549
+ return (!isEmpty(subOptions) && (jsxs(Fragment$1, { children: [header.header && jsx(MenuHeader, { children: header.header }), subOptions.map(function (item, index) {
3550
+ return item.value === MenuSeparatorString ? (jsx(MenuDivider, {}, "div_".concat(index))) : (jsxs(Fragment, { children: [jsx(MenuRadioItem, { item: item, options: options, setOptions: setOptions }), item.checked && item.subComponent && (jsx(MenuSubComponent, __assign({}, { item: item, options: options, setOptions: setOptions, data: data, triggerSave: triggerSave }, { subComponentIsValid: subComponentIsValidRef.current })))] }, "val_".concat(item.value)));
3551
+ })] })));
3552
+ }) })))] })) })));
3553
+ };
3554
+ var FilterInput = function (props) {
3555
+ var options = props.options, setOptions = props.setOptions, onSelectFilter = props.onSelectFilter, filter = props.filter, setFilter = props.setFilter, headerGroups = props.headerGroups, filterPlaceholder = props.filterPlaceholder, filterHelpText = props.filterHelpText;
3556
+ var toggleSelectAllVisible = useCallback(function () {
3557
+ if (!options || !headerGroups)
3404
3558
  return;
3405
- setFilteredValues(options
3406
- .map(function (option) {
3407
- if (option.label != null && typeof option.label !== "string") {
3408
- console.error("Cannot filter non-string labels", option);
3409
- return undefined;
3410
- }
3411
- var str = option.label || "";
3412
- return str.toLowerCase().indexOf(filter.trim()) === -1 ? option.value : undefined;
3413
- })
3414
- .filter(function (r) { return r !== undefined; }));
3415
- }, [props.filtered, filter, options]);
3416
- var toggleValue = useCallback(function (item) {
3417
- var _a;
3418
- if ("".concat(item.value) in selectedValues) {
3419
- setSelectedValues(omit(selectedValues, ["".concat(item.value)]));
3559
+ if (isEmpty(filter.trim())) {
3560
+ // Toggle off if any items are checked otherwise on
3561
+ var anyChecked_1 = options.some(function (o) { return o.checked; });
3562
+ options.forEach(function (o) { return (o.checked = !anyChecked_1); });
3420
3563
  }
3421
3564
  else {
3422
- setSelectedValues(__assign(__assign({}, selectedValues), (_a = {}, _a["".concat(item.value)] = null, _a)));
3565
+ // Toggle on if any filtered items are checked otherwise off
3566
+ var anyChecked_2 = Object.values(headerGroups).some(function (headerOptions) {
3567
+ return headerOptions.some(function (o) { return o.checked === false; });
3568
+ });
3569
+ Object.values(headerGroups).forEach(function (headerOptions) {
3570
+ headerOptions.forEach(function (o) {
3571
+ if (o.checked !== undefined)
3572
+ o.checked = anyChecked_2;
3573
+ });
3574
+ });
3423
3575
  }
3424
- }, [selectedValues]);
3425
- var _f = useGridPopoverHook({
3426
- className: props.className,
3427
- invalid: invalid,
3428
- save: save
3429
- }), popoverWrapper = _f.popoverWrapper, triggerSave = _f.triggerSave;
3430
- return popoverWrapper(jsx(ComponentLoadingWrapper, __assign({ loading: !options, className: "GridFormMultiSelect-container" }, { children: jsxs(Fragment$1, { children: [options && props.filtered && (jsxs(Fragment$1, { children: [jsx(FocusableItem, __assign({ className: "filter-item" }, { children: function (_a) {
3431
- var _b;
3432
- var ref = _a.ref;
3433
- return (jsx("div", __assign({ style: { display: "flex", width: "100%" }, className: "GridFormMultiSelect-filter" }, { children: jsx("input", { autoFocus: true, className: "free-text-input", style: { border: "0px" }, ref: ref, type: "text", placeholder: (_b = props.filterPlaceholder) !== null && _b !== void 0 ? _b : "Placeholder", "data-testid": "filteredMenu-free-text-input", defaultValue: "", onChange: function (e) { return setFilter(e.target.value.toLowerCase()); } }) })));
3434
- } }), "filter"), jsx(MenuDivider, {}, "$$divider_filter")] })), jsx("div", __assign({ className: "GridFormMultiSelect-options" }, { children: options === null || options === void 0 ? void 0 : options.map(function (item, index) {
3435
- var _a;
3436
- return item.value === MenuSeparatorString ? (jsx(MenuDivider, {}, "$$divider_".concat(index))) : filteredValues.includes(item.value) ? null : (jsxs("div", { children: [jsx(MenuItem, __assign({ onClick: function (e) {
3437
- // Global react-menu MenuItem handler handles tabs
3438
- if (e.key !== "Tab") {
3439
- e.keepOpen = true;
3440
- toggleValue(item);
3441
- }
3442
- } }, { children: jsx(LuiCheckboxInput, { isChecked: "".concat(item.value) in selectedValues, value: "".concat(item.value), label: (_a = item.label) !== null && _a !== void 0 ? _a : (item.value == null ? "<".concat(item.value, ">") : "".concat(item.value)), inputProps: {
3443
- onClick: function (e) {
3444
- e.preventDefault();
3445
- e.stopPropagation();
3446
- return false;
3447
- }
3448
- }, onChange: function () {
3449
- /*Do nothing, change handled by menuItem*/
3450
- } }) })), "".concat(item.value) in selectedValues && item.subComponent && (jsx(FocusableItem, __assign({ className: "LuiDeprecatedForms" }, { children: function (_) {
3451
- return item.subComponent && (jsx(GridSubComponentContext.Provider, __assign({ value: {
3452
- data: data,
3453
- value: selectedValues["".concat(item.value)],
3454
- setValue: function (value) {
3455
- var _a;
3456
- setSelectedValues(__assign(__assign({}, selectedValues), (_a = {}, _a["".concat(item.value)] = value, _a)));
3457
- },
3458
- setValid: function (valid) {
3459
- subComponentIsValid.current["".concat(item.value)] = valid;
3460
- },
3461
- triggerSave: triggerSave
3462
- } }, { children: jsx(item.subComponent, {}) })));
3463
- } }), "".concat(item.value, "_subcomponent")))] }, "".concat(index)));
3464
- }) }))] }) })));
3576
+ setOptions(__spreadArray([], options, true));
3577
+ }, [filter, headerGroups, options, setOptions]);
3578
+ var addCustomFilterValue = useCallback(function () {
3579
+ if (!options || !onSelectFilter)
3580
+ return;
3581
+ var preFilterOptions = JSON.stringify(options);
3582
+ onSelectFilter(filter.trim(), options);
3583
+ // Detect if options list changed and update
3584
+ if (preFilterOptions === JSON.stringify(options))
3585
+ return;
3586
+ setOptions(__spreadArray([], options, true));
3587
+ setFilter("");
3588
+ }, [filter, onSelectFilter, options, setFilter, setOptions]);
3589
+ var handleKeyUp = useCallback(function (e) {
3590
+ if (e.key === "Enter") {
3591
+ e.stopPropagation();
3592
+ e.preventDefault();
3593
+ if (e.ctrlKey)
3594
+ toggleSelectAllVisible();
3595
+ else if (onSelectFilter)
3596
+ addCustomFilterValue();
3597
+ }
3598
+ }, [addCustomFilterValue, onSelectFilter, toggleSelectAllVisible]);
3599
+ return (jsxs(Fragment$1, { children: [jsx(FocusableItem, __assign({ className: "filter-item" }, { children: function (_) { return (jsxs("div", __assign({ style: { width: "100%" }, className: "GridFormMultiSelect-filter" }, { children: [jsx("input", { className: "LuiTextInput-input", type: "text", placeholder: filterPlaceholder !== null && filterPlaceholder !== void 0 ? filterPlaceholder : "Placeholder", "data-testid": "filteredMenu-free-text-input", value: filter, "data-disableenterautosave": true, "data-allowtabtoSave": true, onChange: function (e) { return setFilter(e.target.value); }, onKeyUp: handleKeyUp }), filterHelpText && (jsx(FormError, { error: null, helpText: typeof filterHelpText === "function" ? filterHelpText(filter.trim(), options) : filterHelpText }))] }))); } }), "filter"), jsx(MenuDivider, {}, "$$divider_filter"), headerGroups && !toPairs(headerGroups).some(function (_a) {
3600
+ _a[0]; var options = _a[1];
3601
+ return !isEmpty(options);
3602
+ }) && (jsx("div", __assign({ className: "szh-menu__item" }, { children: "[No items match the filter]" })))] }));
3603
+ };
3604
+ var MenuRadioItem = function (props) {
3605
+ var _a, _b;
3606
+ var item = props.item, options = props.options, setOptions = props.setOptions;
3607
+ var toggleValue = useCallback(function (item) {
3608
+ item.checked = !item.checked;
3609
+ setOptions(__spreadArray([], options, true));
3610
+ }, [options, setOptions]);
3611
+ return (jsx(MenuItem, __assign({ onClick: function (e) {
3612
+ // Global react-menu MenuItem handler handles tabs
3613
+ if (e.key !== "Tab") {
3614
+ e.keepOpen = true;
3615
+ toggleValue(item);
3616
+ }
3617
+ } }, { children: jsx(LuiCheckboxInput, { isChecked: (_a = item.checked) !== null && _a !== void 0 ? _a : false, value: "".concat(item.value), label: (_b = item.label) !== null && _b !== void 0 ? _b : (item.value == null ? "<".concat(item.value, ">") : "".concat(item.value)), inputProps: {
3618
+ onClick: function (e) {
3619
+ // Click is handled by MenuItem onClick
3620
+ e.preventDefault();
3621
+ e.stopPropagation();
3622
+ }
3623
+ }, onChange: function () {
3624
+ /*Do nothing, change handled by menuItem*/
3625
+ } }) })));
3626
+ };
3627
+ var MenuSubComponent = function (props) {
3628
+ var data = props.data, item = props.item, options = props.options, setOptions = props.setOptions, subComponentIsValid = props.subComponentIsValid, triggerSave = props.triggerSave;
3629
+ return (jsx(FocusableItem, __assign({ className: "LuiDeprecatedForms" }, { children: function (_) {
3630
+ return item.subComponent && (jsx(GridSubComponentContext.Provider, __assign({ value: {
3631
+ context: { options: options },
3632
+ data: data,
3633
+ value: item.subValue,
3634
+ setValue: function (value) {
3635
+ item.subValue = value;
3636
+ setOptions(__spreadArray([], options, true));
3637
+ },
3638
+ setValid: function (valid) {
3639
+ subComponentIsValid["".concat(item.value)] = valid;
3640
+ },
3641
+ triggerSave: triggerSave
3642
+ } }, { children: jsx(item.subComponent, {}) })));
3643
+ } }), "".concat(item.value, "_subcomponent")));
3465
3644
  };
3466
3645
 
3467
3646
  var GridPopoutEditMultiSelect = function (colDef, props) {
@@ -3596,6 +3775,7 @@ var GridFormPopoverMenu = function (props) {
3596
3775
  return popoverWrapper(jsx(ComponentLoadingWrapper, __assign({ loading: !options, className: "GridFormPopupMenu" }, { children: jsx(Fragment$1, { children: options === null || options === void 0 ? void 0 : options.map(function (item, index) {
3597
3776
  return item.label === PopoutMenuSeparator ? (jsx(MenuDivider, {}, "$$divider_".concat(index))) : (!item.hidden && (jsxs(Fragment, { children: [jsx(MenuItem, __assign({ onClick: function (e) { return onMenuItemClick(e, item); }, disabled: !!item.disabled, title: item.disabled && typeof item.disabled !== "boolean" ? item.disabled : "" }, { children: item.label }), "".concat(item.label)), item.subComponent && subComponentSelected === item && (jsx(FocusableItem, __assign({ className: "LuiDeprecatedForms" }, { children: function (_) {
3598
3777
  return item.subComponent && (jsx(GridSubComponentContext.Provider, __assign({ value: {
3778
+ context: {},
3599
3779
  data: data,
3600
3780
  value: subSelectedValue,
3601
3781
  setValue: function (value) {
@@ -3704,12 +3884,6 @@ styleInject(css_248z$2);
3704
3884
  var css_248z$1 = ".LuiTextInput{margin-bottom:0}.LuiTextInput-formatted{color:#beb9b4;line-height:48px;position:absolute;right:14px;top:0}";
3705
3885
  styleInject(css_248z$1);
3706
3886
 
3707
- var FormError = function (props) {
3708
- return (jsxs(Fragment$1, { children: [props.error && (jsxs("span", __assign({ className: "LuiTextInput-error" }, { children: [jsx(LuiIcon, { alt: "error", name: "ic_error", className: "LuiTextInput-error-icon", size: "sm", status: "error" }), props.error] }))), props.helpText && !props.error && (jsx("span", __assign({ style: {
3709
- fontSize: "0.7rem"
3710
- } }, { children: props.helpText })))] }));
3711
- };
3712
-
3713
3887
  var TextInputFormatted = function (props) {
3714
3888
  return (jsxs("div", __assign({ className: clsx("LuiTextInput Grid-popoverContainer", props.error && "hasError", props.className) }, { children: [jsxs("span", __assign({ className: "LuiTextInput-inputWrapper" }, { children: [jsx("input", __assign({ type: "text", spellCheck: true, defaultValue: props.value }, omit(props, ["error", "value", "helpText", "formatted", "className"]), { className: "LuiTextInput-input", onMouseEnter: function (e) {
3715
3889
  e.currentTarget.focus();
@@ -3905,12 +4079,13 @@ var TextAreaInput = function (props) {
3905
4079
  } }, { children: props.value })) }))] })), jsx(FormError, { error: props.error, helpText: props.helpText })] })));
3906
4080
  };
3907
4081
 
3908
- var TextInputValidator = function (props, value, data) {
4082
+ var TextInputValidator = function (props, value, data, context) {
3909
4083
  if (value == null)
3910
4084
  return null;
3911
4085
  // This can happen because subcomponent is invoked without type safety
3912
4086
  if (typeof value !== "string") {
3913
4087
  console.error("Value is not a string", value);
4088
+ return null;
3914
4089
  }
3915
4090
  if (props.required && value.length === 0) {
3916
4091
  return "Some text is required";
@@ -3918,11 +4093,11 @@ var TextInputValidator = function (props, value, data) {
3918
4093
  if (props.maxLength && value.length > props.maxLength) {
3919
4094
  return "Text must be no longer than ".concat(props.maxLength, " characters");
3920
4095
  }
3921
- if (props.maxBytes && new TextEncoder().encode(value).length > props.maxBytes) {
4096
+ if (props.maxBytes && stringByteLengthIsInvalid(value, props.maxBytes)) {
3922
4097
  return "Text must be no longer than ".concat(props.maxLength, " bytes");
3923
4098
  }
3924
4099
  if (props.invalid) {
3925
- return props.invalid(value, data);
4100
+ return props.invalid(value, data, context);
3926
4101
  }
3927
4102
  return null;
3928
4103
  };
@@ -3930,30 +4105,30 @@ var TextInputValidator = function (props, value, data) {
3930
4105
  var GridFormTextArea = function (props) {
3931
4106
  var _a, _b;
3932
4107
  var _c = useGridPopoverContext(), field = _c.field, initialVale = _c.value, data = _c.data;
3933
- var _d = useState(initialVale != null ? "".concat(initialVale) : ""), value = _d[0], setValue = _d[1];
4108
+ var initValue = useMemo(function () { return (initialVale == null ? "" : "".concat(initialVale)); }, [initialVale]);
4109
+ var _d = useState(initValue), value = _d[0], setValue = _d[1];
3934
4110
  var helpText = (_a = props.helpText) !== null && _a !== void 0 ? _a : "Press tab to save";
3935
- var invalid = useCallback(function () { return TextInputValidator(props, value, data); }, [props, value, data]);
4111
+ var invalid = useCallback(function () { return TextInputValidator(props, value, data, {}); }, [props, value, data]);
3936
4112
  var save = useCallback(function (selectedRows) { return __awaiter(void 0, void 0, void 0, function () {
4113
+ var trimmedValue;
3937
4114
  return __generator(this, function (_a) {
3938
4115
  switch (_a.label) {
3939
4116
  case 0:
3940
- if (initialVale === (value !== null && value !== void 0 ? value : ""))
4117
+ if (invalid())
4118
+ return [2 /*return*/, false];
4119
+ trimmedValue = value.trim();
4120
+ // No change, so don't save
4121
+ if (initValue === trimmedValue)
3941
4122
  return [2 /*return*/, true];
3942
4123
  if (!props.onSave) return [3 /*break*/, 2];
3943
- return [4 /*yield*/, props.onSave(selectedRows, value)];
4124
+ return [4 /*yield*/, props.onSave(selectedRows, trimmedValue)];
3944
4125
  case 1: return [2 /*return*/, _a.sent()];
3945
4126
  case 2:
3946
- if (field == null) {
3947
- console.error("ColDef has no field set");
3948
- return [2 /*return*/, false];
3949
- }
3950
- selectedRows.forEach(function (row) {
3951
- row[field] = value;
3952
- });
4127
+ selectedRows.forEach(function (row) { return (row[field] = trimmedValue); });
3953
4128
  return [2 /*return*/, true];
3954
4129
  }
3955
4130
  });
3956
- }); }, [initialVale, value, props, field]);
4131
+ }); }, [invalid, value, initValue, props, field]);
3957
4132
  var popoverWrapper = useGridPopoverHook({
3958
4133
  className: props.className,
3959
4134
  invalid: invalid,
@@ -3970,7 +4145,7 @@ var GridFormTextInput = function (props) {
3970
4145
  var helpText = (_a = props.helpText) !== null && _a !== void 0 ? _a : "Press enter or tab to save";
3971
4146
  var initValue = useMemo(function () { return (initialVale == null ? "" : "".concat(initialVale)); }, [initialVale]);
3972
4147
  var _d = useState(initValue), value = _d[0], setValue = _d[1];
3973
- var invalid = useCallback(function () { return TextInputValidator(props, value, data); }, [data, props, value]);
4148
+ var invalid = useCallback(function () { return TextInputValidator(props, value, data, {}); }, [data, props, value]);
3974
4149
  var save = useCallback(function (selectedRows) { return __awaiter(void 0, void 0, void 0, function () {
3975
4150
  var trimmedValue;
3976
4151
  return __generator(this, function (_a) {
@@ -3979,19 +4154,14 @@ var GridFormTextInput = function (props) {
3979
4154
  if (invalid())
3980
4155
  return [2 /*return*/, false];
3981
4156
  trimmedValue = value.trim();
4157
+ // No change, so don't save
3982
4158
  if (initValue === trimmedValue)
3983
4159
  return [2 /*return*/, true];
3984
4160
  if (!props.onSave) return [3 /*break*/, 2];
3985
4161
  return [4 /*yield*/, props.onSave(selectedRows, trimmedValue)];
3986
4162
  case 1: return [2 /*return*/, _a.sent()];
3987
4163
  case 2:
3988
- if (field == null) {
3989
- console.error("ColDef has no field set");
3990
- return [2 /*return*/, false];
3991
- }
3992
- selectedRows.forEach(function (row) {
3993
- row[field] = trimmedValue;
3994
- });
4164
+ selectedRows.forEach(function (row) { return (row[field] = trimmedValue); });
3995
4165
  return [2 /*return*/, true];
3996
4166
  }
3997
4167
  });
@@ -4010,32 +4180,30 @@ var GridPopoverTextInput = function (colDef, params) {
4010
4180
 
4011
4181
  var GridFormSubComponentTextInput = function (props) {
4012
4182
  var _a;
4013
- var _b = useContext(GridSubComponentContext), value = _b.value, setValue = _b.setValue, setValid = _b.setValid, data = _b.data;
4183
+ var _b = useContext(GridSubComponentContext), value = _b.value, setValue = _b.setValue, setValid = _b.setValid, data = _b.data, context = _b.context;
4014
4184
  var helpText = (_a = props.helpText) !== null && _a !== void 0 ? _a : "Press enter or tab to save";
4015
4185
  // If is not initialised yet as it's just been created then set the default value
4016
4186
  useEffect(function () {
4017
4187
  if (value == null)
4018
4188
  setValue(props.defaultValue);
4019
4189
  }, [props.defaultValue, setValue, value]);
4020
- var invalid = useCallback(function () { return TextInputValidator(props, value, data); }, [data, props, value]);
4190
+ var invalid = useCallback(function () { return TextInputValidator(props, value, data, context); }, [context, data, props, value]);
4021
4191
  useEffect(function () {
4022
4192
  setValid(value != null && invalid() == null);
4023
4193
  }, [setValid, invalid, value]);
4024
- return (jsx(TextInputFormatted, { value: value, error: invalid(), onChange: function (e) { return setValue(e.target.value); }, helpText: helpText, autoFocus: true, placeholder: props.placeholder, style: {
4025
- width: "100%"
4026
- } }));
4194
+ return (jsx(TextInputFormatted, { value: value, error: invalid(), onChange: function (e) { return setValue(e.target.value); }, helpText: helpText, autoFocus: true, placeholder: props.placeholder, style: { width: "100%" } }));
4027
4195
  };
4028
4196
 
4029
4197
  var GridFormSubComponentTextArea = function (props) {
4030
4198
  var _a;
4031
- var _b = useContext(GridSubComponentContext), value = _b.value, data = _b.data, setValue = _b.setValue, setValid = _b.setValid;
4199
+ var _b = useContext(GridSubComponentContext), value = _b.value, data = _b.data, setValue = _b.setValue, setValid = _b.setValid, context = _b.context;
4032
4200
  var helpText = (_a = props.helpText) !== null && _a !== void 0 ? _a : "Press tab to save";
4033
4201
  // If is not initialised yet as it's just been created then set the default value
4034
4202
  useEffect(function () {
4035
4203
  if (value == null)
4036
4204
  setValue(props.defaultValue);
4037
4205
  }, [props.defaultValue, setValue, value]);
4038
- var invalid = useCallback(function () { return TextInputValidator(props, value, data); }, [data, props, value]);
4206
+ var invalid = useCallback(function () { return TextInputValidator(props, value, data, context); }, [data, props, value, context]);
4039
4207
  useEffect(function () {
4040
4208
  setValid(value != null && invalid() == null);
4041
4209
  }, [setValid, invalid, value]);
@@ -4115,16 +4283,17 @@ var useStateDeferred = function (initialValue) {
4115
4283
  var minimumInProgressTimeMs = 950;
4116
4284
  var ActionButton = function (_a) {
4117
4285
  var _b, _c, _d;
4118
- var icon = _a.icon, name = _a.name, inProgressName = _a.inProgressName, dataTestId = _a.dataTestId, className = _a.className, title = _a.title, onAction = _a.onAction, externalSetInProgress = _a.externalSetInProgress, _e = _a.size, size = _e === void 0 ? "sm" : _e, _f = _a.level, level = _f === void 0 ? "tertiary" : _f, ariaLabel = _a["aria-label"];
4119
- var _g = useState(false), inProgress = _g[0], setInProgress = _g[1];
4286
+ var icon = _a.icon, name = _a.name, inProgressName = _a.inProgressName, dataTestId = _a.dataTestId, className = _a.className, title = _a.title, onAction = _a.onAction, externalSetInProgress = _a.externalSetInProgress, _e = _a.size, size = _e === void 0 ? "sm" : _e, _f = _a.iconPosition, iconPosition = _f === void 0 ? "left" : _f, _g = _a.level, level = _g === void 0 ? "tertiary" : _g, ariaLabel = _a["aria-label"];
4287
+ var _h = useState(false), inProgress = _h[0], setInProgress = _h[1];
4120
4288
  var lastInProgress = usePrevious(inProgress !== null && inProgress !== void 0 ? inProgress : false);
4121
- var _h = useStateDeferred(inProgress), localInProgress = _h[0], setLocalInProgress = _h[1], setLocalInProgressDeferred = _h[2];
4289
+ var _j = useStateDeferred(inProgress), localInProgress = _j[0], setLocalInProgress = _j[1], setLocalInProgressDeferred = _j[2];
4122
4290
  useEffect(function () {
4123
4291
  if (inProgress == lastInProgress)
4124
4292
  return;
4125
4293
  inProgress ? setLocalInProgress(true) : setLocalInProgressDeferred(false, minimumInProgressTimeMs);
4126
4294
  }, [inProgress, lastInProgress, setLocalInProgress, setLocalInProgressDeferred]);
4127
- return (jsxs(LuiButton, __assign({ "data-testid": dataTestId, type: "button", level: level, title: (_b = title !== null && title !== void 0 ? title : ariaLabel) !== null && _b !== void 0 ? _b : name, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : name, className: clsx("lui-button-icon", "ActionButton", className, localInProgress && "ActionButton-inProgress"), size: "lg", style: name == null ? { padding: "8px 5px" } : {}, onClick: function () { return __awaiter(void 0, void 0, void 0, function () {
4295
+ var buttonText = (jsxs("span", __assign({ className: "ActionButton-minimalArea" }, { children: [jsx("span", __assign({ className: "ActionButton-minimalAreaDisplay" }, { children: (_b = (localInProgress ? inProgressName : name)) !== null && _b !== void 0 ? _b : name })), jsx("span", __assign({ className: "ActionButton-minimalAreaExpand" }, { children: name }))] })));
4296
+ return (jsxs(LuiButton, __assign({ "data-testid": dataTestId, type: "button", level: level, title: (_c = title !== null && title !== void 0 ? title : ariaLabel) !== null && _c !== void 0 ? _c : name, "aria-label": ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : name, className: clsx("lui-button-icon-right", "ActionButton", className, localInProgress && "ActionButton-inProgress"), size: "lg", style: name == null ? { padding: "8px 5px" } : {}, onClick: function () { return __awaiter(void 0, void 0, void 0, function () {
4128
4297
  var promise, isPromise;
4129
4298
  return __generator(this, function (_a) {
4130
4299
  switch (_a.label) {
@@ -4146,13 +4315,19 @@ var ActionButton = function (_a) {
4146
4315
  case 3: return [2 /*return*/];
4147
4316
  }
4148
4317
  });
4149
- }); }, disabled: localInProgress }, { children: [localInProgress ? (jsx(LuiMiniSpinner, { size: 16, divProps: {
4318
+ }); }, disabled: localInProgress }, { children: [iconPosition === "right" && buttonText, localInProgress ? (jsx(LuiMiniSpinner, { size: 16, divProps: {
4150
4319
  "data-testid": "loading-spinner",
4151
- style: { margin: 0, paddingRight: 5, paddingLeft: 3, paddingBottom: 0, paddingTop: 0 },
4320
+ style: {
4321
+ margin: 0,
4322
+ paddingRight: 5,
4323
+ paddingLeft: 3,
4324
+ paddingBottom: 0,
4325
+ paddingTop: 0
4326
+ },
4152
4327
  role: "status",
4153
4328
  "aria-label": "Loading"
4154
- } })) : (jsx(LuiIcon, { name: icon, alt: (_c = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : name) !== null && _c !== void 0 ? _c : "", size: size })), jsxs("span", __assign({ className: "ActionButton-minimalArea" }, { children: [jsx("span", __assign({ className: "ActionButton-minimalAreaDisplay" }, { children: (_d = (localInProgress ? inProgressName : name)) !== null && _d !== void 0 ? _d : name })), jsx("span", __assign({ className: "ActionButton-minimalAreaExpand" }, { children: name }))] }))] })));
4329
+ } })) : (jsx(LuiIcon, { name: icon, alt: (_d = ariaLabel !== null && ariaLabel !== void 0 ? ariaLabel : name) !== null && _d !== void 0 ? _d : "", size: size, spanProps: { style: iconPosition === "right" ? { transform: "scaleX(-1)" } : {} } })), iconPosition === "left" && buttonText] })));
4155
4330
  };
4156
4331
 
4157
- export { ActionButton, ComponentLoadingWrapper, ControlledMenu, FocusableItem, GenericCellEditorComponentWrapper, GenericMultiEditCellClass, Grid, GridCell, GridCellRenderer, GridContext, GridContextProvider, GridFormDropDown, GridFormMultiSelect, GridFormPopoverMenu, GridFormSubComponentTextArea, GridFormSubComponentTextInput, GridHeaderSelect, GridIcon, GridLoadableCell, GridPopoutEditMultiSelect, GridPopoverContext, GridPopoverContextProvider, GridPopoverEditBearing, GridPopoverEditBearingCorrection, GridPopoverEditDropDown, GridPopoverMenu, GridPopoverMessage, GridPopoverTextArea, GridPopoverTextInput, GridRenderPopoutMenuCell, GridRendererGenericCell, GridSubComponentContext, GridUpdatingContext, GridUpdatingContextProvider, Menu, MenuButton, MenuDivider, MenuGroup, MenuHeader, MenuHeaderItem, MenuHeaderString, MenuItem, MenuRadioGroup, MenuSeparator, MenuSeparatorString, PopoutMenuSeparator, SubMenu, TextAreaInput, TextInputFormatted, bearingCorrectionValueFormatter, bearingNumberParser, bearingStringValidator, bearingValueFormatter, convertDDToDMS, hasParentClass, isFloat, isNotEmpty, useGridPopoverContext, useGridPopoverHook, useMenuState, usePostSortRowsHook, wait };
4332
+ export { ActionButton, ComponentLoadingWrapper, ControlledMenu, FocusableItem, GenericCellEditorComponentWrapper, GenericMultiEditCellClass, Grid, GridCell, GridCellRenderer, GridContext, GridContextProvider, GridFormDropDown, GridFormMultiSelect, GridFormPopoverMenu, GridFormSubComponentTextArea, GridFormSubComponentTextInput, GridHeaderSelect, GridIcon, GridLoadableCell, GridPopoutEditMultiSelect, GridPopoverContext, GridPopoverContextProvider, GridPopoverEditBearing, GridPopoverEditBearingCorrection, GridPopoverEditDropDown, GridPopoverMenu, GridPopoverMessage, GridPopoverTextArea, GridPopoverTextInput, GridRenderPopoutMenuCell, GridRendererGenericCell, GridSubComponentContext, GridUpdatingContext, GridUpdatingContextProvider, Menu, MenuButton, MenuDivider, MenuGroup, MenuHeader, MenuHeaderItem, MenuHeaderString, MenuItem, MenuRadioGroup, MenuSeparator, MenuSeparatorString, PopoutMenuSeparator, SubMenu, TextAreaInput, TextInputFormatted, bearingCorrectionValueFormatter, bearingNumberParser, bearingStringValidator, bearingValueFormatter, convertDDToDMS, hasParentClass, isFloat, isNotEmpty, stringByteLengthIsInvalid, useGridPopoverContext, useGridPopoverHook, useMenuState, usePostSortRowsHook, wait };
4158
4333
  //# sourceMappingURL=step-ag-grid.esm.js.map