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