@mackin.com/styleguide 11.0.11 → 11.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/index.d.ts +188 -162
  2. package/index.esm.js +1248 -1153
  3. package/index.js +1248 -1152
  4. package/package.json +1 -1
package/index.esm.js CHANGED
@@ -1242,6 +1242,128 @@ const styles = {
1242
1242
  }),
1243
1243
  };
1244
1244
 
1245
+ const defaultMinChars = 3;
1246
+ class AutocompleteController {
1247
+ constructor(getOptions, config) {
1248
+ var _a;
1249
+ this._value = undefined;
1250
+ this._options = [];
1251
+ this._minChars = (_a = config === null || config === void 0 ? void 0 : config.minChars) !== null && _a !== void 0 ? _a : defaultMinChars;
1252
+ if (config === null || config === void 0 ? void 0 : config.debounceMs) {
1253
+ this.getOptions = createDebouncedPromise(getOptions, config === null || config === void 0 ? void 0 : config.debounceMs);
1254
+ }
1255
+ else {
1256
+ this.getOptions = getOptions;
1257
+ }
1258
+ }
1259
+ get value() {
1260
+ return this._value;
1261
+ }
1262
+ get options() {
1263
+ return this._options;
1264
+ }
1265
+ async onChange(newValue) {
1266
+ // don't make getOptions calls if the value hasn't changed.
1267
+ if (newValue === this.value) {
1268
+ return;
1269
+ }
1270
+ // nullish should not make the getOptions call and instead clear everything.
1271
+ if (!newValue) {
1272
+ this._value = newValue;
1273
+ this._options = [];
1274
+ return;
1275
+ }
1276
+ // sub min chars should clear everything and not attempt the getOptions call.
1277
+ if (newValue.length < this._minChars) {
1278
+ this._value = newValue;
1279
+ this._options = [];
1280
+ return;
1281
+ }
1282
+ try {
1283
+ this._value = newValue;
1284
+ this._options = (await this.getOptions(newValue));
1285
+ }
1286
+ catch (err) {
1287
+ // this method will throw errors on debounce rejections. that is to be expected.
1288
+ // for actual getOptions exceptions, the owner of that function will need to handle errors.
1289
+ }
1290
+ }
1291
+ onPick(newValue) {
1292
+ this._value = newValue;
1293
+ this._options = [];
1294
+ }
1295
+ }
1296
+ const createDebouncedPromise = (originalFunction, trailingTimeoutMs) => {
1297
+ let timer;
1298
+ let onCancel;
1299
+ return (value) => {
1300
+ if (timer) {
1301
+ clearTimeout(timer);
1302
+ }
1303
+ if (onCancel) {
1304
+ onCancel('Promise cancelled due to in-progress debounce call.');
1305
+ onCancel = undefined;
1306
+ }
1307
+ return new Promise((res, rej) => {
1308
+ onCancel = rej;
1309
+ timer = setTimeout(() => {
1310
+ originalFunction(value)
1311
+ .then(values => {
1312
+ res(values);
1313
+ })
1314
+ .catch(err => {
1315
+ rej(err);
1316
+ })
1317
+ .finally(() => {
1318
+ clearTimeout(timer);
1319
+ });
1320
+ }, trailingTimeoutMs);
1321
+ });
1322
+ };
1323
+ };
1324
+
1325
+ /** Extracted logic around autocomplete functionality for Autocomplete.tsx that supports Entity (id/name) mapping. */
1326
+ class AutocompleteEntityController {
1327
+ constructor(getOptions, config) {
1328
+ this._options = [];
1329
+ const getStringOptions = async (value) => {
1330
+ this._options = await getOptions(value);
1331
+ return this._options.map(o => o.name);
1332
+ };
1333
+ this._ctrl = new AutocompleteController(getStringOptions, config);
1334
+ }
1335
+ get entity() {
1336
+ return this._pickedEntity;
1337
+ }
1338
+ get entities() {
1339
+ return this._options;
1340
+ }
1341
+ get value() {
1342
+ return this._ctrl.value;
1343
+ }
1344
+ get options() {
1345
+ return this._options.map(o => o.name);
1346
+ }
1347
+ async onChange(newValue) {
1348
+ const clearEntity = newValue !== this._ctrl.value;
1349
+ await this._ctrl.onChange(newValue);
1350
+ if (clearEntity) {
1351
+ this._pickedEntity = undefined;
1352
+ }
1353
+ this.trySyncCtrlOptions();
1354
+ }
1355
+ onPick(newValue) {
1356
+ this._ctrl.onPick(newValue);
1357
+ this._pickedEntity = this._options.find(o => o.name === this._ctrl.value);
1358
+ this.trySyncCtrlOptions();
1359
+ }
1360
+ trySyncCtrlOptions() {
1361
+ if (!this._ctrl.options.length) {
1362
+ this._options = [];
1363
+ }
1364
+ }
1365
+ }
1366
+
1245
1367
  /** Returns a UID. Use this instead of a direct call to a library. */
1246
1368
  function createUid() {
1247
1369
  return uniqueId();
@@ -1614,55 +1736,37 @@ const Checkbox = (props) => {
1614
1736
  finalCheckedColor = checkedColor;
1615
1737
  }
1616
1738
  }
1617
- const checkboxStyles = css `
1618
- display: inline-block;
1619
- ${!props.disabled && !readOnly && `
1620
- &:hover {
1621
- filter: ${theme.controls.hoverBrightness};
1622
- }
1623
- `}
1624
- `;
1625
- const labelStyles = css `
1626
- cursor: pointer;
1627
- user-select: none;
1628
- display: flex;
1629
- align-items: center;
1630
- ${props.disabled && `
1631
- cursor: not-allowed;
1632
- `}
1633
- ${readOnly && `
1634
- cursor: default;
1635
- `}
1636
- `;
1637
- // had to reference a marker class ('checkboxIcon') here for the sibling selector to work.
1638
- // opacity: 0 required for firefox. ignores the width of 0.
1639
- const nativeCheckboxStyles = css `
1640
- margin: 0;
1641
- padding: 0;
1642
- width: 0;
1643
- opacity: 0;
1644
-
1645
- ${!readOnly && `
1646
- &:focus + .checkboxIcon {
1647
- box-shadow: ${theme.controls.focusOutlineShadow};
1648
- }
1649
- `}
1650
- `;
1651
- const iconStyles = css `
1652
- ${!!label && !hideLabel && `
1653
- margin-right: 0.5rem;
1654
- `}
1655
- ${props.disabled && `
1656
- background-color: ${theme.colors.disabled};
1657
- cursor: not-allowed;
1658
- `}
1659
- ${readOnly && `
1660
- cursor: default;
1661
- `}
1662
- ${props.checked && `
1663
- color: ${finalCheckedColor}
1664
- `}
1665
- `;
1739
+ const checkboxStyles = css({
1740
+ display: 'inline-block',
1741
+ '&:hover': (!props.disabled && !readOnly) ? {
1742
+ filter: theme.controls.hoverBrightness
1743
+ } : undefined
1744
+ });
1745
+ const cursor = props.disabled ? 'not-allowed' : props.readOnly ? 'default' : 'pointer';
1746
+ const labelStyles = css({
1747
+ cursor,
1748
+ userSelect: 'none',
1749
+ display: 'flex',
1750
+ alignItems: 'center',
1751
+ position: 'relative'
1752
+ });
1753
+ const nativeCheckboxStyles = css({
1754
+ margin: 0,
1755
+ padding: 0,
1756
+ inset: 0,
1757
+ position: 'absolute',
1758
+ opacity: 0,
1759
+ cursor,
1760
+ '&:focus + .checkboxIcon': !readOnly ? {
1761
+ boxShadow: theme.controls.focusOutlineShadow
1762
+ } : undefined
1763
+ });
1764
+ const iconStyles = css({
1765
+ marginRight: (!!label && !hideLabel) ? '0.5rem' : undefined,
1766
+ backgroundColor: props.disabled ? theme.colors.disabled : undefined,
1767
+ cursor,
1768
+ color: props.checked ? finalCheckedColor : uncheckedIcon
1769
+ });
1666
1770
  return (React.createElement("span", { className: cx('checkbox', checkboxStyles, props.className) },
1667
1771
  React.createElement("label", { className: labelStyles },
1668
1772
  React.createElement("input", Object.assign({}, inputProps, { "aria-label": hideLabel ? label : undefined, tabIndex: readOnly ? -1 : (_a = props.tabIndex) !== null && _a !== void 0 ? _a : undefined, className: nativeCheckboxStyles, type: "checkbox", onChange: e => {
@@ -2978,132 +3082,30 @@ const Label = (props) => {
2978
3082
  return (React.createElement("label", Object.assign({}, labelProps, { htmlFor: htmlFor, className: cx('label', labelStyles, outerClass) }), content));
2979
3083
  };
2980
3084
 
2981
- const Nav = (props) => {
2982
- var _a, _b, _c, _d;
2983
- const nav = React.useRef(null);
2984
- const theme = useThemeSafely();
2985
- const navWidth = (_a = props.navWidth) !== null && _a !== void 0 ? _a : theme.layout.navWidth;
2986
- const totalNavOffset = `calc(${navWidth} + 20px)`;
2987
- const backdrop = useBackdropContext();
2988
- const log = useLogger(`Nav ${(_b = props.id) !== null && _b !== void 0 ? _b : '?'}`, (_c = props.__debug) !== null && _c !== void 0 ? _c : false);
2989
- const slideMs = (_d = props.slideMs) !== null && _d !== void 0 ? _d : theme.timings.nav.slideMs;
2990
- const slideRight = keyframes `
2991
- 0% {
2992
- transform: translateX(0);
2993
- }
2994
-
2995
- 100% {
2996
- transform: translateX(${totalNavOffset});
2997
- }
2998
- `;
2999
- const slideLeft = keyframes `
3000
- 0% {
3001
- transform: translateX(${totalNavOffset});
3002
- }
3003
-
3004
- 100% {
3005
- transform: translateX(0px);
3006
- }
3007
- `;
3008
- const classNavShowing = css `
3009
- animation: ${slideRight} ${slideMs}ms cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
3010
- `;
3011
- const classNavNotShowing = css `
3012
- animation: ${slideLeft} ${slideMs}ms cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
3013
- `;
3014
- const zIndex = theme.zIndexes.nav;
3015
- // the padding-top here is to offset the navs' content from the header. the shadow creeps over it.
3016
- const navStyles = css `
3017
- label: Nav;
3018
- position: fixed;
3019
- top: 0;
3020
- left: calc(${totalNavOffset} * -1);
3021
- bottom: 0;
3022
- background-color: ${theme.colors.nav};
3023
- color: ${theme.colors.navFont};
3024
- width: ${navWidth};
3025
- min-width: ${navWidth};
3026
- box-shadow: 4px 2px 12px 6px rgba(0, 0, 0, 0.2);
3027
- z-index: ${zIndex};
3028
- overflow-y: auto;
3029
- .omniLink, .omniLink:active, .omniLink:focus, .omniLink:visited {
3030
- color: ${theme.colors.navFont};
3031
- }
3032
- padding-top:0;
3033
- `;
3034
- React.useEffect(() => {
3035
- if (!backdrop.showing) {
3036
- props.toggle(false);
3037
- }
3038
- }, [backdrop.showing]);
3039
- useBooleanChanged((current, previous) => {
3040
- var _a;
3041
- log('show changed', `${previous !== null && previous !== void 0 ? previous : 'undefined'} > ${current}`);
3042
- backdrop.setShow(current, { key: (_a = props.id) !== null && _a !== void 0 ? _a : 'Nav', zIndex });
3043
- if (!props.show && props.onCloseFocusId) {
3044
- const focusId = props.onCloseFocusId;
3045
- setTimeout(() => {
3046
- var _a;
3047
- (_a = document.getElementById(focusId)) === null || _a === void 0 ? void 0 : _a.focus();
3048
- }, 0);
3049
- }
3050
- }, props.show);
3051
- React.useLayoutEffect(() => {
3052
- if (nav && nav.current) {
3053
- if (props.show) {
3054
- if (!nav.current.classList.contains(classNavShowing)) {
3055
- nav.current.classList.add(classNavShowing);
3056
- setTimeout(() => {
3057
- var _a;
3058
- (_a = document.getElementById(props.focusContentId)) === null || _a === void 0 ? void 0 : _a.focus();
3059
- }, slideMs + 1);
3060
- }
3061
- }
3062
- else {
3063
- if (nav.current.classList.contains(classNavShowing)) {
3064
- nav.current.classList.remove(classNavShowing);
3065
- nav.current.classList.add(classNavNotShowing);
3066
- setTimeout(() => {
3067
- if (nav && nav.current) {
3068
- nav.current.classList.remove(classNavNotShowing);
3069
- }
3070
- }, slideMs);
3071
- }
3072
- }
3073
- }
3074
- });
3075
- return (React.createElement("nav", { role: "dialog", "aria-modal": "true", "aria-label": props.ariaLabel, ref: nav, className: cx('nav', navStyles, props.className), onKeyDown: e => {
3076
- if (e.code === 'Escape') {
3077
- props.toggle(false);
3078
- }
3079
- } },
3080
- React.createElement(TabLocker, { className: css({ height: '100%' }) }, props.children)));
3081
- };
3082
-
3083
- const LinkContent = (props) => {
3084
- return (React.createElement(React.Fragment, null,
3085
- React.createElement("span", null,
3086
- props.leftIcon && React.createElement("span", { className: css({
3087
- label: 'LinkLeftIcon',
3088
- marginRight: '1rem',
3089
- display: 'inline-block',
3090
- minWidth: '1.5rem',
3091
- verticalAlign: 'middle'
3092
- }) }, props.leftIcon),
3093
- props.waiting ? React.createElement(Icon, { id: "waiting", spin: true }) : props.children),
3094
- props.rightIcon && React.createElement("span", { className: css({
3095
- label: 'LinkRightIcon',
3096
- marginLeft: '1rem',
3097
- verticalAlign: 'middle'
3098
- }) }, props.rightIcon)));
3099
- };
3100
-
3101
- const generateLinkStyles = (props, theme) => {
3102
- const disabled = props.disabled || props.waiting;
3103
- let color = props.colorOverride;
3104
- if (!color) {
3105
- if (!props.variant) {
3106
- color = theme.colors.link;
3085
+ const LinkContent = (props) => {
3086
+ return (React.createElement(React.Fragment, null,
3087
+ React.createElement("span", null,
3088
+ props.leftIcon && React.createElement("span", { className: css({
3089
+ label: 'LinkLeftIcon',
3090
+ marginRight: '1rem',
3091
+ display: 'inline-block',
3092
+ minWidth: '1.5rem',
3093
+ verticalAlign: 'middle'
3094
+ }) }, props.leftIcon),
3095
+ props.waiting ? React.createElement(Icon, { id: "waiting", spin: true }) : props.children),
3096
+ props.rightIcon && React.createElement("span", { className: css({
3097
+ label: 'LinkRightIcon',
3098
+ marginLeft: '1rem',
3099
+ verticalAlign: 'middle'
3100
+ }) }, props.rightIcon)));
3101
+ };
3102
+
3103
+ const generateLinkStyles = (props, theme) => {
3104
+ const disabled = props.disabled || props.waiting;
3105
+ let color = props.colorOverride;
3106
+ if (!color) {
3107
+ if (!props.variant) {
3108
+ color = theme.colors.link;
3107
3109
  }
3108
3110
  else {
3109
3111
  switch (props.variant) {
@@ -3337,6 +3339,29 @@ const generateLinkStyles = (props, theme) => {
3337
3339
  return linkStyles;
3338
3340
  };
3339
3341
 
3342
+ const Link = (props) => {
3343
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
3344
+ const { rightIcon, leftIcon, block, iconBlock, variant, round, small, colorOverride, children, disabled, ref, waiting } = props, linkProps = __rest(props, ["rightIcon", "leftIcon", "block", "iconBlock", "variant", "round", "small", "colorOverride", "children", "disabled", "ref", "waiting"]);
3345
+ const theme = useThemeSafely();
3346
+ const linkStyles = generateLinkStyles(props, theme);
3347
+ const mainClassName = cx('link', linkStyles, props.className);
3348
+ if (variant === 'text') {
3349
+ return React.createElement(Text, { className: mainClassName, tag: "div" }, props.children);
3350
+ }
3351
+ const isDisabled = props.disabled || props.waiting;
3352
+ return (React.createElement("a", Object.assign({}, linkProps, { tabIndex: disabled ? -1 : undefined, target: props.target, className: mainClassName, onClick: e => {
3353
+ var _a;
3354
+ if (isDisabled) {
3355
+ e.stopPropagation();
3356
+ e.preventDefault();
3357
+ }
3358
+ else {
3359
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, e);
3360
+ }
3361
+ } }),
3362
+ React.createElement(LinkContent, Object.assign({}, props))));
3363
+ };
3364
+
3340
3365
  const OmniLink = (props) => {
3341
3366
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
3342
3367
  const { noRouter, rightIcon, leftIcon, block, iconBlock, variant, round, small, colorOverride, children, ref, waiting } = props, linkProps = __rest(props, ["noRouter", "rightIcon", "leftIcon", "block", "iconBlock", "variant", "round", "small", "colorOverride", "children", "ref", "waiting"]);
@@ -3363,11 +3388,113 @@ const OmniLink = (props) => {
3363
3388
  } }), content));
3364
3389
  };
3365
3390
 
3391
+ const Nav = (props) => {
3392
+ var _a, _b, _c, _d;
3393
+ const nav = React.useRef(null);
3394
+ const theme = useThemeSafely();
3395
+ const navWidth = (_a = props.navWidth) !== null && _a !== void 0 ? _a : theme.layout.navWidth;
3396
+ const totalNavOffset = `calc(${navWidth} + 20px)`;
3397
+ const backdrop = useBackdropContext();
3398
+ const log = useLogger(`Nav ${(_b = props.id) !== null && _b !== void 0 ? _b : '?'}`, (_c = props.__debug) !== null && _c !== void 0 ? _c : false);
3399
+ const slideMs = (_d = props.slideMs) !== null && _d !== void 0 ? _d : theme.timings.nav.slideMs;
3400
+ const slideRight = keyframes `
3401
+ 0% {
3402
+ transform: translateX(0);
3403
+ }
3404
+
3405
+ 100% {
3406
+ transform: translateX(${totalNavOffset});
3407
+ }
3408
+ `;
3409
+ const slideLeft = keyframes `
3410
+ 0% {
3411
+ transform: translateX(${totalNavOffset});
3412
+ }
3413
+
3414
+ 100% {
3415
+ transform: translateX(0px);
3416
+ }
3417
+ `;
3418
+ const classNavShowing = css `
3419
+ animation: ${slideRight} ${slideMs}ms cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
3420
+ `;
3421
+ const classNavNotShowing = css `
3422
+ animation: ${slideLeft} ${slideMs}ms cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
3423
+ `;
3424
+ const zIndex = theme.zIndexes.nav;
3425
+ // the padding-top here is to offset the navs' content from the header. the shadow creeps over it.
3426
+ const navStyles = css `
3427
+ label: Nav;
3428
+ position: fixed;
3429
+ top: 0;
3430
+ left: calc(${totalNavOffset} * -1);
3431
+ bottom: 0;
3432
+ background-color: ${theme.colors.nav};
3433
+ color: ${theme.colors.navFont};
3434
+ width: ${navWidth};
3435
+ min-width: ${navWidth};
3436
+ box-shadow: 4px 2px 12px 6px rgba(0, 0, 0, 0.2);
3437
+ z-index: ${zIndex};
3438
+ overflow-y: auto;
3439
+ .omniLink, .omniLink:active, .omniLink:focus, .omniLink:visited {
3440
+ color: ${theme.colors.navFont};
3441
+ }
3442
+ padding-top:0;
3443
+ `;
3444
+ React.useEffect(() => {
3445
+ if (!backdrop.showing) {
3446
+ props.toggle(false);
3447
+ }
3448
+ }, [backdrop.showing]);
3449
+ useBooleanChanged((current, previous) => {
3450
+ var _a;
3451
+ log('show changed', `${previous !== null && previous !== void 0 ? previous : 'undefined'} > ${current}`);
3452
+ backdrop.setShow(current, { key: (_a = props.id) !== null && _a !== void 0 ? _a : 'Nav', zIndex });
3453
+ if (!props.show && props.onCloseFocusId) {
3454
+ const focusId = props.onCloseFocusId;
3455
+ setTimeout(() => {
3456
+ var _a;
3457
+ (_a = document.getElementById(focusId)) === null || _a === void 0 ? void 0 : _a.focus();
3458
+ }, 0);
3459
+ }
3460
+ }, props.show);
3461
+ React.useLayoutEffect(() => {
3462
+ if (nav && nav.current) {
3463
+ if (props.show) {
3464
+ if (!nav.current.classList.contains(classNavShowing)) {
3465
+ nav.current.classList.add(classNavShowing);
3466
+ setTimeout(() => {
3467
+ var _a;
3468
+ (_a = document.getElementById(props.focusContentId)) === null || _a === void 0 ? void 0 : _a.focus();
3469
+ }, slideMs + 1);
3470
+ }
3471
+ }
3472
+ else {
3473
+ if (nav.current.classList.contains(classNavShowing)) {
3474
+ nav.current.classList.remove(classNavShowing);
3475
+ nav.current.classList.add(classNavNotShowing);
3476
+ setTimeout(() => {
3477
+ if (nav && nav.current) {
3478
+ nav.current.classList.remove(classNavNotShowing);
3479
+ }
3480
+ }, slideMs);
3481
+ }
3482
+ }
3483
+ }
3484
+ });
3485
+ return (React.createElement("nav", { role: "dialog", "aria-modal": "true", "aria-label": props.ariaLabel, ref: nav, className: cx('nav', navStyles, props.className), onKeyDown: e => {
3486
+ if (e.code === 'Escape') {
3487
+ props.toggle(false);
3488
+ }
3489
+ } },
3490
+ React.createElement(TabLocker, { className: css({ height: '100%' }) }, props.children)));
3491
+ };
3492
+
3366
3493
  const roundPxPadding = '4px';
3367
3494
  const Picker = (props) => {
3368
- const { value, options, onValueChange, readOnly, round, controlAlign, wrapperClassName, iconClassName, error } = props, selectProps = __rest(props
3495
+ const { value, options, onValueChange, readOnly, round, controlAlign, wrapperClassName, iconClassName, error, optionClassName } = props, selectProps = __rest(props
3369
3496
  // if we put numbers in, we expect them out
3370
- , ["value", "options", "onValueChange", "readOnly", "round", "controlAlign", "wrapperClassName", "iconClassName", "error"]);
3497
+ , ["value", "options", "onValueChange", "readOnly", "round", "controlAlign", "wrapperClassName", "iconClassName", "error", "optionClassName"]);
3371
3498
  // if we put numbers in, we expect them out
3372
3499
  let isNumber = false;
3373
3500
  if (options && options.length) {
@@ -3381,6 +3508,7 @@ const Picker = (props) => {
3381
3508
  }
3382
3509
  const theme = useThemeSafely();
3383
3510
  const selectStyles = css({
3511
+ label: 'Picker',
3384
3512
  // fix the arrow so browser all look the same
3385
3513
  appearance: 'none',
3386
3514
  paddingLeft: theme.controls.padding,
@@ -3424,8 +3552,7 @@ const Picker = (props) => {
3424
3552
  const select = (React.createElement("select", Object.assign({}, selectProps, { tabIndex: readOnly ? -1 : selectProps.tabIndex, className: cx('picker', selectStyles, props.className), value: value, onKeyDown: e => {
3425
3553
  var _a;
3426
3554
  if (readOnly) {
3427
- if (e.keyCode === 9) {
3428
- //TAB
3555
+ if (e.code === 'Tab') {
3429
3556
  return;
3430
3557
  }
3431
3558
  e.preventDefault();
@@ -3466,7 +3593,7 @@ const Picker = (props) => {
3466
3593
  val = o;
3467
3594
  label = o;
3468
3595
  }
3469
- return React.createElement("option", { key: val, value: val }, label);
3596
+ return React.createElement("option", { key: val, value: val, className: optionClassName }, label);
3470
3597
  })));
3471
3598
  return (React.createElement("span", { className: cx(css({
3472
3599
  label: 'PickerWrapper',
@@ -3942,40 +4069,152 @@ class ItemPager {
3942
4069
  }
3943
4070
  }
3944
4071
 
3945
- const ProgressBar = (props) => {
3946
- var _a;
3947
- const bar = React.useRef(null);
3948
- React.useEffect(() => {
3949
- const pct = cleanPct(props.pct);
3950
- if (bar.current) {
3951
- bar.current.style.width = `${pct}%`;
4072
+ const MultiPicker = (props) => {
4073
+ const { value: values, options, onValueChange, readOnly, controlAlign, wrapperClassName, error, optionClassName } = props, selectProps = __rest(props
4074
+ // if we put numbers in, we expect them out
4075
+ , ["value", "options", "onValueChange", "readOnly", "controlAlign", "wrapperClassName", "error", "optionClassName"]);
4076
+ // if we put numbers in, we expect them out
4077
+ let isNumber = false;
4078
+ if (options && options.length) {
4079
+ const testOption = options[0];
4080
+ if (typeof testOption === 'object') {
4081
+ isNumber = typeof testOption.id === 'number';
3952
4082
  }
3953
- });
4083
+ else {
4084
+ isNumber = typeof testOption === 'number';
4085
+ }
4086
+ }
3954
4087
  const theme = useThemeSafely();
3955
- const barStyles = css `
3956
- width: 100%;
3957
- height: 1rem;
3958
- border: ${theme.controls.border};
3959
- background-color: ${theme.colors.progressBg};
3960
- ${props.round && `
3961
- border-radius: 1rem;
3962
- `}
3963
- `;
3964
- const fillStyles = css `
3965
- transition: width 0.5s ease-in-out;
3966
- height: 100%;
3967
- background-color: ${theme.colors.progressFill};
3968
- ${props.round && `
3969
- border-radius: 1rem;
3970
- `}
3971
- `;
3972
- const showPctPosition = (_a = props.showPctPosition) !== null && _a !== void 0 ? _a : 'bottom';
3973
- const wrapStyles = css({
3974
- label: 'ProgressBar',
3975
- display: 'flex',
3976
- flexDirection: showPctPosition === 'bottom' ? 'column' : 'row',
3977
- alignItems: showPctPosition === 'right' ? 'center' : undefined,
3978
- gap: showPctPosition === 'right' ? '0.5rem' : undefined,
4088
+ const selectStyles = css({
4089
+ label: 'MultiPicker',
4090
+ padding: theme.controls.padding,
4091
+ minHeight: theme.controls.height,
4092
+ backgroundColor: theme.colors.bg,
4093
+ color: theme.colors.font,
4094
+ fontSize: theme.controls.fontSize,
4095
+ width: '100%',
4096
+ border: theme.controls.border,
4097
+ borderRadius: theme.controls.borderRadius || 0, /* safari fix */
4098
+ transition: theme.controls.transition,
4099
+ '&:disabled': {
4100
+ color: theme.colors.font,
4101
+ opacity: 1,
4102
+ backgroundColor: theme.colors.disabled,
4103
+ cursor: 'not-allowed'
4104
+ },
4105
+ '&:focus': {
4106
+ outline: 'none',
4107
+ boxShadow: theme.controls.focusOutlineShadow
4108
+ }
4109
+ }, error && {
4110
+ borderColor: theme.colors.required,
4111
+ ':focus': {
4112
+ boxShadow: theme.controls.focusOutlineRequiredShadow
4113
+ }
4114
+ }, readOnly && {
4115
+ backgroundColor: 'transparent !important',
4116
+ backgroundImage: 'unset',
4117
+ border: 'none',
4118
+ '&:focus': {
4119
+ outline: 'none',
4120
+ boxShadow: 'none'
4121
+ }
4122
+ });
4123
+ const select = (React.createElement("select", Object.assign({}, selectProps, { multiple: true, tabIndex: readOnly ? -1 : selectProps.tabIndex, className: cx(selectStyles, props.className), value: values.map(v => v.toString()), onKeyDown: e => {
4124
+ var _a;
4125
+ if (readOnly) {
4126
+ if (e.code === 'Tab') {
4127
+ return;
4128
+ }
4129
+ e.preventDefault();
4130
+ e.stopPropagation();
4131
+ }
4132
+ else {
4133
+ (_a = selectProps.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(selectProps, e);
4134
+ }
4135
+ }, onMouseDown: e => {
4136
+ var _a;
4137
+ if (readOnly) {
4138
+ e.preventDefault();
4139
+ e.stopPropagation();
4140
+ }
4141
+ else {
4142
+ (_a = selectProps.onMouseDown) === null || _a === void 0 ? void 0 : _a.call(selectProps, e);
4143
+ }
4144
+ }, onChange: e => {
4145
+ var _a;
4146
+ const values = Array.from(e.target.selectedOptions).map(o => {
4147
+ let val = o.value;
4148
+ if (isNumber) {
4149
+ val = parseInt(val, 10);
4150
+ if (isNaN(val)) {
4151
+ val = '';
4152
+ }
4153
+ }
4154
+ return val;
4155
+ });
4156
+ onValueChange(values);
4157
+ (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
4158
+ } }), (options || []).map(o => {
4159
+ var _a;
4160
+ let val;
4161
+ let label;
4162
+ if (typeof o === 'object') {
4163
+ val = o.id;
4164
+ label = (_a = o.name) !== null && _a !== void 0 ? _a : o.id;
4165
+ }
4166
+ else {
4167
+ val = o;
4168
+ label = o;
4169
+ }
4170
+ return React.createElement("option", { key: val, value: val, className: optionClassName }, label);
4171
+ })));
4172
+ return (React.createElement("span", { className: cx(css({
4173
+ label: 'MultiPickerWrapper',
4174
+ position: 'relative',
4175
+ display: 'inline-block',
4176
+ width: '100%',
4177
+ }), wrapperClassName) },
4178
+ React.createElement("div", { className: css({
4179
+ position: 'relative'
4180
+ }) }, select),
4181
+ (error || controlAlign) && React.createElement(InputErrorDisplay, { error: error })));
4182
+ };
4183
+
4184
+ const ProgressBar = (props) => {
4185
+ var _a;
4186
+ const bar = React.useRef(null);
4187
+ React.useEffect(() => {
4188
+ const pct = cleanPct(props.pct);
4189
+ if (bar.current) {
4190
+ bar.current.style.width = `${pct}%`;
4191
+ }
4192
+ });
4193
+ const theme = useThemeSafely();
4194
+ const barStyles = css `
4195
+ width: 100%;
4196
+ height: 1rem;
4197
+ border: ${theme.controls.border};
4198
+ background-color: ${theme.colors.progressBg};
4199
+ ${props.round && `
4200
+ border-radius: 1rem;
4201
+ `}
4202
+ `;
4203
+ const fillStyles = css `
4204
+ transition: width 0.5s ease-in-out;
4205
+ height: 100%;
4206
+ background-color: ${theme.colors.progressFill};
4207
+ ${props.round && `
4208
+ border-radius: 1rem;
4209
+ `}
4210
+ `;
4211
+ const showPctPosition = (_a = props.showPctPosition) !== null && _a !== void 0 ? _a : 'bottom';
4212
+ const wrapStyles = css({
4213
+ label: 'ProgressBar',
4214
+ display: 'flex',
4215
+ flexDirection: showPctPosition === 'bottom' ? 'column' : 'row',
4216
+ alignItems: showPctPosition === 'right' ? 'center' : undefined,
4217
+ gap: showPctPosition === 'right' ? '0.5rem' : undefined,
3979
4218
  });
3980
4219
  const pctLabelStyles = (props.showPct && props.showPctPosition === 'right') ? css({
3981
4220
  width: '3rem'
@@ -4070,6 +4309,29 @@ const SearchBox = React.forwardRef((props, ref) => {
4070
4309
  return (React.createElement("div", { className: searchBoxWrapperStyles }, input));
4071
4310
  });
4072
4311
 
4312
+ /** Converts an enum to an array of entities with id and name. The enum can be an integer or string enum.*/
4313
+ const enumToEntities = (enumObj) => {
4314
+ const entities = [];
4315
+ for (const key in enumObj) {
4316
+ if (isNaN(parseInt(key, 10))) {
4317
+ entities.push({
4318
+ id: enumObj[key],
4319
+ name: key
4320
+ });
4321
+ }
4322
+ }
4323
+ return entities;
4324
+ };
4325
+
4326
+ /** Displays the value in American dollars. */
4327
+ const getCurrencyDisplay = (value, isCents, denomination = '$') => {
4328
+ let actualValue = value || 0;
4329
+ if (isCents) {
4330
+ actualValue /= 100;
4331
+ }
4332
+ return `${denomination}${actualValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
4333
+ };
4334
+
4073
4335
  const GlobalStyles = () => {
4074
4336
  const theme = useThemeSafely();
4075
4337
  injectGlobal({
@@ -4098,698 +4360,37 @@ const GlobalStyles = () => {
4098
4360
  return null;
4099
4361
  };
4100
4362
 
4101
- /*
4102
- From https://fireship.io/snippets/use-media-query-hook/.
4103
- Tried using https://www.npmjs.com/package/react-media, but it cause Webpack build issues.
4104
- */
4105
- /** React wrapper around window resizing and window.matchMedia.
4106
- * https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
4107
- */
4108
- const useMediaQuery = (query) => {
4109
- const [matches, setMatches] = useState(window.matchMedia(query).matches);
4110
- useEffect(() => {
4111
- const media = window.matchMedia(query);
4112
- if (media.matches !== matches) {
4113
- setMatches(media.matches);
4114
- }
4115
- const listener = () => setMatches(media.matches);
4116
- window.addEventListener("resize", listener);
4117
- return () => window.removeEventListener("resize", listener);
4118
- }, [matches, query]);
4119
- return matches;
4120
- };
4363
+ var decimal=["decimal","décimale"];var place=["lugar","lieu"];var places=["lugares","lieux"];var file=["archivo","fichier"];var selected=["seleccionado","choisi"];var Upload=["Subir","Télécharger"];var Clear=["Cerrar","Fermer"];var Showing=["Mostrando","Affichage de"];var of=["de","sur"];var results=["resultados","résultats"];var Page=["Página","Page"];var Limit=["Límite","Limite"];var Sort=["Ordenar","Trier"];var January=["Enero","Janvier"];var February=["Febrero","Février"];var March=["Marzo","Mars"];var April=["Abril","Avril"];var May=["Mayo","Mai"];var June=["Junio","Juin"];var July=["Julio","Juillet"];var August=["Agosto","Août"];var September=["Septiembre","Septembre"];var October=["Octubre","Octobre"];var November=["Noviembre","Novembre"];var December=["Diciembre","Décembre"];var Sun=["Dom","Dim"];var Mon=["Lun","Lun"];var Tue=["Mar","Mar"];var Wed=["Mié","Mer"];var Thu=["Jue","Jeu"];var Fri=["Vie","Ven"];var Sat=["Sáb","Sam"];var OK=["Aceptar","Accepter"];var Cancel=["Cancelar","Annuler"];var strings = {"Required.":["Requerido.","Requis."],"Must be at least":["Debe ser al menos","Doit être au moins"],"characters in length.":["caracteres de longitud.","caractères de longueur."],"Must be an integer.":["Debe ser un número entero.","Doit être un entier."],"Limited to":["Limitado a","Limité à"],decimal:decimal,place:place,places:places,"Must be greater than or equal to":["Debe ser mayor o igual a","Doit être supérieur ou égal à"],"Must be less than or equal to":["Debe ser menor o igual a","Doit être inférieur ou égal à"],"Invalid email.":["Correo electrónico no válido.","Email invalide."],"Invalid URL.":["URL no válida.","URL non valide."],"Click or drag and drop files.":["Haga clic o arrastre y suelte archivos.","Cliquez ou faites glisser et déposez les fichiers."],file:file,selected:selected,"Upload failed.":["Error en la carga.","Le téléchargement a échoué."],Upload:Upload,"Upload successful.":["Carga exitosa","Téléchargement réussi."],"Invalid files":["Archivos no válidos","Fichiers invalides"],"Max file size exceeded":["Se ha excedido el tamaño máximo de archivo","La taille maximale du fichier a été dépassée"],"Error":["Error","Erreur"],Clear:Clear,Showing:Showing,of:of,results:results,"No Results":["No Hay Resultados","Aucun Résultat"],Page:Page,Limit:Limit,Sort:Sort,January:January,February:February,March:March,April:April,May:May,June:June,July:July,August:August,September:September,October:October,November:November,December:December,Sun:Sun,Mon:Mon,Tue:Tue,Wed:Wed,Thu:Thu,Fri:Fri,Sat:Sat,OK:OK,Cancel:Cancel,"Copied!":["¡Copiado!","Copié!"],"Copy to clipboard":["Copiar al portapapeles","Copier dans le presse-papier"]};
4121
4364
 
4122
- const Slider = (p) => {
4365
+ const LocalizationProvider = (p) => {
4123
4366
  var _a;
4124
- const theme = useThemeSafely();
4125
- const sliderContainer = useRef(null);
4126
- const sliderHandleSize = (_a = p.sliderHandleSize) !== null && _a !== void 0 ? _a : theme.controls.height;
4127
- const height = p.showValue ? `calc(${sliderHandleSize} + 1.5rem)` : sliderHandleSize;
4128
- // we're keeping this value in a ref vs. state so the constant updates don't cause re-render.
4129
- // we will have to respond to outside value changes after the fact however...
4130
- const currentValue = useRef(p.value);
4131
- const [, forceUpdate] = useState(Date.now());
4132
- useEffect(() => {
4133
- if (p.value !== currentValue.current) {
4134
- currentValue.current = p.value;
4135
- forceUpdate(Date.now());
4136
- }
4137
- }, [p.value]);
4138
- return (React__default.createElement("div", { ref: sliderContainer, className: cx(css({
4139
- label: 'Slider',
4140
- width: '100%',
4141
- height,
4142
- }), p.className) },
4143
- React__default.createElement(ReactSlider, { ariaLabel: p.ariaLabel, step: p.step, min: p.min, max: p.max, value: p.value, onAfterChange: (value) => {
4144
- p.onChange(value);
4145
- }, onChange: p.onUpdate || p.showValue ? (value) => {
4146
- var _a;
4147
- currentValue.current = value;
4148
- (_a = p.onUpdate) === null || _a === void 0 ? void 0 : _a.call(p, value);
4149
- } : undefined, renderTrack: (props) => {
4150
- const { className } = props, rest = __rest(props, ["className"]);
4151
- return (React__default.createElement("div", Object.assign({ className: cx(className, p.trackClassName, css({
4152
- display: 'flex',
4153
- alignItems: 'center',
4154
- height: sliderHandleSize
4155
- })) }, rest),
4156
- React__default.createElement("div", { className: css({
4157
- backgroundColor: theme.colors.secondary,
4158
- height: `calc(${sliderHandleSize} / 4)`,
4159
- borderRadius: theme.controls.roundRadius,
4160
- width: '100%'
4161
- }, p.innerTrackClassName && rest.key === 'track-1' && Array.isArray(p.value) ? p.innerTrackClassName : undefined) })));
4162
- }, renderThumb: (props, state) => {
4163
- const { className } = props, rest = __rest(props, ["className"]);
4164
- let specificThumbStyles;
4165
- if (p.handle1ClassName && props.key === 'thumb-0') {
4166
- specificThumbStyles = p.handle1ClassName;
4367
+ const log = useLogger('LocalizationProvider', (_a = p.__debug) !== null && _a !== void 0 ? _a : false);
4368
+ const stringsDb = strings;
4369
+ return (React__default.createElement(LocalizationContext.Provider, { value: {
4370
+ language: p.language,
4371
+ getText: (text) => {
4372
+ if (!text) {
4373
+ return '';
4167
4374
  }
4168
- else if (p.handle2ClassName && props.key === 'thumb-1') {
4169
- specificThumbStyles = p.handle2ClassName;
4375
+ if (!isNaN(parseInt(text))) {
4376
+ return text;
4170
4377
  }
4171
- return (React__default.createElement("div", Object.assign({ className: cx(className, css({
4172
- borderRadius: theme.controls.roundRadius,
4173
- backgroundColor: 'white',
4174
- border: theme.controls.border,
4175
- cursor: 'grab',
4176
- boxShadow: theme.controls.buttonBoxShadow,
4177
- transition: theme.controls.transition,
4178
- '&:focus': {
4179
- outline: 'none',
4180
- boxShadow: theme.controls.focusOutlineShadow
4181
- },
4182
- '&:active': {
4183
- boxShadow: 'none',
4184
- cursor: 'grabbing'
4185
- },
4186
- '&:hover': {
4187
- filter: theme.controls.hoverBrightness
4188
- }
4189
- }), specificThumbStyles, p.handleClassName, css({
4190
- width: sliderHandleSize,
4191
- height: sliderHandleSize,
4192
- })) }, rest, { tabIndex: p.tabIndex }), p.showValue && (React__default.createElement(HandleText, { sliderHandleSize: sliderHandleSize, className: p.sliderTextClassName, index: state.index, parentElement: sliderContainer.current, value: Array.isArray(currentValue.current) ? currentValue.current[state.index] : currentValue.current, renderValue: p.renderValue, renderValueWidth: p.renderValueWidth }))));
4193
- } })));
4194
- };
4195
- const rectsCollideX = (r1, r2) => {
4196
- if (r1.left >= r2.left && r1.left <= r2.right) {
4197
- return true;
4198
- }
4199
- if (r1.right >= r2.left && r1.right <= r2.right) {
4200
- return true;
4201
- }
4202
- return false;
4203
- };
4204
- const HandleText = (p) => {
4205
- var _a, _b, _c;
4206
- const displayValue = (_b = (_a = p.renderValue) === null || _a === void 0 ? void 0 : _a.call(p, p.value)) !== null && _b !== void 0 ? _b : p.value;
4207
- const renderValueWidth = (_c = p.renderValueWidth) !== null && _c !== void 0 ? _c : p.sliderHandleSize;
4208
- const renderValueLeft = useMemo(() => {
4209
- return `calc(${renderValueWidth} * 0.5 * -1 + (${p.sliderHandleSize} * 0.5))`;
4210
- }, [p.renderValueWidth, p.sliderHandleSize]);
4211
- const [flipText, setFlipText] = useState(false);
4212
- const offset = '2px';
4213
- useEffect(() => {
4214
- // this needs to fire on every render due to the other handle also moving.
4215
- var _a, _b;
4216
- if (p.index === 1) {
4217
- // only do this for the max/right-most handle
4218
- const [r1, r2] = Array.from((_b = (_a = p.parentElement) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.slider-handle').values()) !== null && _b !== void 0 ? _b : []).map(e => e.getBoundingClientRect());
4219
- if (r1 && r2) {
4220
- setFlipText(rectsCollideX(r1, r2));
4378
+ const wordArray = stringsDb[text];
4379
+ if (!wordArray) {
4380
+ log(`No localization data exists for "${text}".`);
4381
+ }
4382
+ if (p.language === StyleGuideLanguage.English) {
4383
+ return text;
4384
+ }
4385
+ const languageIndex = p.language - 1;
4386
+ const translation = wordArray === null || wordArray === void 0 ? void 0 : wordArray[languageIndex];
4387
+ if (translation) {
4388
+ return translation;
4389
+ }
4390
+ log(`No ${StyleGuideLanguage[p.language]} translations exist for "${text}".`);
4391
+ return text;
4221
4392
  }
4222
- }
4223
- });
4224
- return (React__default.createElement(Text, { ellipsis: true, className: cx('slider-handle', css({
4225
- width: renderValueWidth,
4226
- left: renderValueLeft,
4227
- top: flipText ? undefined : `calc(${p.sliderHandleSize} + ${offset})`,
4228
- bottom: flipText ? `calc(${p.sliderHandleSize} + ${offset})` : undefined,
4229
- position: 'absolute',
4230
- overflow: 'hidden',
4231
- }), p.className), tag: "div", align: "center" }, displayValue));
4232
- };
4233
-
4234
- const TabHeader = (p) => {
4235
- var _a, _b;
4236
- const [tabIndex, setTabIndex] = React.useState((_a = p.startingIndex) !== null && _a !== void 0 ? _a : 0);
4237
- const [tabsChanging, setTabsChanging] = React.useState(false);
4238
- const theme = useThemeSafely();
4239
- const variant = (_b = p.variant) !== null && _b !== void 0 ? _b : 'tab';
4240
- const menuStyles = css({
4241
- display: 'flex',
4242
- gap: theme.controls.gap,
4243
- listStyleType: 'none',
4244
- margin: 0,
4245
- padding: 0,
4246
- flexWrap: variant === 'button' ? 'wrap' : 'nowrap'
4247
- }, variant === 'button' && {
4248
- borderBottom: theme.controls.border,
4249
- paddingBottom: '1rem'
4250
- });
4251
- function onTabSelect(index, tabId, focusAfter) {
4252
- const onChange = () => {
4253
- var _a;
4254
- setTabIndex(index);
4255
- (_a = p.onTabChanged) === null || _a === void 0 ? void 0 : _a.call(p, index);
4256
- if (focusAfter) {
4257
- setTimeout(() => {
4258
- var _a;
4259
- (_a = document.getElementById(tabId)) === null || _a === void 0 ? void 0 : _a.focus();
4260
- }, 0);
4261
- }
4262
- };
4263
- if (p.onBeforeTabChanged) {
4264
- setTabsChanging(true);
4265
- p.onBeforeTabChanged(index)
4266
- .then(() => {
4267
- onChange();
4268
- })
4269
- .catch(() => {
4270
- /* do nothing */
4271
- })
4272
- .finally(() => {
4273
- setTabsChanging(false);
4274
- });
4275
- }
4276
- else {
4277
- onChange();
4278
- }
4279
- }
4280
- return (React.createElement("div", { className: "tabHeader" },
4281
- React.createElement("ul", { role: 'tablist', "aria-label": p.ariaLabel, className: cx(menuStyles, p.containerClassName) }, p.tabs.map((tab, index) => {
4282
- var _a, _b;
4283
- const active = index === tabIndex;
4284
- let tabStyles;
4285
- let buttonStyles;
4286
- let buttonVariant;
4287
- if (variant === 'tab') {
4288
- tabStyles = css({
4289
- paddingLeft: '1rem',
4290
- paddingRight: '1rem',
4291
- backgroundColor: theme.colors.bg,
4292
- zIndex: 1,
4293
- }, active && {
4294
- border: theme.controls.border,
4295
- borderRadius: theme.controls.borderRadius,
4296
- borderBottomLeftRadius: 0,
4297
- borderBottomRightRadius: 0,
4298
- borderBottom: 'none',
4299
- zIndex: 3,
4300
- });
4301
- buttonVariant = 'link';
4302
- buttonStyles = css({
4303
- maxWidth: (_a = p.maxTabWidth) !== null && _a !== void 0 ? _a : '10rem',
4304
- overflow: 'hidden'
4305
- });
4306
- }
4307
- else {
4308
- buttonVariant = active ? 'primary' : undefined;
4309
- buttonStyles = css({
4310
- paddingLeft: '1rem',
4311
- paddingRight: '1rem',
4312
- maxWidth: (_b = p.maxTabWidth) !== null && _b !== void 0 ? _b : '10rem',
4313
- overflow: 'hidden',
4314
- });
4315
- }
4316
- let title = tab.title;
4317
- let buttonContent;
4318
- if (typeof tab.name === 'string') {
4319
- title !== null && title !== void 0 ? title : (title = tab.name);
4320
- buttonContent = React.createElement(Text, { tag: "div", align: "center", ellipsis: true }, tab.name);
4321
- }
4322
- else {
4323
- buttonContent = tab.name;
4324
- }
4325
- const tabId = getTabHeaderTabId(p.id, index);
4326
- return (React.createElement("li", { key: index, className: cx(tabStyles, p.tabClassName) },
4327
- React.createElement(Button, { id: tabId, tabIndex: active ? 0 : -1, role: "tab", "aria-controls": p.ariaControlsId, "aria-selected": active, disabled: tabsChanging, className: buttonStyles, variant: buttonVariant, title: title, readOnly: active, onClick: () => {
4328
- onTabSelect(index, tabId, false);
4329
- }, onKeyDown: e => {
4330
- e.stopPropagation();
4331
- let newIndex = index;
4332
- if (e.code === 'ArrowLeft') {
4333
- if (index === 0) {
4334
- newIndex = p.tabs.length - 1;
4335
- }
4336
- else {
4337
- newIndex = index - 1;
4338
- }
4339
- }
4340
- else if (e.code === 'ArrowRight') {
4341
- if (index === p.tabs.length - 1) {
4342
- newIndex = 0;
4343
- }
4344
- else {
4345
- newIndex = index + 1;
4346
- }
4347
- }
4348
- if (newIndex !== index) {
4349
- onTabSelect(newIndex, getTabHeaderTabId(p.id, newIndex), true);
4350
- }
4351
- } }, buttonContent)));
4352
- })),
4353
- variant === 'tab' && (React.createElement("div", { className: cx(css({
4354
- label: 'TabHeaderDivider',
4355
- borderBottom: theme.controls.border,
4356
- marginTop: `calc(${theme.controls.borderWidth} * -1)`,
4357
- zIndex: 2,
4358
- position: 'relative'
4359
- }), p.tabHeaderDividerClassName) }))));
4360
- };
4361
- function getTabHeaderTabId(tabHeaderId, tabIndex) {
4362
- return `${tabHeaderId}_tab_${tabIndex}`;
4363
- }
4364
-
4365
- const Table = (props) => {
4366
- const theme = useThemeSafely();
4367
- const tableStyles = css `
4368
- label: Table;
4369
- width: 100%;
4370
- border-collapse: collapse;
4371
- ${props.noCellBorder && `
4372
- .table__td {
4373
- border-left: none;
4374
- border-right: none;
4375
- }
4376
- `}
4377
- ${props.altRows && `
4378
- .table__tr:nth-of-type(even) {
4379
- background-color: ${theme.colors.lightBg};
4380
- }
4381
- `}
4382
- `;
4383
- const wrapperStyles = css `
4384
- label: TableScrollWrapper;
4385
- width:100%;
4386
- overflow-y: auto;
4387
- padding:0 1px; //fixes always showing of the table scroller
4388
- `;
4389
- return (React.createElement("div", { className: cx(wrapperStyles, props.wrapperClassName) },
4390
- React.createElement("table", { className: cx(tableStyles, props.className) },
4391
- props.caption && React.createElement("caption", { className: css({
4392
- fontWeight: 'bold',
4393
- padding: theme.controls.padding
4394
- }) }, props.caption),
4395
- props.children)));
4396
- };
4397
- const Tr = (props) => {
4398
- return (React.createElement("tr", { className: cx('table__tr', props.className) }, props.children));
4399
- };
4400
- const Th = (props) => {
4401
- var _a;
4402
- let style = props.style;
4403
- if (props.width) {
4404
- if (style) {
4405
- style = Object.assign(Object.assign({}, style), { width: props.width, minWidth: props.width });
4406
- }
4407
- else {
4408
- style = { width: props.width, minWidth: props.width };
4409
- }
4410
- }
4411
- const theme = useThemeSafely();
4412
- const thStyles = css `
4413
- border-bottom: ${theme.controls.border};
4414
- padding: ${theme.controls.padding};
4415
- font-weight: bold;
4416
- text-align: ${(_a = props.align) !== null && _a !== void 0 ? _a : 'center'};
4417
- > .button {
4418
- font-weight: bold;
4419
- }
4420
- `;
4421
- return (React.createElement("th", { className: cx(thStyles, props.className), style: style }, props.children));
4422
- };
4423
- const Td = (props) => {
4424
- var _a;
4425
- const theme = useThemeSafely();
4426
- const tdStyles = css `
4427
- border: ${theme.controls.border};
4428
- padding: ${theme.controls.padding};
4429
- vertical-align: middle;
4430
- text-align: ${(_a = props.align) !== null && _a !== void 0 ? _a : 'center'};
4431
- `;
4432
- return (React.createElement("td", { colSpan: props.colSpan, style: props.style, className: cx('table__td', tdStyles, props.className) }, props.children));
4433
- };
4434
-
4435
- const TdCurrency = (props) => {
4436
- let actualValue = props.value || 0;
4437
- if (props.cents) {
4438
- actualValue = actualValue / 100;
4439
- }
4440
- const displayValue = actualValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
4441
- return (React.createElement(Td, { align: "right" },
4442
- "$",
4443
- displayValue));
4444
- };
4445
-
4446
- const TdNumber = (props) => {
4447
- return React.createElement(Td, { align: "right" }, props.value || props.value === 0 ? props.value.toLocaleString() : '');
4448
- };
4449
-
4450
- const ThSort = (props) => {
4451
- let iconId = '';
4452
- if (props.direction) {
4453
- if (props.direction === 'asc') {
4454
- iconId = 'sortAsc';
4455
- }
4456
- else {
4457
- iconId = 'sortDesc';
4458
- }
4459
- }
4460
- let rightContentSpecialJustify = 'center';
4461
- switch (props.align) {
4462
- case 'left':
4463
- rightContentSpecialJustify = 'flex-start';
4464
- break;
4465
- case 'right':
4466
- rightContentSpecialJustify = 'flex-end';
4467
- break;
4468
- }
4469
- return (React.createElement(Th, { align: props.align, style: props.style, width: props.width },
4470
- React.createElement("div", { className: props.rightContent && css({
4471
- display: 'flex',
4472
- alignItems: 'center',
4473
- justifyContent: rightContentSpecialJustify,
4474
- gap: '0.5rem'
4475
- }) },
4476
- React.createElement(Button, { onClick: props.onClick, variant: "link" },
4477
- props.text,
4478
- iconId && React.createElement(Icon, { className: css({
4479
- marginLeft: '0.5rem'
4480
- }), id: iconId })),
4481
- props.rightContent)));
4482
- };
4483
-
4484
- const defaultMaxLength = 200;
4485
- const defaultRows = 10;
4486
- const TextArea = React.forwardRef((props, ref) => {
4487
- var _a, _b;
4488
- const [localValue, setLocalValue] = React.useState(props.value);
4489
- const inputRef = (ref !== null && ref !== void 0 ? ref : React.useRef(null));
4490
- const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, props);
4491
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
4492
- const { onValueChange, customError, reportValueOnError, showErrorDisplay, allowUpdateOnFocus } = props, nativeProps = __rest(props, ["onValueChange", "customError", "reportValueOnError", "showErrorDisplay", "allowUpdateOnFocus"]);
4493
- const theme = useThemeSafely();
4494
- React.useEffect(() => {
4495
- updateErrorMessage();
4496
- }, []);
4497
- useIgnoreMount(() => {
4498
- var _a;
4499
- if ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.checkValidity()) {
4500
- onValueChange(localValue);
4501
- }
4502
- else {
4503
- if (reportValueOnError) {
4504
- onValueChange(localValue);
4505
- }
4506
- else {
4507
- onValueChange(undefined);
4508
- }
4509
- }
4510
- updateErrorMessage();
4511
- }, [localValue]);
4512
- useIgnoreMount(() => {
4513
- if (allowUpdateOnFocus || document.activeElement !== inputRef.current) {
4514
- setLocalValue(props.value);
4515
- }
4516
- updateErrorMessage();
4517
- }, [props.value]);
4518
- const styles = css({
4519
- backgroundColor: theme.colors.bg,
4520
- maxWidth: '100%',
4521
- minHeight: theme.controls.height,
4522
- fontFamily: theme.fonts.family,
4523
- fontSize: theme.fonts.size,
4524
- width: '100%',
4525
- border: theme.controls.border,
4526
- borderRadius: theme.controls.borderRadius,
4527
- color: theme.colors.font,
4528
- paddingTop: '0.75rem',
4529
- paddingLeft: theme.controls.padding,
4530
- paddingRight: theme.controls.padding,
4531
- height: 'auto',
4532
- transition: theme.controls.transition,
4533
- ':focus': {
4534
- outline: 'none',
4535
- boxShadow: theme.controls.focusOutlineShadow
4536
- },
4537
- ':disabled': {
4538
- backgroundColor: theme.colors.disabled,
4539
- cursor: 'not-allowed'
4540
- },
4541
- ':invalid': {
4542
- borderColor: theme.colors.required,
4543
- ':focus': {
4544
- boxShadow: theme.controls.focusOutlineRequiredShadow
4545
- }
4546
- },
4547
- }, props.readOnly && {
4548
- backgroundColor: 'transparent',
4549
- cursor: 'default',
4550
- border: 'none',
4551
- ':focus': {
4552
- outline: 'none',
4553
- boxShadow: 'none'
4554
- }
4555
- });
4556
- return (React.createElement("span", { className: css({
4557
- display: 'inline-block',
4558
- width: '100%'
4559
- }) },
4560
- React.createElement("textarea", Object.assign({}, nativeProps, { className: cx(styles, props.className), autoComplete: (_a = props.autoComplete) !== null && _a !== void 0 ? _a : 'off', tabIndex: props.readOnly ? -1 : props.tabIndex, maxLength: props.maxLength || defaultMaxLength, rows: (_b = props.rows) !== null && _b !== void 0 ? _b : defaultRows, ref: inputRef, value: localValue !== null && localValue !== void 0 ? localValue : '', onChange: e => {
4561
- var _a;
4562
- setLocalValue(e.target.value || undefined);
4563
- (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
4564
- }, onBlur: e => {
4565
- var _a;
4566
- if (!props.noTrim) {
4567
- setLocalValue(currentValue => {
4568
- return currentValue === null || currentValue === void 0 ? void 0 : currentValue.trim();
4569
- });
4570
- }
4571
- (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
4572
- } })),
4573
- (showErrorDisplay !== null && showErrorDisplay !== void 0 ? showErrorDisplay : true) && React.createElement(InputErrorDisplay, { error: validationError })));
4574
- });
4575
-
4576
- const ToggleButton = React.forwardRef((props, ref) => {
4577
- const { checked, checkedChildren, uncheckedChildren, checkedVariant, checkedClassName, checkedStyle, checkedIcon, uncheckedIcon } = props, buttonProps = __rest(props, ["checked", "checkedChildren", "uncheckedChildren", "checkedVariant", "checkedClassName", "checkedStyle", "checkedIcon", "uncheckedIcon"]);
4578
- let children;
4579
- if (checked) {
4580
- children = checkedChildren;
4581
- }
4582
- else {
4583
- children = uncheckedChildren;
4584
- }
4585
- return (React.createElement(Button, Object.assign({}, buttonProps, { ref: ref, className: cx('toggleButton', checked && 'toggleButton--checked', props.className, checked && checkedClassName), rightIcon: checked ? checkedIcon : uncheckedIcon, variant: checked ? checkedVariant : props.variant, style: checked ? checkedStyle : props.style }), children));
4586
- });
4587
-
4588
- const ToggleButtonGroup = (props) => {
4589
- const theme = useThemeSafely();
4590
- const groupStyles = css `
4591
- display: flex;
4592
- box-shadow: ${theme.controls.buttonBoxShadow};
4593
- border-radius: ${theme.controls.borderRadius};
4594
- ${props.round && `
4595
- border-radius: ${theme.controls.roundRadius};
4596
- `}
4597
- `;
4598
- const buttonStyles = css `
4599
- flex-grow:1;
4600
- box-shadow: none;
4601
- &:nth-of-type(1n+2){
4602
- margin-left: -1px;
4603
- }
4604
- border-radius: 0;
4605
- &:first-of-type{
4606
- border-top-left-radius: ${theme.controls.borderRadius};
4607
- border-bottom-left-radius: ${theme.controls.borderRadius};
4608
- }
4609
- &:last-child {
4610
- border-top-right-radius: ${theme.controls.borderRadius};
4611
- border-bottom-right-radius: ${theme.controls.borderRadius};
4612
- }
4613
- ${props.round && `
4614
- &:first-of-type{
4615
- border-top-left-radius: ${theme.controls.roundRadius};
4616
- border-bottom-left-radius: ${theme.controls.roundRadius};
4617
- padding-left: 1rem;
4618
- }
4619
- &:last-child {
4620
- border-top-right-radius: ${theme.controls.roundRadius};
4621
- border-bottom-right-radius: ${theme.controls.roundRadius};
4622
- padding-right: 1rem;
4623
- }
4624
- `}
4625
- `;
4626
- return (React.createElement("div", { className: cx('toggleButtonGroup', groupStyles, props.className) }, props.options.map(o => {
4627
- const active = o.id === props.value;
4628
- return React.createElement(Button, { style: props.width ? { width: props.width } : undefined, small: props.small, rightIcon: o.rightIcon, key: o.id, tabIndex: active ? -1 : 0, className: cx(css `
4629
- ${buttonStyles}
4630
- ${active && `
4631
- background-color: ${theme.colors.font};
4632
- color: ${theme.colors.bg};
4633
- cursor: default;
4634
- &:hover:not(:disabled) {
4635
- filter: none;
4636
- }
4637
- &:focus {
4638
- outline: none;
4639
- box-shadow: none;
4640
- }
4641
- `}
4642
- `, active ? o.activeClass : undefined), disabled: props.disabled, enforceMinWidth: props.enforceMinWidth, onClick: () => {
4643
- if (active) {
4644
- return;
4645
- }
4646
- props.onChange(o.id);
4647
- } }, o.name);
4648
- })));
4649
- };
4650
-
4651
- const TogglePasswordInput = React.forwardRef((props, ref) => {
4652
- const { onVisibilityChanged } = props, inputProps = __rest(props, ["onVisibilityChanged"]);
4653
- const [show, setShow] = React.useState(false);
4654
- useIgnoreMount(() => {
4655
- onVisibilityChanged === null || onVisibilityChanged === void 0 ? void 0 : onVisibilityChanged(show);
4656
- }, [show]);
4657
- return (React.createElement(TextInput, Object.assign({}, inputProps, { ref: ref, type: show ? 'text' : 'password', rightControl: (React.createElement(Button, { small: true, style: {
4658
- // small button is required here due to the icon pushing outside the boundries of the
4659
- // parent textbox. increasing the font size here to fill the small button.
4660
- fontSize: '1rem'
4661
- }, variant: "icon", onClick: () => {
4662
- setShow(previous => !previous);
4663
- } },
4664
- React.createElement(Icon, { id: show ? 'show' : 'hide' }))) })));
4665
- });
4666
-
4667
- let clearTimerId = undefined;
4668
- /** Allows for status notificaiton methods for screen readers.
4669
- * This hook does not have any dependencies, so it can be used in projects that don't important anything else from the style_guide. */
4670
- function useAriaLiveRegion() {
4671
- const id = 'MknAriaLiveRegion';
4672
- if (!document.getElementById(id)) {
4673
- const div = document.createElement('div');
4674
- div.id = id;
4675
- // different sources cannot decide if this is needed.
4676
- // "Can work for status messages, but aria-live="polite" + text is usually simpler and more reliable for loading."
4677
- div.role = 'status';
4678
- div.ariaLive = 'polite';
4679
- div.ariaAtomic = 'true';
4680
- div.style.position = 'absolute';
4681
- div.style.width = '1px';
4682
- div.style.height = '1px';
4683
- div.style.padding = '0px';
4684
- div.style.margin = '-1px';
4685
- div.style.overflow = 'hidden';
4686
- div.style.whiteSpace = 'nowrap';
4687
- document.body.prepend(div);
4688
- }
4689
- const clearNotification = () => {
4690
- if (clearTimerId) {
4691
- clearTimeout(clearTimerId);
4692
- }
4693
- const element = document.getElementById(id);
4694
- if (!element) {
4695
- return;
4696
- }
4697
- element.textContent = '';
4698
- };
4699
- return {
4700
- /**
4701
- * @param message - The text to be read by the screen reader.
4702
- * @param clearTimeoutMs - Milliseconds to wait before the message is cleared. Defaults to `2000`.
4703
- */
4704
- notify: (message, clearTimoutMs) => {
4705
- if (clearTimerId) {
4706
- clearTimeout(clearTimerId);
4707
- }
4708
- const element = document.getElementById(id);
4709
- if (!element) {
4710
- return;
4711
- }
4712
- element.textContent = message;
4713
- clearTimerId = setTimeout(() => {
4714
- // this is considered a good practice. if you leave text here, some screen readers will read it out randomly as you navigate around the page.
4715
- clearNotification();
4716
- }, clearTimoutMs !== null && clearTimoutMs !== void 0 ? clearTimoutMs : 2000);
4717
- },
4718
- clearNotification
4719
- };
4720
- }
4721
-
4722
- const WaitingIndicator = (p) => {
4723
- var _a, _b, _c;
4724
- const [show, setShow] = useState(p.show);
4725
- const hideTimer = useRef(0);
4726
- const lastShowStatus = useRef(false);
4727
- const log = useLogger(`WaitingIndicator ${(_a = p.id) !== null && _a !== void 0 ? _a : '?'}`, (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
4728
- const { notify, clearNotification } = useAriaLiveRegion();
4729
- if (p.__debug) {
4730
- useEffect(() => {
4731
- log('mounted');
4732
- return () => {
4733
- log('unmounted');
4734
- };
4735
- }, []);
4736
- }
4737
- useEffect(() => {
4738
- log('show changed', p.show);
4739
- // we need to store the 'last props' since props.show will be captured locally and the incorrect
4740
- // value will display in the timeout below.
4741
- log('storing lastShowStatus', p.show);
4742
- lastShowStatus.current = p.show;
4743
- if (p.show) {
4744
- log('setShow', true);
4745
- setShow(true);
4746
- if (p.minShowTimeMs) {
4747
- log('staring hideTimer', 'timout in ms:', p.minShowTimeMs);
4748
- hideTimer.current = window.setTimeout(() => {
4749
- log('hideTimer complete', 'clearing hideTimer');
4750
- window.clearTimeout(hideTimer.current);
4751
- hideTimer.current = 0;
4752
- // this check is necessary since the show status may have updated again to true.
4753
- // if so, ignore this timeout since we're already past the min time and we're still
4754
- // showing the component.
4755
- if (!lastShowStatus.current) {
4756
- log('setShow', false);
4757
- setShow(false);
4758
- }
4759
- else {
4760
- log('ignoring hideTimer handler due to hideTimer ticking');
4761
- }
4762
- }, p.minShowTimeMs);
4763
- }
4764
- }
4765
- else {
4766
- // ignore hiding the component since the min timer is running.
4767
- if (!hideTimer.current) {
4768
- log('setShow', false);
4769
- setShow(false);
4770
- }
4771
- else {
4772
- log('ignoring show change due to hideTimer ticking');
4773
- }
4774
- }
4775
- if (p.show) {
4776
- // set to a very long time so the hiding of the waiting indicator clears it.
4777
- notify('Loading content.', 60000);
4778
- }
4779
- else {
4780
- clearNotification();
4781
- }
4782
- }, [p.show]);
4783
- const id = (_c = p.id) !== null && _c !== void 0 ? _c : 'MknWaitingIndicator';
4784
- return (React__default.createElement(Modal, { id: id, __debug: p.__debug, __asWaitingIndicator: true, heading: '', onClose: () => {
4785
- /* noop */
4786
- }, className: "waitingIndicator", show: show },
4787
- React__default.createElement("div", { className: css({
4788
- color: 'white',
4789
- fontSize: '3rem',
4790
- padding: '0.7rem'
4791
- }) },
4792
- React__default.createElement(Icon, { id: "waiting", spin: true }))));
4393
+ } }, p.children));
4793
4394
  };
4794
4395
 
4795
4396
  /*
@@ -5088,284 +4689,778 @@ summary {
5088
4689
  `;
5089
4690
  return null;
5090
4691
  };
5091
-
5092
- /** Displays the value in American dollars. */
5093
- const getCurrencyDisplay = (value, isCents, denomination = '$') => {
5094
- let actualValue = value || 0;
5095
- if (isCents) {
5096
- actualValue /= 100;
4692
+
4693
+ const Table = (props) => {
4694
+ const theme = useThemeSafely();
4695
+ const tableStyles = css `
4696
+ label: Table;
4697
+ width: 100%;
4698
+ border-collapse: collapse;
4699
+ ${props.noCellBorder && `
4700
+ .table__td {
4701
+ border-left: none;
4702
+ border-right: none;
4703
+ }
4704
+ `}
4705
+ ${props.altRows && `
4706
+ .table__tr:nth-of-type(even) {
4707
+ background-color: ${theme.colors.lightBg};
4708
+ }
4709
+ `}
4710
+ `;
4711
+ const wrapperStyles = css `
4712
+ label: TableScrollWrapper;
4713
+ width:100%;
4714
+ overflow-y: auto;
4715
+ padding:0 1px; //fixes always showing of the table scroller
4716
+ `;
4717
+ return (React.createElement("div", { className: cx(wrapperStyles, props.wrapperClassName) },
4718
+ React.createElement("table", { className: cx(tableStyles, props.className) },
4719
+ props.caption && React.createElement("caption", { className: css({
4720
+ fontWeight: 'bold',
4721
+ padding: theme.controls.padding
4722
+ }) }, props.caption),
4723
+ props.children)));
4724
+ };
4725
+ const Tr = (props) => {
4726
+ return (React.createElement("tr", { className: cx('table__tr', props.className) }, props.children));
4727
+ };
4728
+ const Th = (props) => {
4729
+ var _a;
4730
+ let style = props.style;
4731
+ if (props.width) {
4732
+ if (style) {
4733
+ style = Object.assign(Object.assign({}, style), { width: props.width, minWidth: props.width });
4734
+ }
4735
+ else {
4736
+ style = { width: props.width, minWidth: props.width };
4737
+ }
4738
+ }
4739
+ const theme = useThemeSafely();
4740
+ const thStyles = css `
4741
+ border-bottom: ${theme.controls.border};
4742
+ padding: ${theme.controls.padding};
4743
+ font-weight: bold;
4744
+ text-align: ${(_a = props.align) !== null && _a !== void 0 ? _a : 'center'};
4745
+ > .button {
4746
+ font-weight: bold;
4747
+ }
4748
+ `;
4749
+ return (React.createElement("th", { className: cx(thStyles, props.className), style: style }, props.children));
4750
+ };
4751
+ const Td = (props) => {
4752
+ var _a;
4753
+ const theme = useThemeSafely();
4754
+ const tdStyles = css `
4755
+ border: ${theme.controls.border};
4756
+ padding: ${theme.controls.padding};
4757
+ vertical-align: middle;
4758
+ text-align: ${(_a = props.align) !== null && _a !== void 0 ? _a : 'center'};
4759
+ `;
4760
+ return (React.createElement("td", { colSpan: props.colSpan, style: props.style, className: cx('table__td', tdStyles, props.className) }, props.children));
4761
+ };
4762
+
4763
+ /* eslint @typescript-eslint/no-explicit-any: 0 */
4764
+ const ThemeRenderer = (p) => {
4765
+ const { backgroundColor, color } = p, theme = __rest(p, ["backgroundColor", "color"]);
4766
+ const flatTheme = flatten(theme);
4767
+ const entries = orderBy(Object.entries(flatTheme), x => x[0]);
4768
+ return (React.createElement(Table, { caption: (React.createElement("div", null,
4769
+ React.createElement(Text, { tag: "h1", align: "center" }, "Theme"),
4770
+ React.createElement(Text, { tag: "p", align: "center", italics: true }, "Background color applied to show colors with alpha ('rgba(X, X, X, 0.X)')"))), className: css({
4771
+ backgroundColor: backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : '#eee7ca',
4772
+ color: color !== null && color !== void 0 ? color : 'black'
4773
+ }) },
4774
+ React.createElement("thead", null,
4775
+ React.createElement(Tr, null,
4776
+ React.createElement(Th, { align: "left" }, "Property"),
4777
+ React.createElement(Th, { align: "left" }, "Value"))),
4778
+ React.createElement("tbody", null, entries.map(([key, value]) => {
4779
+ let colorBox;
4780
+ if (/color/i.test(key)) {
4781
+ colorBox = (React.createElement("span", { className: css({
4782
+ display: 'block',
4783
+ border: '1px solid black',
4784
+ width: '100%',
4785
+ height: 24,
4786
+ background: value
4787
+ }) }));
4788
+ }
4789
+ return (React.createElement(Tr, { key: key },
4790
+ React.createElement(Td, { align: "left" }, key),
4791
+ React.createElement(Td, { align: "left" },
4792
+ React.createElement("div", { className: css({
4793
+ display: 'flex',
4794
+ alignItems: 'center',
4795
+ gap: '1rem'
4796
+ }) },
4797
+ React.createElement("span", { className: css({ flexShrink: 1 }) }, value),
4798
+ " ",
4799
+ colorBox))));
4800
+ }))));
4801
+ };
4802
+ const flatten = (obj, parent, path = 'theme') => {
4803
+ const flatObj = parent !== null && parent !== void 0 ? parent : {};
4804
+ for (const prop in obj) {
4805
+ const value = obj[prop];
4806
+ const fullPath = `${path}.${prop}`;
4807
+ if (typeof value !== 'object') {
4808
+ flatObj[fullPath] = value;
4809
+ }
4810
+ else {
4811
+ flatten(value, flatObj, fullPath);
4812
+ }
4813
+ }
4814
+ return flatObj;
4815
+ };
4816
+
4817
+ let clearTimerId = undefined;
4818
+ /** Allows for status notificaiton methods for screen readers.
4819
+ * This hook does not have any dependencies, so it can be used in projects that don't important anything else from the style_guide. */
4820
+ function useAriaLiveRegion() {
4821
+ const id = 'MknAriaLiveRegion';
4822
+ if (!document.getElementById(id)) {
4823
+ const div = document.createElement('div');
4824
+ div.id = id;
4825
+ // different sources cannot decide if this is needed.
4826
+ // "Can work for status messages, but aria-live="polite" + text is usually simpler and more reliable for loading."
4827
+ div.role = 'status';
4828
+ div.ariaLive = 'polite';
4829
+ div.ariaAtomic = 'true';
4830
+ div.style.position = 'absolute';
4831
+ div.style.width = '1px';
4832
+ div.style.height = '1px';
4833
+ div.style.padding = '0px';
4834
+ div.style.margin = '-1px';
4835
+ div.style.overflow = 'hidden';
4836
+ div.style.whiteSpace = 'nowrap';
4837
+ document.body.prepend(div);
4838
+ }
4839
+ const clearNotification = () => {
4840
+ if (clearTimerId) {
4841
+ clearTimeout(clearTimerId);
4842
+ }
4843
+ const element = document.getElementById(id);
4844
+ if (!element) {
4845
+ return;
4846
+ }
4847
+ element.textContent = '';
4848
+ };
4849
+ return {
4850
+ /**
4851
+ * @param message - The text to be read by the screen reader.
4852
+ * @param clearTimeoutMs - Milliseconds to wait before the message is cleared. Defaults to `2000`.
4853
+ */
4854
+ notify: (message, clearTimoutMs) => {
4855
+ if (clearTimerId) {
4856
+ clearTimeout(clearTimerId);
4857
+ }
4858
+ const element = document.getElementById(id);
4859
+ if (!element) {
4860
+ return;
4861
+ }
4862
+ element.textContent = message;
4863
+ clearTimerId = setTimeout(() => {
4864
+ // this is considered a good practice. if you leave text here, some screen readers will read it out randomly as you navigate around the page.
4865
+ clearNotification();
4866
+ }, clearTimoutMs !== null && clearTimoutMs !== void 0 ? clearTimoutMs : 2000);
4867
+ },
4868
+ clearNotification
4869
+ };
4870
+ }
4871
+
4872
+ /*
4873
+ From https://fireship.io/snippets/use-media-query-hook/.
4874
+ Tried using https://www.npmjs.com/package/react-media, but it cause Webpack build issues.
4875
+ */
4876
+ /** React wrapper around window resizing and window.matchMedia.
4877
+ * https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
4878
+ */
4879
+ const useMediaQuery = (query) => {
4880
+ const [matches, setMatches] = useState(window.matchMedia(query).matches);
4881
+ useEffect(() => {
4882
+ const media = window.matchMedia(query);
4883
+ if (media.matches !== matches) {
4884
+ setMatches(media.matches);
4885
+ }
4886
+ const listener = () => setMatches(media.matches);
4887
+ window.addEventListener("resize", listener);
4888
+ return () => window.removeEventListener("resize", listener);
4889
+ }, [matches, query]);
4890
+ return matches;
4891
+ };
4892
+
4893
+ const Slider = (p) => {
4894
+ var _a;
4895
+ const theme = useThemeSafely();
4896
+ const sliderContainer = useRef(null);
4897
+ const sliderHandleSize = (_a = p.sliderHandleSize) !== null && _a !== void 0 ? _a : theme.controls.height;
4898
+ const height = p.showValue ? `calc(${sliderHandleSize} + 1.5rem)` : sliderHandleSize;
4899
+ // we're keeping this value in a ref vs. state so the constant updates don't cause re-render.
4900
+ // we will have to respond to outside value changes after the fact however...
4901
+ const currentValue = useRef(p.value);
4902
+ const [, forceUpdate] = useState(Date.now());
4903
+ useEffect(() => {
4904
+ if (p.value !== currentValue.current) {
4905
+ currentValue.current = p.value;
4906
+ forceUpdate(Date.now());
4907
+ }
4908
+ }, [p.value]);
4909
+ return (React__default.createElement("div", { ref: sliderContainer, className: cx(css({
4910
+ label: 'Slider',
4911
+ width: '100%',
4912
+ height,
4913
+ }), p.className) },
4914
+ React__default.createElement(ReactSlider, { ariaLabel: p.ariaLabel, step: p.step, min: p.min, max: p.max, value: p.value, onAfterChange: (value) => {
4915
+ p.onChange(value);
4916
+ }, onChange: p.onUpdate || p.showValue ? (value) => {
4917
+ var _a;
4918
+ currentValue.current = value;
4919
+ (_a = p.onUpdate) === null || _a === void 0 ? void 0 : _a.call(p, value);
4920
+ } : undefined, renderTrack: (props) => {
4921
+ const { className } = props, rest = __rest(props, ["className"]);
4922
+ return (React__default.createElement("div", Object.assign({ className: cx(className, p.trackClassName, css({
4923
+ display: 'flex',
4924
+ alignItems: 'center',
4925
+ height: sliderHandleSize
4926
+ })) }, rest),
4927
+ React__default.createElement("div", { className: css({
4928
+ backgroundColor: theme.colors.secondary,
4929
+ height: `calc(${sliderHandleSize} / 4)`,
4930
+ borderRadius: theme.controls.roundRadius,
4931
+ width: '100%'
4932
+ }, p.innerTrackClassName && rest.key === 'track-1' && Array.isArray(p.value) ? p.innerTrackClassName : undefined) })));
4933
+ }, renderThumb: (props, state) => {
4934
+ var _a;
4935
+ const { className } = props, rest = __rest(props, ["className"]);
4936
+ let specificThumbStyles;
4937
+ if (p.handle1ClassName && props.key === 'thumb-0') {
4938
+ specificThumbStyles = p.handle1ClassName;
4939
+ }
4940
+ else if (p.handle2ClassName && props.key === 'thumb-1') {
4941
+ specificThumbStyles = p.handle2ClassName;
4942
+ }
4943
+ return (React__default.createElement("div", Object.assign({ className: cx(className, css({
4944
+ borderRadius: theme.controls.roundRadius,
4945
+ backgroundColor: 'white',
4946
+ border: theme.controls.border,
4947
+ cursor: 'grab',
4948
+ boxShadow: theme.controls.buttonBoxShadow,
4949
+ transition: theme.controls.transition,
4950
+ '&:focus': {
4951
+ outline: 'none',
4952
+ boxShadow: theme.controls.focusOutlineShadow
4953
+ },
4954
+ '&:active': {
4955
+ boxShadow: 'none',
4956
+ cursor: 'grabbing'
4957
+ },
4958
+ '&:hover': {
4959
+ filter: theme.controls.hoverBrightness
4960
+ }
4961
+ }), specificThumbStyles, p.handleClassName, css({
4962
+ width: sliderHandleSize,
4963
+ height: sliderHandleSize,
4964
+ })) }, rest, { tabIndex: (_a = p.tabIndex) !== null && _a !== void 0 ? _a : 0 }), p.showValue && (React__default.createElement(HandleText, { sliderHandleSize: sliderHandleSize, className: p.sliderTextClassName, index: state.index, parentElement: sliderContainer.current, value: Array.isArray(currentValue.current) ? currentValue.current[state.index] : currentValue.current, renderValue: p.renderValue, renderValueWidth: p.renderValueWidth }))));
4965
+ } })));
4966
+ };
4967
+ const rectsCollideX = (r1, r2) => {
4968
+ if (r1.left >= r2.left && r1.left <= r2.right) {
4969
+ return true;
5097
4970
  }
5098
- return `${denomination}${actualValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
4971
+ if (r1.right >= r2.left && r1.right <= r2.right) {
4972
+ return true;
4973
+ }
4974
+ return false;
4975
+ };
4976
+ const HandleText = (p) => {
4977
+ var _a, _b, _c;
4978
+ const displayValue = (_b = (_a = p.renderValue) === null || _a === void 0 ? void 0 : _a.call(p, p.value)) !== null && _b !== void 0 ? _b : p.value;
4979
+ const renderValueWidth = (_c = p.renderValueWidth) !== null && _c !== void 0 ? _c : p.sliderHandleSize;
4980
+ const renderValueLeft = useMemo(() => {
4981
+ return `calc(${renderValueWidth} * 0.5 * -1 + (${p.sliderHandleSize} * 0.5))`;
4982
+ }, [p.renderValueWidth, p.sliderHandleSize]);
4983
+ const [flipText, setFlipText] = useState(false);
4984
+ const offset = '2px';
4985
+ useEffect(() => {
4986
+ // this needs to fire on every render due to the other handle also moving.
4987
+ var _a, _b;
4988
+ if (p.index === 1) {
4989
+ // only do this for the max/right-most handle
4990
+ const [r1, r2] = Array.from((_b = (_a = p.parentElement) === null || _a === void 0 ? void 0 : _a.querySelectorAll('.slider-handle').values()) !== null && _b !== void 0 ? _b : []).map(e => e.getBoundingClientRect());
4991
+ if (r1 && r2) {
4992
+ setFlipText(rectsCollideX(r1, r2));
4993
+ }
4994
+ }
4995
+ });
4996
+ return (React__default.createElement(Text, { ellipsis: true, className: cx('slider-handle', css({
4997
+ width: renderValueWidth,
4998
+ left: renderValueLeft,
4999
+ top: flipText ? undefined : `calc(${p.sliderHandleSize} + ${offset})`,
5000
+ bottom: flipText ? `calc(${p.sliderHandleSize} + ${offset})` : undefined,
5001
+ position: 'absolute',
5002
+ overflow: 'hidden',
5003
+ }), p.className), tag: "div", align: "center" }, displayValue));
5099
5004
  };
5100
5005
 
5101
- /** Converts an enum to an array of entities with id and name. The enum can be an integer or string enum.*/
5102
- const enumToEntities = (enumObj) => {
5103
- const entities = [];
5104
- for (const key in enumObj) {
5105
- if (isNaN(parseInt(key, 10))) {
5106
- entities.push({
5107
- id: enumObj[key],
5108
- name: key
5006
+ const TabHeader = (p) => {
5007
+ var _a, _b;
5008
+ const [tabIndex, setTabIndex] = React.useState((_a = p.startingIndex) !== null && _a !== void 0 ? _a : 0);
5009
+ const [tabsChanging, setTabsChanging] = React.useState(false);
5010
+ const theme = useThemeSafely();
5011
+ const variant = (_b = p.variant) !== null && _b !== void 0 ? _b : 'tab';
5012
+ const menuStyles = css({
5013
+ display: 'flex',
5014
+ gap: theme.controls.gap,
5015
+ listStyleType: 'none',
5016
+ margin: 0,
5017
+ padding: 0,
5018
+ flexWrap: variant === 'button' ? 'wrap' : 'nowrap'
5019
+ }, variant === 'button' && {
5020
+ borderBottom: theme.controls.border,
5021
+ paddingBottom: '1rem'
5022
+ });
5023
+ function onTabSelect(index, tabId, focusAfter) {
5024
+ const onChange = () => {
5025
+ var _a;
5026
+ setTabIndex(index);
5027
+ (_a = p.onTabChanged) === null || _a === void 0 ? void 0 : _a.call(p, index);
5028
+ if (focusAfter) {
5029
+ setTimeout(() => {
5030
+ var _a;
5031
+ (_a = document.getElementById(tabId)) === null || _a === void 0 ? void 0 : _a.focus();
5032
+ }, 0);
5033
+ }
5034
+ };
5035
+ if (p.onBeforeTabChanged) {
5036
+ setTabsChanging(true);
5037
+ p.onBeforeTabChanged(index)
5038
+ .then(() => {
5039
+ onChange();
5040
+ })
5041
+ .catch(() => {
5042
+ /* do nothing */
5043
+ })
5044
+ .finally(() => {
5045
+ setTabsChanging(false);
5109
5046
  });
5110
5047
  }
5048
+ else {
5049
+ onChange();
5050
+ }
5051
+ }
5052
+ return (React.createElement("div", { className: "tabHeader" },
5053
+ React.createElement("ul", { role: 'tablist', "aria-label": p.ariaLabel, className: cx(menuStyles, p.containerClassName) }, p.tabs.map((tab, index) => {
5054
+ var _a, _b;
5055
+ const active = index === tabIndex;
5056
+ let tabStyles;
5057
+ let buttonStyles;
5058
+ let buttonVariant;
5059
+ if (variant === 'tab') {
5060
+ tabStyles = css({
5061
+ paddingLeft: '1rem',
5062
+ paddingRight: '1rem',
5063
+ backgroundColor: theme.colors.bg,
5064
+ zIndex: 1,
5065
+ }, active && {
5066
+ border: theme.controls.border,
5067
+ borderRadius: theme.controls.borderRadius,
5068
+ borderBottomLeftRadius: 0,
5069
+ borderBottomRightRadius: 0,
5070
+ borderBottom: 'none',
5071
+ zIndex: 3,
5072
+ });
5073
+ buttonVariant = 'link';
5074
+ buttonStyles = css({
5075
+ maxWidth: (_a = p.maxTabWidth) !== null && _a !== void 0 ? _a : '10rem',
5076
+ overflow: 'hidden'
5077
+ });
5078
+ }
5079
+ else {
5080
+ buttonVariant = active ? 'primary' : undefined;
5081
+ buttonStyles = css({
5082
+ paddingLeft: '1rem',
5083
+ paddingRight: '1rem',
5084
+ maxWidth: (_b = p.maxTabWidth) !== null && _b !== void 0 ? _b : '10rem',
5085
+ overflow: 'hidden',
5086
+ });
5087
+ }
5088
+ let title = tab.title;
5089
+ let buttonContent;
5090
+ if (typeof tab.name === 'string') {
5091
+ title !== null && title !== void 0 ? title : (title = tab.name);
5092
+ buttonContent = React.createElement(Text, { tag: "div", align: "center", ellipsis: true }, tab.name);
5093
+ }
5094
+ else {
5095
+ buttonContent = tab.name;
5096
+ }
5097
+ const tabId = getTabHeaderTabId(p.id, index);
5098
+ return (React.createElement("li", { key: index, className: cx(tabStyles, p.tabClassName) },
5099
+ React.createElement(Button, { id: tabId, tabIndex: active ? 0 : -1, role: "tab", "aria-controls": p.ariaControlsId, "aria-selected": active, disabled: tabsChanging, className: buttonStyles, variant: buttonVariant, title: title, readOnly: active, onClick: () => {
5100
+ onTabSelect(index, tabId, false);
5101
+ }, onKeyDown: e => {
5102
+ e.stopPropagation();
5103
+ let newIndex = index;
5104
+ if (e.code === 'ArrowLeft') {
5105
+ if (index === 0) {
5106
+ newIndex = p.tabs.length - 1;
5107
+ }
5108
+ else {
5109
+ newIndex = index - 1;
5110
+ }
5111
+ }
5112
+ else if (e.code === 'ArrowRight') {
5113
+ if (index === p.tabs.length - 1) {
5114
+ newIndex = 0;
5115
+ }
5116
+ else {
5117
+ newIndex = index + 1;
5118
+ }
5119
+ }
5120
+ if (newIndex !== index) {
5121
+ onTabSelect(newIndex, getTabHeaderTabId(p.id, newIndex), true);
5122
+ }
5123
+ } }, buttonContent)));
5124
+ })),
5125
+ variant === 'tab' && (React.createElement("div", { className: cx(css({
5126
+ label: 'TabHeaderDivider',
5127
+ borderBottom: theme.controls.border,
5128
+ marginTop: `calc(${theme.controls.borderWidth} * -1)`,
5129
+ zIndex: 2,
5130
+ position: 'relative'
5131
+ }), p.tabHeaderDividerClassName) }))));
5132
+ };
5133
+ function getTabHeaderTabId(tabHeaderId, tabIndex) {
5134
+ return `${tabHeaderId}_tab_${tabIndex}`;
5135
+ }
5136
+
5137
+ const TabContainer = (p) => {
5138
+ var _a;
5139
+ const [tabIndex, setTabIndex] = React.useState((_a = p.startingIndex) !== null && _a !== void 0 ? _a : 0);
5140
+ const theme = useThemeSafely();
5141
+ const tabPanelId = `${p.id}_tabpanel`;
5142
+ const tabHeaderId = `${p.id}_TabHeader`;
5143
+ return (React.createElement("div", { className: css({
5144
+ label: 'TabContainer'
5145
+ }) },
5146
+ React.createElement(TabHeader, { id: tabHeaderId, ariaControlsId: tabPanelId, ariaLabel: p.ariaLabel, tabs: p.tabs, maxTabWidth: p.maxTabWidth, variant: p.variant, containerClassName: p.tabHeaderClassName, tabClassName: p.tabClassName, tabHeaderDividerClassName: p.tabHeaderDividerClassName, startingIndex: tabIndex, onBeforeTabChanged: p.onBeforeTabChanged, onTabChanged: (newIndex) => {
5147
+ var _a;
5148
+ setTabIndex(newIndex);
5149
+ (_a = p.onTabChanged) === null || _a === void 0 ? void 0 : _a.call(p, newIndex);
5150
+ } }),
5151
+ React.createElement("div", { role: 'tabpanel', id: tabPanelId, "aria-labelledby": getTabHeaderTabId(tabHeaderId, tabIndex), className: cx(css({
5152
+ label: 'TabContainerContent',
5153
+ padding: '1rem',
5154
+ borderLeft: theme.controls.border,
5155
+ borderRight: theme.controls.border,
5156
+ borderBottom: theme.controls.border,
5157
+ }), p.contentClassName) }, p.tabs[tabIndex].getContent())));
5158
+ };
5159
+
5160
+ const TdCurrency = (props) => {
5161
+ let actualValue = props.value || 0;
5162
+ if (props.cents) {
5163
+ actualValue = actualValue / 100;
5164
+ }
5165
+ const displayValue = actualValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
5166
+ return (React.createElement(Td, { align: "right" },
5167
+ "$",
5168
+ displayValue));
5169
+ };
5170
+
5171
+ const TdNumber = (props) => {
5172
+ return React.createElement(Td, { align: "right" }, props.value || props.value === 0 ? props.value.toLocaleString() : '');
5173
+ };
5174
+
5175
+ const ThSort = (props) => {
5176
+ let iconId = '';
5177
+ if (props.direction) {
5178
+ if (props.direction === 'asc') {
5179
+ iconId = 'sortAsc';
5180
+ }
5181
+ else {
5182
+ iconId = 'sortDesc';
5183
+ }
5111
5184
  }
5112
- return entities;
5185
+ let rightContentSpecialJustify = 'center';
5186
+ switch (props.align) {
5187
+ case 'left':
5188
+ rightContentSpecialJustify = 'flex-start';
5189
+ break;
5190
+ case 'right':
5191
+ rightContentSpecialJustify = 'flex-end';
5192
+ break;
5193
+ }
5194
+ return (React.createElement(Th, { align: props.align, style: props.style, width: props.width },
5195
+ React.createElement("div", { className: props.rightContent && css({
5196
+ display: 'flex',
5197
+ alignItems: 'center',
5198
+ justifyContent: rightContentSpecialJustify,
5199
+ gap: '0.5rem'
5200
+ }) },
5201
+ React.createElement(Button, { onClick: props.onClick, variant: "link" },
5202
+ props.text,
5203
+ iconId && React.createElement(Icon, { className: css({
5204
+ marginLeft: '0.5rem'
5205
+ }), id: iconId })),
5206
+ props.rightContent)));
5113
5207
  };
5114
5208
 
5115
- const Link = (props) => {
5209
+ const defaultMaxLength = 200;
5210
+ const defaultRows = 10;
5211
+ const TextArea = React.forwardRef((props, ref) => {
5212
+ var _a, _b;
5213
+ const [localValue, setLocalValue] = React.useState(props.value);
5214
+ const inputRef = (ref !== null && ref !== void 0 ? ref : React.useRef(null));
5215
+ const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, props);
5116
5216
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
5117
- const { rightIcon, leftIcon, block, iconBlock, variant, round, small, colorOverride, children, disabled, ref, waiting } = props, linkProps = __rest(props, ["rightIcon", "leftIcon", "block", "iconBlock", "variant", "round", "small", "colorOverride", "children", "disabled", "ref", "waiting"]);
5217
+ const { onValueChange, customError, reportValueOnError, showErrorDisplay, allowUpdateOnFocus } = props, nativeProps = __rest(props, ["onValueChange", "customError", "reportValueOnError", "showErrorDisplay", "allowUpdateOnFocus"]);
5118
5218
  const theme = useThemeSafely();
5119
- const linkStyles = generateLinkStyles(props, theme);
5120
- const mainClassName = cx('link', linkStyles, props.className);
5121
- if (variant === 'text') {
5122
- return React.createElement(Text, { className: mainClassName, tag: "div" }, props.children);
5123
- }
5124
- const isDisabled = props.disabled || props.waiting;
5125
- return (React.createElement("a", Object.assign({}, linkProps, { tabIndex: disabled ? -1 : undefined, target: props.target, className: mainClassName, onClick: e => {
5126
- var _a;
5127
- if (isDisabled) {
5128
- e.stopPropagation();
5129
- e.preventDefault();
5219
+ React.useEffect(() => {
5220
+ updateErrorMessage();
5221
+ }, []);
5222
+ useIgnoreMount(() => {
5223
+ var _a;
5224
+ if ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.checkValidity()) {
5225
+ onValueChange(localValue);
5226
+ }
5227
+ else {
5228
+ if (reportValueOnError) {
5229
+ onValueChange(localValue);
5130
5230
  }
5131
5231
  else {
5132
- (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props, e);
5133
- }
5134
- } }),
5135
- React.createElement(LinkContent, Object.assign({}, props))));
5136
- };
5137
-
5138
- /* eslint @typescript-eslint/no-explicit-any: 0 */
5139
- const ThemeRenderer = (p) => {
5140
- const { backgroundColor, color } = p, theme = __rest(p, ["backgroundColor", "color"]);
5141
- const flatTheme = flatten(theme);
5142
- const entries = orderBy(Object.entries(flatTheme), x => x[0]);
5143
- return (React.createElement(Table, { caption: (React.createElement("div", null,
5144
- React.createElement(Text, { tag: "h1", align: "center" }, "Theme"),
5145
- React.createElement(Text, { tag: "p", align: "center", italics: true }, "Background color applied to show colors with alpha ('rgba(X, X, X, 0.X)')"))), className: css({
5146
- backgroundColor: backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : '#eee7ca',
5147
- color: color !== null && color !== void 0 ? color : 'black'
5148
- }) },
5149
- React.createElement("thead", null,
5150
- React.createElement(Tr, null,
5151
- React.createElement(Th, { align: "left" }, "Property"),
5152
- React.createElement(Th, { align: "left" }, "Value"))),
5153
- React.createElement("tbody", null, entries.map(([key, value]) => {
5154
- let colorBox;
5155
- if (/color/i.test(key)) {
5156
- colorBox = (React.createElement("span", { className: css({
5157
- display: 'block',
5158
- border: '1px solid black',
5159
- width: '100%',
5160
- height: 24,
5161
- background: value
5162
- }) }));
5232
+ onValueChange(undefined);
5163
5233
  }
5164
- return (React.createElement(Tr, { key: key },
5165
- React.createElement(Td, { align: "left" }, key),
5166
- React.createElement(Td, { align: "left" },
5167
- React.createElement("div", { className: css({
5168
- display: 'flex',
5169
- alignItems: 'center',
5170
- gap: '1rem'
5171
- }) },
5172
- React.createElement("span", { className: css({ flexShrink: 1 }) }, value),
5173
- " ",
5174
- colorBox))));
5175
- }))));
5176
- };
5177
- const flatten = (obj, parent, path = 'theme') => {
5178
- const flatObj = parent !== null && parent !== void 0 ? parent : {};
5179
- for (const prop in obj) {
5180
- const value = obj[prop];
5181
- const fullPath = `${path}.${prop}`;
5182
- if (typeof value !== 'object') {
5183
- flatObj[fullPath] = value;
5184
5234
  }
5185
- else {
5186
- flatten(value, flatObj, fullPath);
5235
+ updateErrorMessage();
5236
+ }, [localValue]);
5237
+ useIgnoreMount(() => {
5238
+ if (allowUpdateOnFocus || document.activeElement !== inputRef.current) {
5239
+ setLocalValue(props.value);
5187
5240
  }
5241
+ updateErrorMessage();
5242
+ }, [props.value]);
5243
+ const styles = css({
5244
+ backgroundColor: theme.colors.bg,
5245
+ maxWidth: '100%',
5246
+ minHeight: theme.controls.height,
5247
+ fontFamily: theme.fonts.family,
5248
+ fontSize: theme.fonts.size,
5249
+ width: '100%',
5250
+ border: theme.controls.border,
5251
+ borderRadius: theme.controls.borderRadius,
5252
+ color: theme.colors.font,
5253
+ paddingTop: '0.75rem',
5254
+ paddingLeft: theme.controls.padding,
5255
+ paddingRight: theme.controls.padding,
5256
+ height: 'auto',
5257
+ transition: theme.controls.transition,
5258
+ ':focus': {
5259
+ outline: 'none',
5260
+ boxShadow: theme.controls.focusOutlineShadow
5261
+ },
5262
+ ':disabled': {
5263
+ backgroundColor: theme.colors.disabled,
5264
+ cursor: 'not-allowed'
5265
+ },
5266
+ ':invalid': {
5267
+ borderColor: theme.colors.required,
5268
+ ':focus': {
5269
+ boxShadow: theme.controls.focusOutlineRequiredShadow
5270
+ }
5271
+ },
5272
+ }, props.readOnly && {
5273
+ backgroundColor: 'transparent',
5274
+ cursor: 'default',
5275
+ border: 'none',
5276
+ ':focus': {
5277
+ outline: 'none',
5278
+ boxShadow: 'none'
5279
+ }
5280
+ });
5281
+ return (React.createElement("span", { className: css({
5282
+ display: 'inline-block',
5283
+ width: '100%'
5284
+ }) },
5285
+ React.createElement("textarea", Object.assign({}, nativeProps, { className: cx(styles, props.className), autoComplete: (_a = props.autoComplete) !== null && _a !== void 0 ? _a : 'off', tabIndex: props.readOnly ? -1 : props.tabIndex, maxLength: props.maxLength || defaultMaxLength, rows: (_b = props.rows) !== null && _b !== void 0 ? _b : defaultRows, ref: inputRef, value: localValue !== null && localValue !== void 0 ? localValue : '', onChange: e => {
5286
+ var _a;
5287
+ setLocalValue(e.target.value || undefined);
5288
+ (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
5289
+ }, onBlur: e => {
5290
+ var _a;
5291
+ if (!props.noTrim) {
5292
+ setLocalValue(currentValue => {
5293
+ return currentValue === null || currentValue === void 0 ? void 0 : currentValue.trim();
5294
+ });
5295
+ }
5296
+ (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
5297
+ } })),
5298
+ (showErrorDisplay !== null && showErrorDisplay !== void 0 ? showErrorDisplay : true) && React.createElement(InputErrorDisplay, { error: validationError })));
5299
+ });
5300
+
5301
+ const ToggleButton = React.forwardRef((props, ref) => {
5302
+ const { checked, checkedChildren, uncheckedChildren, checkedVariant, checkedClassName, checkedStyle, checkedIcon, uncheckedIcon } = props, buttonProps = __rest(props, ["checked", "checkedChildren", "uncheckedChildren", "checkedVariant", "checkedClassName", "checkedStyle", "checkedIcon", "uncheckedIcon"]);
5303
+ let children;
5304
+ if (checked) {
5305
+ children = checkedChildren;
5188
5306
  }
5189
- return flatObj;
5307
+ else {
5308
+ children = uncheckedChildren;
5309
+ }
5310
+ return (React.createElement(Button, Object.assign({}, buttonProps, { ref: ref, className: cx('toggleButton', checked && 'toggleButton--checked', props.className, checked && checkedClassName), rightIcon: checked ? checkedIcon : uncheckedIcon, variant: checked ? checkedVariant : props.variant, style: checked ? checkedStyle : props.style }), children));
5311
+ });
5312
+
5313
+ const ToggleButtonGroup = (props) => {
5314
+ const theme = useThemeSafely();
5315
+ const groupStyles = css `
5316
+ display: flex;
5317
+ box-shadow: ${theme.controls.buttonBoxShadow};
5318
+ border-radius: ${theme.controls.borderRadius};
5319
+ ${props.round && `
5320
+ border-radius: ${theme.controls.roundRadius};
5321
+ `}
5322
+ `;
5323
+ const buttonStyles = css `
5324
+ flex-grow:1;
5325
+ box-shadow: none;
5326
+ &:nth-of-type(1n+2){
5327
+ margin-left: -1px;
5328
+ }
5329
+ border-radius: 0;
5330
+ &:first-of-type{
5331
+ border-top-left-radius: ${theme.controls.borderRadius};
5332
+ border-bottom-left-radius: ${theme.controls.borderRadius};
5333
+ }
5334
+ &:last-child {
5335
+ border-top-right-radius: ${theme.controls.borderRadius};
5336
+ border-bottom-right-radius: ${theme.controls.borderRadius};
5337
+ }
5338
+ ${props.round && `
5339
+ &:first-of-type{
5340
+ border-top-left-radius: ${theme.controls.roundRadius};
5341
+ border-bottom-left-radius: ${theme.controls.roundRadius};
5342
+ padding-left: 1rem;
5343
+ }
5344
+ &:last-child {
5345
+ border-top-right-radius: ${theme.controls.roundRadius};
5346
+ border-bottom-right-radius: ${theme.controls.roundRadius};
5347
+ padding-right: 1rem;
5348
+ }
5349
+ `}
5350
+ `;
5351
+ return (React.createElement("div", { className: cx('toggleButtonGroup', groupStyles, props.className) }, props.options.map(o => {
5352
+ const active = o.id === props.value;
5353
+ return React.createElement(Button, { style: props.width ? { width: props.width } : undefined, small: props.small, rightIcon: o.rightIcon, key: o.id, tabIndex: active ? -1 : 0, className: cx(css `
5354
+ ${buttonStyles}
5355
+ ${active && `
5356
+ background-color: ${theme.colors.font};
5357
+ color: ${theme.colors.bg};
5358
+ cursor: default;
5359
+ &:hover:not(:disabled) {
5360
+ filter: none;
5361
+ }
5362
+ &:focus {
5363
+ outline: none;
5364
+ box-shadow: none;
5365
+ }
5366
+ `}
5367
+ `, active ? o.activeClass : undefined), disabled: props.disabled, enforceMinWidth: props.enforceMinWidth, onClick: () => {
5368
+ if (active) {
5369
+ return;
5370
+ }
5371
+ props.onChange(o.id);
5372
+ } }, o.name);
5373
+ })));
5190
5374
  };
5191
5375
 
5192
- const TabContainer = (p) => {
5193
- var _a;
5194
- const [tabIndex, setTabIndex] = React.useState((_a = p.startingIndex) !== null && _a !== void 0 ? _a : 0);
5195
- const theme = useThemeSafely();
5196
- const tabPanelId = `${p.id}_tabpanel`;
5197
- const tabHeaderId = `${p.id}_TabHeader`;
5198
- return (React.createElement("div", { className: css({
5199
- label: 'TabContainer'
5200
- }) },
5201
- React.createElement(TabHeader, { id: tabHeaderId, ariaControlsId: tabPanelId, ariaLabel: p.ariaLabel, tabs: p.tabs, maxTabWidth: p.maxTabWidth, variant: p.variant, containerClassName: p.tabHeaderClassName, tabClassName: p.tabClassName, tabHeaderDividerClassName: p.tabHeaderDividerClassName, startingIndex: tabIndex, onBeforeTabChanged: p.onBeforeTabChanged, onTabChanged: (newIndex) => {
5202
- var _a;
5203
- setTabIndex(newIndex);
5204
- (_a = p.onTabChanged) === null || _a === void 0 ? void 0 : _a.call(p, newIndex);
5205
- } }),
5206
- React.createElement("div", { role: 'tabpanel', id: tabPanelId, "aria-labelledby": getTabHeaderTabId(tabHeaderId, tabIndex), className: cx(css({
5207
- label: 'TabContainerContent',
5208
- padding: '1rem',
5209
- borderLeft: theme.controls.border,
5210
- borderRight: theme.controls.border,
5211
- borderBottom: theme.controls.border,
5212
- }), p.contentClassName) }, p.tabs[tabIndex].getContent())));
5213
- };
5376
+ const TogglePasswordInput = React.forwardRef((props, ref) => {
5377
+ const { onVisibilityChanged } = props, inputProps = __rest(props, ["onVisibilityChanged"]);
5378
+ const [show, setShow] = React.useState(false);
5379
+ useIgnoreMount(() => {
5380
+ onVisibilityChanged === null || onVisibilityChanged === void 0 ? void 0 : onVisibilityChanged(show);
5381
+ }, [show]);
5382
+ return (React.createElement(TextInput, Object.assign({}, inputProps, { ref: ref, type: show ? 'text' : 'password', rightControl: (React.createElement(Button, { small: true, style: {
5383
+ // small button is required here due to the icon pushing outside the boundries of the
5384
+ // parent textbox. increasing the font size here to fill the small button.
5385
+ fontSize: '1rem'
5386
+ }, variant: "icon", onClick: () => {
5387
+ setShow(previous => !previous);
5388
+ } },
5389
+ React.createElement(Icon, { id: show ? 'show' : 'hide' }))) })));
5390
+ });
5214
5391
 
5215
- const defaultMinChars = 3;
5216
- class AutocompleteController {
5217
- constructor(getOptions, config) {
5218
- var _a;
5219
- this._value = undefined;
5220
- this._options = [];
5221
- this._minChars = (_a = config === null || config === void 0 ? void 0 : config.minChars) !== null && _a !== void 0 ? _a : defaultMinChars;
5222
- if (config === null || config === void 0 ? void 0 : config.debounceMs) {
5223
- this.getOptions = createDebouncedPromise(getOptions, config === null || config === void 0 ? void 0 : config.debounceMs);
5224
- }
5225
- else {
5226
- this.getOptions = getOptions;
5227
- }
5228
- }
5229
- get value() {
5230
- return this._value;
5231
- }
5232
- get options() {
5233
- return this._options;
5234
- }
5235
- async onChange(newValue) {
5236
- // don't make getOptions calls if the value hasn't changed.
5237
- if (newValue === this.value) {
5238
- return;
5239
- }
5240
- // nullish should not make the getOptions call and instead clear everything.
5241
- if (!newValue) {
5242
- this._value = newValue;
5243
- this._options = [];
5244
- return;
5245
- }
5246
- // sub min chars should clear everything and not attempt the getOptions call.
5247
- if (newValue.length < this._minChars) {
5248
- this._value = newValue;
5249
- this._options = [];
5250
- return;
5251
- }
5252
- try {
5253
- this._value = newValue;
5254
- this._options = (await this.getOptions(newValue));
5255
- }
5256
- catch (err) {
5257
- // this method will throw errors on debounce rejections. that is to be expected.
5258
- // for actual getOptions exceptions, the owner of that function will need to handle errors.
5259
- }
5260
- }
5261
- onPick(newValue) {
5262
- this._value = newValue;
5263
- this._options = [];
5392
+ const WaitingIndicator = (p) => {
5393
+ var _a, _b, _c;
5394
+ const [show, setShow] = useState(p.show);
5395
+ const hideTimer = useRef(0);
5396
+ const lastShowStatus = useRef(false);
5397
+ const log = useLogger(`WaitingIndicator ${(_a = p.id) !== null && _a !== void 0 ? _a : '?'}`, (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
5398
+ const { notify, clearNotification } = useAriaLiveRegion();
5399
+ if (p.__debug) {
5400
+ useEffect(() => {
5401
+ log('mounted');
5402
+ return () => {
5403
+ log('unmounted');
5404
+ };
5405
+ }, []);
5264
5406
  }
5265
- }
5266
- const createDebouncedPromise = (originalFunction, trailingTimeoutMs) => {
5267
- let timer;
5268
- let onCancel;
5269
- return (value) => {
5270
- if (timer) {
5271
- clearTimeout(timer);
5407
+ useEffect(() => {
5408
+ log('show changed', p.show);
5409
+ // we need to store the 'last props' since props.show will be captured locally and the incorrect
5410
+ // value will display in the timeout below.
5411
+ log('storing lastShowStatus', p.show);
5412
+ lastShowStatus.current = p.show;
5413
+ if (p.show) {
5414
+ log('setShow', true);
5415
+ setShow(true);
5416
+ if (p.minShowTimeMs) {
5417
+ log('staring hideTimer', 'timout in ms:', p.minShowTimeMs);
5418
+ hideTimer.current = window.setTimeout(() => {
5419
+ log('hideTimer complete', 'clearing hideTimer');
5420
+ window.clearTimeout(hideTimer.current);
5421
+ hideTimer.current = 0;
5422
+ // this check is necessary since the show status may have updated again to true.
5423
+ // if so, ignore this timeout since we're already past the min time and we're still
5424
+ // showing the component.
5425
+ if (!lastShowStatus.current) {
5426
+ log('setShow', false);
5427
+ setShow(false);
5428
+ }
5429
+ else {
5430
+ log('ignoring hideTimer handler due to hideTimer ticking');
5431
+ }
5432
+ }, p.minShowTimeMs);
5433
+ }
5272
5434
  }
5273
- if (onCancel) {
5274
- onCancel('Promise cancelled due to in-progress debounce call.');
5275
- onCancel = undefined;
5435
+ else {
5436
+ // ignore hiding the component since the min timer is running.
5437
+ if (!hideTimer.current) {
5438
+ log('setShow', false);
5439
+ setShow(false);
5440
+ }
5441
+ else {
5442
+ log('ignoring show change due to hideTimer ticking');
5443
+ }
5276
5444
  }
5277
- return new Promise((res, rej) => {
5278
- onCancel = rej;
5279
- timer = setTimeout(() => {
5280
- originalFunction(value)
5281
- .then(values => {
5282
- res(values);
5283
- })
5284
- .catch(err => {
5285
- rej(err);
5286
- })
5287
- .finally(() => {
5288
- clearTimeout(timer);
5289
- });
5290
- }, trailingTimeoutMs);
5291
- });
5292
- };
5293
- };
5294
-
5295
- /** Extracted logic around autocomplete functionality for Autocomplete.tsx that supports Entity (id/name) mapping. */
5296
- class AutocompleteEntityController {
5297
- constructor(getOptions, config) {
5298
- this._options = [];
5299
- const getStringOptions = async (value) => {
5300
- this._options = await getOptions(value);
5301
- return this._options.map(o => o.name);
5302
- };
5303
- this._ctrl = new AutocompleteController(getStringOptions, config);
5304
- }
5305
- get entity() {
5306
- return this._pickedEntity;
5307
- }
5308
- get entities() {
5309
- return this._options;
5310
- }
5311
- get value() {
5312
- return this._ctrl.value;
5313
- }
5314
- get options() {
5315
- return this._options.map(o => o.name);
5316
- }
5317
- async onChange(newValue) {
5318
- const clearEntity = newValue !== this._ctrl.value;
5319
- await this._ctrl.onChange(newValue);
5320
- if (clearEntity) {
5321
- this._pickedEntity = undefined;
5445
+ if (p.show) {
5446
+ // set to a very long time so the hiding of the waiting indicator clears it.
5447
+ notify('Loading content.', 60000);
5322
5448
  }
5323
- this.trySyncCtrlOptions();
5324
- }
5325
- onPick(newValue) {
5326
- this._ctrl.onPick(newValue);
5327
- this._pickedEntity = this._options.find(o => o.name === this._ctrl.value);
5328
- this.trySyncCtrlOptions();
5329
- }
5330
- trySyncCtrlOptions() {
5331
- if (!this._ctrl.options.length) {
5332
- this._options = [];
5449
+ else {
5450
+ clearNotification();
5333
5451
  }
5334
- }
5335
- }
5336
-
5337
- var decimal=["decimal","décimale"];var place=["lugar","lieu"];var places=["lugares","lieux"];var file=["archivo","fichier"];var selected=["seleccionado","choisi"];var Upload=["Subir","Télécharger"];var Clear=["Cerrar","Fermer"];var Showing=["Mostrando","Affichage de"];var of=["de","sur"];var results=["resultados","résultats"];var Page=["Página","Page"];var Limit=["Límite","Limite"];var Sort=["Ordenar","Trier"];var January=["Enero","Janvier"];var February=["Febrero","Février"];var March=["Marzo","Mars"];var April=["Abril","Avril"];var May=["Mayo","Mai"];var June=["Junio","Juin"];var July=["Julio","Juillet"];var August=["Agosto","Août"];var September=["Septiembre","Septembre"];var October=["Octubre","Octobre"];var November=["Noviembre","Novembre"];var December=["Diciembre","Décembre"];var Sun=["Dom","Dim"];var Mon=["Lun","Lun"];var Tue=["Mar","Mar"];var Wed=["Mié","Mer"];var Thu=["Jue","Jeu"];var Fri=["Vie","Ven"];var Sat=["Sáb","Sam"];var OK=["Aceptar","Accepter"];var Cancel=["Cancelar","Annuler"];var strings = {"Required.":["Requerido.","Requis."],"Must be at least":["Debe ser al menos","Doit être au moins"],"characters in length.":["caracteres de longitud.","caractères de longueur."],"Must be an integer.":["Debe ser un número entero.","Doit être un entier."],"Limited to":["Limitado a","Limité à"],decimal:decimal,place:place,places:places,"Must be greater than or equal to":["Debe ser mayor o igual a","Doit être supérieur ou égal à"],"Must be less than or equal to":["Debe ser menor o igual a","Doit être inférieur ou égal à"],"Invalid email.":["Correo electrónico no válido.","Email invalide."],"Invalid URL.":["URL no válida.","URL non valide."],"Click or drag and drop files.":["Haga clic o arrastre y suelte archivos.","Cliquez ou faites glisser et déposez les fichiers."],file:file,selected:selected,"Upload failed.":["Error en la carga.","Le téléchargement a échoué."],Upload:Upload,"Upload successful.":["Carga exitosa","Téléchargement réussi."],"Invalid files":["Archivos no válidos","Fichiers invalides"],"Max file size exceeded":["Se ha excedido el tamaño máximo de archivo","La taille maximale du fichier a été dépassée"],"Error":["Error","Erreur"],Clear:Clear,Showing:Showing,of:of,results:results,"No Results":["No Hay Resultados","Aucun Résultat"],Page:Page,Limit:Limit,Sort:Sort,January:January,February:February,March:March,April:April,May:May,June:June,July:July,August:August,September:September,October:October,November:November,December:December,Sun:Sun,Mon:Mon,Tue:Tue,Wed:Wed,Thu:Thu,Fri:Fri,Sat:Sat,OK:OK,Cancel:Cancel,"Copied!":["¡Copiado!","Copié!"],"Copy to clipboard":["Copiar al portapapeles","Copier dans le presse-papier"]};
5338
-
5339
- const LocalizationProvider = (p) => {
5340
- var _a;
5341
- const log = useLogger('LocalizationProvider', (_a = p.__debug) !== null && _a !== void 0 ? _a : false);
5342
- const stringsDb = strings;
5343
- return (React__default.createElement(LocalizationContext.Provider, { value: {
5344
- language: p.language,
5345
- getText: (text) => {
5346
- if (!text) {
5347
- return '';
5348
- }
5349
- if (!isNaN(parseInt(text))) {
5350
- return text;
5351
- }
5352
- const wordArray = stringsDb[text];
5353
- if (!wordArray) {
5354
- log(`No localization data exists for "${text}".`);
5355
- }
5356
- if (p.language === StyleGuideLanguage.English) {
5357
- return text;
5358
- }
5359
- const languageIndex = p.language - 1;
5360
- const translation = wordArray === null || wordArray === void 0 ? void 0 : wordArray[languageIndex];
5361
- if (translation) {
5362
- return translation;
5363
- }
5364
- log(`No ${StyleGuideLanguage[p.language]} translations exist for "${text}".`);
5365
- return text;
5366
- }
5367
- } }, p.children));
5452
+ }, [p.show]);
5453
+ const id = (_c = p.id) !== null && _c !== void 0 ? _c : 'MknWaitingIndicator';
5454
+ return (React__default.createElement(Modal, { id: id, __debug: p.__debug, __asWaitingIndicator: true, heading: '', onClose: () => {
5455
+ /* noop */
5456
+ }, className: "waitingIndicator", show: show },
5457
+ React__default.createElement("div", { className: css({
5458
+ color: 'white',
5459
+ fontSize: '3rem',
5460
+ padding: '0.7rem'
5461
+ }) },
5462
+ React__default.createElement(Icon, { id: "waiting", spin: true }))));
5368
5463
  };
5369
5464
 
5370
- export { Accordion, Autocomplete, AutocompleteController, AutocompleteEntityController, Backdrop$1 as Backdrop, Backdrop as Backdrop2, BoundMemoryPager, BoundStaticPager, Button, Calendar, Checkbox, ConfirmModal, CopyButton, DateInput, DialogPopover, Divider, ErrorModal, FileUploader, Form, FormColumnRow, FormFlexRow, GlobalStyles, Header, Highlight, ICONS, Icon, Image, InfoPanel, InfoTip, ItemPager, Label, Link, List, ListItem, LocalizationProvider, Modal, Nav, NormalizeCss, NumberInput, OmniLink, PagedResult, Pager, Picker, ProgressBar, SearchBox, Slider, StyleGuideLanguage, TabContainer, TabHeader, TabLocker, Table, Td, TdCurrency, TdNumber, Text, TextArea, TextInput, Th, ThSort, ThemeProvider, ThemeRenderer, ToggleButton, ToggleButtonGroup, TogglePasswordInput, TooltipPopover, Tr, WaitingIndicator, calcDynamicThemeProps, defaultTheme, enumToEntities, getCurrencyDisplay, getFileSizeDisplay, modalScrollFixClassName, useAccordionState, useAriaLiveRegion, useBooleanChanged, useIgnoreMount, useMediaQuery, useScrollbarSize, useThemeSafely, useWaiting };
5465
+ export { Accordion, Autocomplete, AutocompleteController, AutocompleteEntityController, Backdrop$1 as Backdrop, Backdrop as Backdrop2, BoundMemoryPager, BoundStaticPager, Button, Calendar, Checkbox, ConfirmModal, CopyButton, DateInput, DialogPopover, Divider, ErrorModal, FileUploader, Form, FormColumnRow, FormFlexRow, GlobalStyles, Header, Highlight, ICONS, Icon, Image, InfoPanel, InfoTip, ItemPager, Label, Link, List, ListItem, LocalizationProvider, Modal, MultiPicker, Nav, NormalizeCss, NumberInput, OmniLink, PagedResult, Pager, Picker, ProgressBar, SearchBox, Slider, StyleGuideLanguage, TabContainer, TabHeader, TabLocker, Table, Td, TdCurrency, TdNumber, Text, TextArea, TextInput, Th, ThSort, ThemeProvider, ThemeRenderer, ToggleButton, ToggleButtonGroup, TogglePasswordInput, TooltipPopover, Tr, WaitingIndicator, calcDynamicThemeProps, defaultTheme, enumToEntities, getCurrencyDisplay, getFileSizeDisplay, modalScrollFixClassName, useAccordionState, useAriaLiveRegion, useBooleanChanged, useIgnoreMount, useMediaQuery, useScrollbarSize, useThemeSafely, useWaiting };
5371
5466
  //# sourceMappingURL=index.esm.js.map