@mackin.com/styleguide 8.12.0 → 8.12.1

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 (3) hide show
  1. package/index.d.ts +5 -5
  2. package/index.esm.js +4831 -0
  3. package/package.json +2 -1
package/index.esm.js ADDED
@@ -0,0 +1,4831 @@
1
+ import * as React from 'react';
2
+ import React__default, { createContext, useContext, useState, useEffect, useRef, useMemo } from 'react';
3
+ import { cx, css, keyframes, injectGlobal } from '@emotion/css';
4
+ import { faCheckCircle, faCircle, faChevronDown, faChevronUp, faSquare, faTimesCircle } from '@fortawesome/pro-regular-svg-icons';
5
+ import { faPlus, faTrashAlt, faSave, faCrow, faTimes, faSync, faCheckSquare } from '@fortawesome/pro-solid-svg-icons';
6
+ import { faWifi, faWifiSlash, faBars, faSearch, faQuestionCircle, faNarwhal, faChevronRight, faChevronLeft, faCloudDownload, faCloudUpload, faCalendarAlt, faCopy, faPaste, faEyeSlash, faEye } from '@fortawesome/pro-light-svg-icons';
7
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8
+ import { debounce, sumBy, orderBy, get } from 'lodash';
9
+ import { format, isExists, getDaysInMonth, getDay, isSameMonth, isBefore, isAfter, isSameDay, endOfMonth, addMonths, startOfMonth } from 'date-fns';
10
+ import { nanoid } from 'nanoid';
11
+ import { createPortal } from 'react-dom';
12
+ import { Popover as Popover$1, ArrowContainer } from 'react-tiny-popover';
13
+ import { Link as Link$1 } from 'react-router-dom';
14
+ import ReactSlider from 'react-slider';
15
+
16
+ /*! *****************************************************************************
17
+ Copyright (c) Microsoft Corporation.
18
+
19
+ Permission to use, copy, modify, and/or distribute this software for any
20
+ purpose with or without fee is hereby granted.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
23
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
24
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
25
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
26
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
27
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
28
+ PERFORMANCE OF THIS SOFTWARE.
29
+ ***************************************************************************** */
30
+
31
+ function __rest(s, e) {
32
+ var t = {};
33
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
34
+ t[p] = s[p];
35
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
36
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
37
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
38
+ t[p[i]] = s[p[i]];
39
+ }
40
+ return t;
41
+ }
42
+
43
+ //TB: FUTURE will need to update. the versions of fa are at 6 now.
44
+ // major version
45
+ const ICONS = {
46
+ add: faPlus,
47
+ delete: faTrashAlt,
48
+ save: faSave,
49
+ activate: faCheckCircle,
50
+ deactivate: faCircle,
51
+ online: faWifi,
52
+ offline: faWifiSlash,
53
+ noIcon: faCrow,
54
+ close: faTimes,
55
+ waiting: faSync,
56
+ refresh: faSync,
57
+ menu: faBars,
58
+ search: faSearch,
59
+ expand: faChevronDown,
60
+ collapse: faChevronUp,
61
+ help: faQuestionCircle,
62
+ debug: faNarwhal,
63
+ goTo: faChevronRight,
64
+ goBack: faChevronLeft,
65
+ download: faCloudDownload,
66
+ upload: faCloudUpload,
67
+ selected: faCheckSquare,
68
+ unselected: faSquare,
69
+ pagerLeft: faChevronLeft,
70
+ pagerRight: faChevronRight,
71
+ sortAsc: faChevronUp,
72
+ sortDesc: faChevronDown,
73
+ pickDate: faCalendarAlt,
74
+ copy: faCopy,
75
+ paste: faPaste,
76
+ clear: faTimesCircle,
77
+ hide: faEyeSlash,
78
+ show: faEye
79
+ };
80
+ const Icon = (props) => {
81
+ var _a;
82
+ const icon = (_a = ICONS[props.id]) !== null && _a !== void 0 ? _a : ICONS['noIcon'];
83
+ return React.createElement(FontAwesomeIcon, { title: props.title, style: props.style, onClick: props.onClick, spin: props.spin, className: cx('icon', css({ label: 'Icon' }), props.className), icon: icon });
84
+ };
85
+
86
+ /** Call this on your theme after messing with the props. It will re-build things that depend on sizes and colors. */
87
+ const calcDynamicThemeProps = (theme) => {
88
+ theme.controls.border = `${theme.controls.borderWidth} solid ${theme.colors.border}`;
89
+ theme.controls.transition = `box-shadow ${theme.controls.transitionDuration} ${theme.controls.transitionEasing}, opacity ${theme.controls.transitionDuration} ${theme.controls.transitionEasing}, background-color ${theme.controls.transitionDuration} ${theme.controls.transitionEasing}, filter ${theme.controls.transitionDuration} ${theme.controls.transitionEasing}`;
90
+ theme.controls.focusOutlineShadow = `0px 0px 4px 2px ${theme.colors.focusOutline}`;
91
+ theme.controls.focusOutlineRequiredShadow = `0px 0px 4px 2px ${theme.colors.focusOutlineRequired}`;
92
+ theme.controls.dividerBorder = `${theme.controls.dividerThickness} solid ${theme.colors.divider}`;
93
+ theme.controls.inputErrorMinHeight = `calc(${theme.fonts.sizeSmall} * 1.5 + 4px)`;
94
+ theme.mediaQueries.desktop = `@media(min-width:${theme.breakpoints.desktop})`;
95
+ theme.mediaQueries.tablet = `@media(min-width:${theme.breakpoints.tablet})`;
96
+ };
97
+ const defaultTheme = {
98
+ colors: {
99
+ primary: '#7851a9',
100
+ primaryFont: 'rgba(255, 255, 255, 0.9)',
101
+ primary2: '#007bff',
102
+ primary2Font: 'rgba(255, 255, 255, 0.9)',
103
+ secondary: '#9e9e9e',
104
+ secondaryFont: 'rgba(255, 255, 255, 0.9)',
105
+ secondary2Font: 'rgba(255, 255, 255, 0.9)',
106
+ info: '#7851a9',
107
+ infoFont: 'rgba(255, 255, 255, 0.9)',
108
+ warning: '#F5AD94',
109
+ warningFont: 'rgba(0, 0, 0, 0.9)',
110
+ positive: '#28a745',
111
+ positiveFont: 'rgba(255, 255, 255, 0.9)',
112
+ negative: '#dc3545',
113
+ negativeFont: 'rgba(255, 255, 255, 0.9)',
114
+ omg: '#dc3545',
115
+ omgFont: 'rgba(255, 255, 255, 0.9)',
116
+ bg: 'white',
117
+ lightBg: 'rgba(0,0,0,.075)',
118
+ font: 'rgba(0, 0, 0, 0.70)',
119
+ header: '#7851a9',
120
+ headerFont: 'rgba(255, 255, 255, 0.9)',
121
+ link: '#007bff',
122
+ border: 'rgba(0, 0, 0, 0.25)',
123
+ divider: 'rgba(0, 0, 0, 0.50)',
124
+ nav: '#7851a9',
125
+ navFont: 'rgba(255, 255, 255, 0.9)',
126
+ focusOutline: 'rgb(0 188 212 / 75%)',
127
+ progressBg: '#007bff63',
128
+ progressFill: '#007bff',
129
+ modalBg: 'white',
130
+ disabled: 'rgba(0,0,0,.075)',
131
+ textHighlight: 'hsl(54deg 100% 62%)',
132
+ required: '#dc3545',
133
+ focusOutlineRequired: 'rgb(212 28 89 / 75%)',
134
+ backdrop: 'rgba(0, 0, 0, 0.25)',
135
+ pagerBg: 'rgba(0,0,0,.075)'
136
+ },
137
+ fonts: {
138
+ family: 'Arial, Helvetica, sans-serif',
139
+ size: '16px',
140
+ sizeSmall: '0.7rem',
141
+ sizeLarge: '1.3rem',
142
+ headerFamily: 'Arial, Helvetica, sans-serif',
143
+ sizeH1: '2rem',
144
+ sizeH2: '1.5rem',
145
+ sizeH3: '1.17rem',
146
+ sizeH4: '1rem',
147
+ },
148
+ controls: {
149
+ padding: '0.5rem',
150
+ fontSize: '1rem',
151
+ borderWidth: '1px',
152
+ borderRadius: '',
153
+ border: ``,
154
+ height: '44px',
155
+ heightSmall: '34px',
156
+ boxShadow: '2px 2px 4px rgba(0, 0, 0, 0.25)',
157
+ headerBoxShadow: '0px 2px 12px 6px rgba(0, 0, 0, 0.2)',
158
+ buttonBoxShadow: '2px 2px 4px rgba(0, 0, 0, 0.25)',
159
+ hoverBrightness: 'brightness(0.8)',
160
+ hoverBackground: 'rgba(0, 0, 0, 0.1)',
161
+ transitionDuration: '0.25s',
162
+ transitionEasing: 'ease-in-out',
163
+ transition: '',
164
+ focusOutlineShadow: '',
165
+ focusOutlineRequiredShadow: '',
166
+ roundRadius: '3rem',
167
+ disabledOpacity: '0.5',
168
+ formButtonMinWidth: '7rem',
169
+ gap: '1rem',
170
+ dividerMargin: '1rem',
171
+ dividerBorder: '',
172
+ dividerThickness: '2px',
173
+ inputErrorMinHeight: '',
174
+ paragraphPadding: '1rem'
175
+ },
176
+ zIndexes: {
177
+ header: 50,
178
+ backdrop: 100,
179
+ nav: 110,
180
+ flyout: 120,
181
+ modal: 130,
182
+ tooltip: 1000
183
+ },
184
+ layout: {
185
+ headerHeight: '74px',
186
+ headerBodyOffset: '2rem',
187
+ navWidth: '300px'
188
+ },
189
+ breakpoints: {
190
+ desktop: '800px',
191
+ tablet: '768px'
192
+ },
193
+ mediaQueries: {
194
+ desktop: '',
195
+ tablet: '' // set in calcDynamicThemeProps
196
+ },
197
+ timings: {
198
+ nav: {
199
+ slideMs: 250
200
+ }
201
+ }
202
+ };
203
+ calcDynamicThemeProps(defaultTheme);
204
+
205
+ const ThemeContext = createContext(defaultTheme);
206
+ const ThemeProvider = (p) => {
207
+ return (React__default.createElement(ThemeContext.Provider, { value: p.theme }, p.children));
208
+ };
209
+
210
+ /** Returns a user-provided theme if ThemeProvider was used correctly, or the default theme. */
211
+ const useThemeSafely = () => {
212
+ const theme = useContext(ThemeContext);
213
+ // Check for expected prop. If not present, the user did not wrap the app in ThemeProvider
214
+ // OR did not pass the default theme as the base.
215
+ if (theme && theme.breakpoints) {
216
+ return theme;
217
+ }
218
+ return defaultTheme;
219
+ };
220
+
221
+ const getTagStyles = (theme, tag) => {
222
+ switch (tag) {
223
+ case 'p': return {
224
+ marginTop: theme.controls.paragraphPadding,
225
+ marginBottom: theme.controls.paragraphPadding
226
+ };
227
+ case 'h1': return {
228
+ fontSize: theme.fonts.sizeH1
229
+ };
230
+ case 'h2': return {
231
+ fontSize: theme.fonts.sizeH2
232
+ };
233
+ case 'h3': return {
234
+ fontSize: theme.fonts.sizeH3
235
+ };
236
+ case 'h4': return {
237
+ fontSize: theme.fonts.sizeH4
238
+ };
239
+ default: return undefined;
240
+ }
241
+ };
242
+ const headerRegex = /h1|h2|h3|h4/;
243
+ const alignStyles = {
244
+ 'center': { textAlign: 'center' },
245
+ 'left': { textAlign: 'left' },
246
+ 'right': { textAlign: 'right' }
247
+ };
248
+ /** Wraps common needs for displaying text. Use for all text-containing elements to save on duplicated styling. */
249
+ const Text = (props) => {
250
+ var _a, _b;
251
+ const theme = useThemeSafely();
252
+ const tagChoice = props.tag || 'p';
253
+ const style = props.style || {};
254
+ if (props.lineClamp) {
255
+ style.WebkitLineClamp = props.lineClamp;
256
+ }
257
+ if (props.leftPad) {
258
+ style.paddingLeft = props.leftPad;
259
+ }
260
+ let fontWeight;
261
+ if (props.bold) {
262
+ fontWeight = 'bold';
263
+ }
264
+ else if (props.bold === false) {
265
+ fontWeight = 'normal';
266
+ }
267
+ const styles = css(getTagStyles(theme, tagChoice), {
268
+ userSelect: 'text',
269
+ label: 'Text',
270
+ fontSize: props.fontSize ? props.fontSize : undefined
271
+ }, alignStyles[(_a = props.align) !== null && _a !== void 0 ? _a : 'left'], props.smaller && { fontSize: theme.fonts.sizeSmall }, props.larger && { fontSize: theme.fonts.sizeLarge }, props.italics && { fontStyle: 'italic' }, props.ellipsis && {
272
+ overflow: 'hidden',
273
+ whiteSpace: 'nowrap',
274
+ textOverflow: 'ellipsis'
275
+ }, props.lineClamp && {
276
+ WebkitBoxOrient: 'vertical',
277
+ overflow: 'hidden',
278
+ textOverflow: 'ellipsis',
279
+ display: '-webkit-box'
280
+ }, props.spacedOut && { lineHeight: '1.5rem' }, { fontWeight }, props.noPad && { margin: 0, padding: 0 }, headerRegex.test((_b = props.tag) !== null && _b !== void 0 ? _b : '') && {
281
+ fontFamily: theme.fonts.headerFamily
282
+ });
283
+ return React.createElement(tagChoice, {
284
+ style: style,
285
+ className: cx('text', styles, props.className)
286
+ }, props.children);
287
+ };
288
+
289
+ //TB: FUTURE de-dup these styles. create individual styles and compose them manually.
290
+ const Button = React.forwardRef((props, ref) => {
291
+ var _a;
292
+ const { variant, round, rightIcon, leftIcon, iconBlock, small, readOnly, waiting, enforceMinWidth, controlAlign } = props, nativeProps = __rest(props, ["variant", "round", "rightIcon", "leftIcon", "iconBlock", "small", "readOnly", "waiting", "enforceMinWidth", "controlAlign"]);
293
+ const theme = useThemeSafely();
294
+ const buttonStyles = css `
295
+ padding-left: ${theme.controls.padding};
296
+ padding-right: ${theme.controls.padding};
297
+ background-color: white;
298
+ border: ${theme.controls.border};
299
+ border-radius: ${theme.controls.borderRadius};
300
+ cursor: pointer;
301
+ box-shadow: ${theme.controls.buttonBoxShadow};
302
+ color: ${theme.colors.font};
303
+ height: ${theme.controls.height};
304
+ transition: ${theme.controls.transition};
305
+ font-size: 1rem;
306
+ font-weight: bold;
307
+ flex-shrink: 0;
308
+ min-width: ${theme.controls.height};
309
+
310
+ &:disabled {
311
+ opacity: ${theme.controls.disabledOpacity};
312
+ cursor: not-allowed;
313
+ }
314
+
315
+ &:focus {
316
+ outline: none;
317
+ box-shadow: ${theme.controls.focusOutlineShadow};
318
+ position: relative;
319
+ z-index: 2;
320
+ }
321
+
322
+ &:active {
323
+ box-shadow: none;
324
+ }
325
+
326
+ &:hover:not(:disabled) {
327
+ filter: ${theme.controls.hoverBrightness};
328
+ }
329
+ `;
330
+ const styles = css `
331
+ ${buttonStyles}
332
+ ${props.variant === 'circle' && `
333
+ width: ${theme.controls.height};
334
+ border-radius: 100%;
335
+ display: flex;
336
+ justify-content: center;
337
+ align-items: center;
338
+ ${props.small && `
339
+ width: ${theme.controls.heightSmall};
340
+ min-width: ${theme.controls.heightSmall};
341
+ `}
342
+ `}
343
+ ${props.variant === 'icon' && `
344
+ width: ${theme.controls.height};
345
+ border-radius: 100%;
346
+ padding: 0;
347
+ box-shadow: none;
348
+ border: none;
349
+ font-size: 1.6rem;
350
+ display: flex;
351
+ justify-content: center;
352
+ align-items: center;
353
+ background-color: transparent;
354
+ &:hover:not(:disabled) {
355
+ background-color: ${theme.controls.hoverBackground};
356
+ }
357
+ ${props.small && `
358
+ width: ${theme.controls.heightSmall};
359
+ min-width: ${theme.controls.heightSmall};
360
+ font-size: 1.3rem;
361
+ `}
362
+ `}
363
+ ${(props.variant === 'label') && `
364
+ display: inline-block;
365
+ width: auto;
366
+ box-shadow: none;
367
+ border: none;
368
+ background-color: transparent;
369
+ &:hover:not(:disabled) {
370
+ background-color: ${theme.controls.hoverBackground};
371
+ }
372
+ `}
373
+ ${(props.variant === 'text') && `
374
+ background-color: transparent;
375
+ font-weight: normal;
376
+ cursor: auto;
377
+ display: inline-block;
378
+ width: auto;
379
+ box-shadow: none;
380
+ border: none;
381
+ line-height: ${theme.controls.height};
382
+ &:hover:not(:disabled) {
383
+ filter: none;
384
+ }
385
+ `}
386
+ ${props.variant === 'link' && `
387
+ padding: 0;
388
+ display: inline-block;
389
+ width: auto;
390
+ box-shadow: none;
391
+ border: none;
392
+ color: ${theme.colors.link};
393
+ font-weight: normal;
394
+ background-color: transparent;
395
+ &:hover {
396
+ text-decoration: underline;
397
+ }
398
+ &:focus {
399
+ box-shadow: none;
400
+ text-decoration: underline;
401
+ }
402
+ `}
403
+ ${props.variant === 'inlineLink' && `
404
+ padding: 0;
405
+ display: inline-block;
406
+ width: auto;
407
+ box-shadow: none;
408
+ border: none;
409
+ color: ${theme.colors.link};
410
+ font-weight: normal;
411
+ height: auto;
412
+ background-color: transparent;
413
+ &:hover {
414
+ text-decoration: underline;
415
+ }
416
+ &:focus {
417
+ box-shadow: none;
418
+ text-decoration: underline;
419
+ }
420
+ `}
421
+ ${props.variant === 'primary' && `
422
+ background-color: ${theme.colors.primary};
423
+ color: ${theme.colors.primaryFont};
424
+ `}
425
+ ${props.variant === 'primary2' && `
426
+ background-color: ${theme.colors.primary2};
427
+ color: ${theme.colors.primary2Font};
428
+ `}
429
+ ${props.variant === 'secondary' && `
430
+ background-color: ${theme.colors.secondary};
431
+ color: ${theme.colors.secondary2Font};
432
+ `}
433
+ ${props.variant === 'omg' && `
434
+ background-color: ${theme.colors.omg};
435
+ color: ${theme.colors.omgFont};
436
+ `}
437
+ ${props.variant === 'positive' && `
438
+ background-color: ${theme.colors.positive};
439
+ color: ${theme.colors.positiveFont};
440
+ `}
441
+ ${props.variant === 'negative' && `
442
+ background-color: ${theme.colors.negative};
443
+ color: ${theme.colors.negativeFont};
444
+ `}
445
+ ${props.enforceMinWidth && `
446
+ min-width: ${theme.controls.formButtonMinWidth};
447
+ `}
448
+ ${props.readOnly && `
449
+ cursor: default;
450
+ box-shadow: none;
451
+ pointer-events:none;
452
+
453
+ &:hover {
454
+ filter:none;
455
+ }
456
+
457
+ &:focus {
458
+ box-shadow: none;
459
+ }
460
+ `}
461
+ ${props.small && `
462
+ font-size: 0.8rem;
463
+ height:${theme.controls.heightSmall};
464
+ `}
465
+ ${props.round && `
466
+ border-radius: ${theme.controls.roundRadius};
467
+ `}
468
+ ${props.iconBlock && `
469
+ display: flex;
470
+ justify-content: space-between;
471
+ align-items: center;
472
+ `}
473
+ `;
474
+ const disabled = props.disabled || props.waiting;
475
+ let content;
476
+ if (variant === 'text') {
477
+ content = React.createElement(Text, { className: cx(styles, props.className), tag: "div" }, props.children);
478
+ }
479
+ else {
480
+ content = (React.createElement("button", Object.assign({}, nativeProps, { ref: ref, disabled: disabled, className: cx('button', styles, props.className), type: (_a = props.type) !== null && _a !== void 0 ? _a : 'button' }),
481
+ props.leftIcon && React.createElement("span", { className: css({ marginRight: '0.5rem' }) }, props.leftIcon),
482
+ props.waiting ? React.createElement(Icon, { id: "waiting", spin: true }) : props.children,
483
+ props.rightIcon && React.createElement("span", { className: css({ marginLeft: '0.5rem' }) }, props.rightIcon)));
484
+ }
485
+ if (props.controlAlign) {
486
+ return (React.createElement("span", { className: css({
487
+ display: 'inline-block',
488
+ paddingBottom: theme.controls.inputErrorMinHeight
489
+ }) }, content));
490
+ }
491
+ return content;
492
+ });
493
+
494
+ const accordianExpandTimeMs = 250;
495
+ const accordianMaxHeight = 1020;
496
+ const accordianTimingFunction = 'ease-in-out';
497
+ // we need to apply the seperately so stuff doesn't hang over during expand/collapse.
498
+ // if we remove this and just keep overflow:hidden, autocompletes will get cut off in the subpanels.
499
+ const visibleStyle = css({
500
+ overflow: 'visible !important'
501
+ });
502
+ const Accordian = (props) => {
503
+ var _a, _b, _c, _d;
504
+ const [open, setOpen] = React.useState(false);
505
+ const theme = useThemeSafely();
506
+ const content = React.useRef(null);
507
+ const contentStyles = css({
508
+ overflow: 'hidden',
509
+ maxHeight: 0,
510
+ transition: `max-height ${(_a = props.expandTimeMs) !== null && _a !== void 0 ? _a : accordianExpandTimeMs}ms ${(_b = props.transitionTimingFunction) !== null && _b !== void 0 ? _b : accordianTimingFunction}`
511
+ });
512
+ const expandedContentStyles = css({
513
+ maxHeight: (_c = props.maxHeight) !== null && _c !== void 0 ? _c : accordianMaxHeight
514
+ });
515
+ const expandedContentWrapperStyles = !props.noPad ? css({
516
+ padding: '0 1rem 1rem 1rem'
517
+ }) : undefined;
518
+ React.useEffect(() => {
519
+ const currentContent = content.current;
520
+ if (currentContent) {
521
+ if (open) {
522
+ currentContent.classList.add(expandedContentStyles);
523
+ window.setTimeout(() => {
524
+ currentContent.classList.add(visibleStyle);
525
+ }, accordianExpandTimeMs);
526
+ }
527
+ else {
528
+ currentContent.classList.remove(visibleStyle, expandedContentStyles);
529
+ }
530
+ }
531
+ }, [open]);
532
+ React.useEffect(() => {
533
+ var _a;
534
+ if (props.open === undefined) {
535
+ // technically, we only need to use this effect if props.open was initialized with a boolean.
536
+ // you can't have conditional effects so here we go...
537
+ return;
538
+ }
539
+ setOpen((_a = props.open) !== null && _a !== void 0 ? _a : false);
540
+ }, [props.open]);
541
+ return (React.createElement("div", { className: "accordian" },
542
+ React.createElement(Button, { readOnly: props.disabled, variant: props.variant, className: cx(css({
543
+ display: 'flex',
544
+ alignItems: 'center',
545
+ justifyContent: 'space-between',
546
+ height: 'auto',
547
+ minHeight: theme.controls.height,
548
+ width: ((_d = props.block) !== null && _d !== void 0 ? _d : true) ? '100%' : 'auto'
549
+ }, props.className)), onClick: e => {
550
+ e.stopPropagation();
551
+ if (props.onChange) {
552
+ props.onChange(!open);
553
+ }
554
+ else {
555
+ setOpen(!open);
556
+ }
557
+ }, rightIcon: !props.disabled ? React.createElement(Icon, { id: open ? 'collapse' : 'expand' }) : undefined },
558
+ React.createElement("span", null, props.header)),
559
+ React.createElement("div", { ref: content, className: cx('accordian__body', contentStyles) },
560
+ React.createElement("div", { className: expandedContentWrapperStyles }, props.children))));
561
+ };
562
+ const useAccordianState = (count, openIndex) => {
563
+ const [panels, setShowPanel] = React.useState(new Array(count).fill(false).map((b, i) => {
564
+ return i === openIndex;
565
+ }));
566
+ return [
567
+ panels,
568
+ (index, open) => {
569
+ setShowPanel(previousState => {
570
+ const newState = previousState.slice().fill(false);
571
+ newState[index] = open;
572
+ return newState;
573
+ });
574
+ }
575
+ ];
576
+ };
577
+
578
+ const DEFAULT_DEBOUNCE_MS = 250;
579
+ const NUMBER_REGEX = /^-?\d+\.?\d*$/;
580
+ const DATE_REGEX = /(\d{1,2})(?:\/|-)(\d{1,2})(?:\/|-)(\d{4})/;
581
+ const DEFAULT_MAX_LENGTH = 100;
582
+ const formatLocalValue = (value, type) => {
583
+ let newValue = '';
584
+ if (value !== undefined && value !== null) {
585
+ if (type === 'date' && typeof value === 'number') {
586
+ newValue = format(value, 'MM/dd/yyyy');
587
+ }
588
+ else {
589
+ newValue = value;
590
+ }
591
+ }
592
+ return newValue;
593
+ };
594
+ /** @deprecated Use DateInput, NumberInput, or TextInput instead. */
595
+ const Input = React.forwardRef((props, ref) => {
596
+ var _a, _b, _c;
597
+ const [localValue, setLocalValue] = React.useState(formatLocalValue(props.value, props.type));
598
+ const debounceMs = (_a = props.debounceMs) !== null && _a !== void 0 ? _a : DEFAULT_DEBOUNCE_MS;
599
+ const autoComplete = (_b = props.autoComplete) !== null && _b !== void 0 ? _b : 'off';
600
+ const vars = React.useRef({
601
+ wrappedOnChange: (props.onChange && debounceMs) ? debounce((value, name) => {
602
+ var _a;
603
+ (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, value, name);
604
+ }, debounceMs) : undefined,
605
+ focused: false
606
+ });
607
+ const outerOnChange = (_c = vars.current.wrappedOnChange) !== null && _c !== void 0 ? _c : props.onChange;
608
+ const trySyncLocalValue = () => {
609
+ if (vars.current.focused) {
610
+ return;
611
+ }
612
+ if (props.value === localValue) {
613
+ return;
614
+ }
615
+ const newValue = formatLocalValue(props.value, props.type);
616
+ setLocalValue(newValue);
617
+ };
618
+ React.useEffect(() => {
619
+ trySyncLocalValue();
620
+ }, [props.value]);
621
+ const theme = useThemeSafely();
622
+ const inputStyles = css({
623
+ fontFamily: theme.fonts.family,
624
+ fontSize: theme.fonts.size,
625
+ width: '100%',
626
+ border: theme.controls.border,
627
+ borderRadius: theme.controls.borderRadius,
628
+ color: theme.colors.font,
629
+ paddingLeft: theme.controls.padding,
630
+ paddingRight: theme.controls.padding,
631
+ height: theme.controls.height,
632
+ transition: theme.controls.transition,
633
+ ':focus': {
634
+ outline: 'none',
635
+ boxShadow: theme.controls.focusOutlineShadow
636
+ },
637
+ ':disabled': {
638
+ backgroundColor: theme.colors.disabled,
639
+ cursor: 'not-allowed'
640
+ },
641
+ ':invalid': {
642
+ borderColor: theme.colors.required,
643
+ ':focus': {
644
+ boxShadow: theme.controls.focusOutlineRequiredShadow
645
+ }
646
+ },
647
+ }, props.round && props.type !== 'textarea' && {
648
+ borderRadius: theme.controls.roundRadius,
649
+ paddingLeft: `calc(${theme.controls.padding} * 2)`,
650
+ paddingRight: `calc(${theme.controls.padding} * 2)`
651
+ }, props.readOnly && {
652
+ backgroundColor: 'transparent',
653
+ cursor: 'default',
654
+ border: 'none',
655
+ ':focus': {
656
+ outline: 'none',
657
+ boxShadow: 'none'
658
+ }
659
+ }, props.rightControl && props.type !== 'textarea' && {
660
+ paddingRight: theme.controls.height
661
+ });
662
+ let inputElement;
663
+ const onFocus = (e) => {
664
+ var _a;
665
+ vars.current.focused = true;
666
+ (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
667
+ };
668
+ const onBlur = (e) => {
669
+ var _a;
670
+ vars.current.focused = false;
671
+ trySyncLocalValue();
672
+ (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
673
+ };
674
+ let localOnChange;
675
+ if (props.type === 'number') {
676
+ localOnChange = e => {
677
+ const value = e.target.value;
678
+ if (NUMBER_REGEX.test(value)) {
679
+ let numValue = parseFloat(value);
680
+ if (props.min && numValue < props.min) {
681
+ numValue = props.min;
682
+ }
683
+ if (props.max && numValue > props.max) {
684
+ numValue = props.max;
685
+ }
686
+ setLocalValue(numValue);
687
+ outerOnChange === null || outerOnChange === void 0 ? void 0 : outerOnChange(numValue, props.name, e);
688
+ }
689
+ else if (!value) {
690
+ setLocalValue(value);
691
+ outerOnChange === null || outerOnChange === void 0 ? void 0 : outerOnChange(undefined, props.name, e);
692
+ }
693
+ };
694
+ }
695
+ else if (props.type === 'date') {
696
+ localOnChange = e => {
697
+ const value = e.target.value;
698
+ setLocalValue(value);
699
+ if (outerOnChange) {
700
+ const dateParts = DATE_REGEX.exec(value);
701
+ if (!dateParts) {
702
+ outerOnChange(undefined, props.name, e);
703
+ }
704
+ else {
705
+ const year = parseInt(dateParts[3], 10);
706
+ const month = parseInt(dateParts[1], 10);
707
+ const day = parseInt(dateParts[2], 10);
708
+ if (isExists(year, month, day)) {
709
+ let ms = new Date(year, month - 1, day).valueOf();
710
+ if (props.min && ms < props.min) {
711
+ ms = props.min;
712
+ }
713
+ if (props.max && ms > props.max) {
714
+ ms = props.max;
715
+ }
716
+ outerOnChange(ms, props.name, e);
717
+ }
718
+ else {
719
+ outerOnChange(undefined, props.name, e);
720
+ }
721
+ }
722
+ }
723
+ };
724
+ }
725
+ else {
726
+ localOnChange = e => {
727
+ const value = e.target.value;
728
+ setLocalValue(value);
729
+ outerOnChange === null || outerOnChange === void 0 ? void 0 : outerOnChange(value, props.name, e);
730
+ };
731
+ }
732
+ if (props.type === 'number') {
733
+ inputElement = React.createElement("input", Object.assign({}, props.inputAriaAttributes, { autoFocus: props.autoFocus, ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly,
734
+ // set fixed default to defeat pasting stupid numbers
735
+ maxLength: 50, min: props.min, max: props.max, required: props.required, disabled: props.disabled, id: props.id, className: cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: "number", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress }));
736
+ }
737
+ else if (props.type === 'date') {
738
+ inputElement = React.createElement("input", Object.assign({}, props.inputAriaAttributes, { autoFocus: props.autoFocus, ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: 10, required: props.required, disabled: props.disabled, id: props.id, className: cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: "text", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress }));
739
+ }
740
+ else if (props.type === 'textarea') {
741
+ inputElement = React.createElement("textarea", Object.assign({}, props.inputAriaAttributes, { autoFocus: props.autoFocus, ref: ref, name: props.name, style: props.style, rows: props.rows || 10, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, className: cx(css `
742
+ ${inputStyles}
743
+ max-width: 100%;
744
+ min-height: ${theme.controls.height};
745
+ padding-top: 0.75rem;
746
+ height:auto;`, props.inputClassName), placeholder: props.placeholder, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress }));
747
+ }
748
+ else {
749
+ // text, password, email, and url
750
+ inputElement = React.createElement("input", Object.assign({}, props.inputAriaAttributes, { autoFocus: props.autoFocus, ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, className: cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: props.type, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress }));
751
+ }
752
+ const inputWrapperStyles = css `
753
+ width:100%;
754
+ ${props.rightControl && `
755
+ position: relative;
756
+ `}
757
+ `;
758
+ const rightControlStyles = props.rightControl && css `
759
+ position: absolute;
760
+ right: ${theme.controls.padding};
761
+ top: 0;
762
+ bottom: 0;
763
+ display: flex;
764
+ align-items: center;
765
+ ${props.round && `
766
+ right: calc(${theme.controls.padding} * 2);
767
+ `}
768
+ `;
769
+ return (React.createElement("div", { className: cx('input', inputWrapperStyles, props.className) },
770
+ inputElement,
771
+ props.rightControl && props.type !== 'textarea' && React.createElement("div", { className: rightControlStyles }, props.rightControl)));
772
+ });
773
+
774
+ const List = React.forwardRef((props, ref) => {
775
+ const children = props.items ? props.items.map((item, i) => React.createElement(ListItem, { key: i }, item)) : props.children;
776
+ const theme = useThemeSafely();
777
+ const listProps = __rest(props, ["altRowColor", "noLines", "items"]);
778
+ const listStyles = css `
779
+ margin: 0;
780
+ padding: 0;
781
+ list-style-type: none;
782
+ ${props.altRowColor && `
783
+ > .listItem:nth-of-type(even) {
784
+ background-color: ${theme.colors.lightBg};
785
+ }
786
+ `}
787
+ ${props.noLines && `
788
+ > .listItem {
789
+ border-bottom: none;
790
+ }
791
+ `}
792
+ `;
793
+ return (React.createElement("ul", Object.assign({}, listProps, { ref: ref, className: cx('list', listStyles, props.className) }), children));
794
+ });
795
+ const ListItem = (props) => {
796
+ const liProps = __rest(props, ["variant", "noContentStyling"]);
797
+ const theme = useThemeSafely();
798
+ const itemStyles = css `
799
+ border-bottom: ${theme.controls.border};
800
+ &:last-child {
801
+ border-bottom: none;
802
+ }
803
+ `;
804
+ const contentStyle = css({
805
+ padding: `calc(${theme.controls.padding} * 1.5)`
806
+ }, props.variant === 'full' && {
807
+ padding: 0
808
+ }, !props.noContentStyling && {
809
+ '>.button': {
810
+ padding: `calc(${theme.controls.padding} * 1.5)`,
811
+ width: '100%',
812
+ border: 'none'
813
+ },
814
+ '>.omniLink': {
815
+ paddingLeft: `calc(${theme.controls.padding} * 1.5)`,
816
+ paddingRight: `calc(${theme.controls.padding} * 1.5)`,
817
+ width: '100%',
818
+ border: 'none',
819
+ lineHeight: theme.controls.height
820
+ },
821
+ '>.button:not(:focus), >.omniLink:not(:focus)': {
822
+ boxShadow: 'none',
823
+ },
824
+ '>.omniLink:not(.omniLink--iconBlock)': {
825
+ display: 'block'
826
+ }
827
+ });
828
+ return (React.createElement("li", Object.assign({}, liProps, { className: cx('listItem', itemStyles, props.className) }),
829
+ React.createElement("div", { className: css(contentStyle) }, props.children)));
830
+ };
831
+
832
+ const TabLocker = (props) => {
833
+ const tabLocker = React.useRef(null);
834
+ return (React.createElement("div", { className: "tabLocker", style: props.style, ref: tabLocker, onKeyDown: e => {
835
+ var _a, _b;
836
+ if (props.disabled) {
837
+ return;
838
+ }
839
+ if (e.key === 'Tab') {
840
+ e.preventDefault();
841
+ e.stopPropagation();
842
+ const tabElements = Array.from((_b = (_a = tabLocker.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll('a, button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])')) !== null && _b !== void 0 ? _b : []).filter(el => !el.hasAttribute('disabled'));
843
+ if (tabElements.length) {
844
+ const direction = e.shiftKey ? -1 : 1;
845
+ const index = tabElements.findIndex(x => x === document.activeElement);
846
+ if (index === undefined) {
847
+ tabElements[0].focus();
848
+ }
849
+ else if (index === tabElements.length - 1 && direction === 1) {
850
+ tabElements[0].focus();
851
+ }
852
+ else if (index === 0 && direction === -1) {
853
+ tabElements[tabElements.length - 1].focus();
854
+ }
855
+ else {
856
+ tabElements[index + direction].focus();
857
+ }
858
+ }
859
+ }
860
+ } }, props.children));
861
+ };
862
+
863
+ //TB: FUTURE will need to use the new input
864
+ const defaultMaxShownValues = 7;
865
+ const buttonMarkerClass = 'ListItem__button';
866
+ const Autocomplete = (p) => {
867
+ var _a, _b;
868
+ const theme = useThemeSafely();
869
+ const element = React.useRef(null);
870
+ const input = React.useRef(null);
871
+ const list = React.useRef(null);
872
+ const [values, setValues] = React.useState([]);
873
+ const showValues = React.useMemo(() => values.length > 0, [values]);
874
+ const maxShowValues = (_a = p.maxShownValues) !== null && _a !== void 0 ? _a : defaultMaxShownValues;
875
+ const shownValues = React.useMemo(() => {
876
+ if (!p.allowScroll) {
877
+ return values.slice(0, maxShowValues);
878
+ }
879
+ return values.slice();
880
+ }, [values]);
881
+ const onChangeForOptions = React.useRef(debounce((value) => {
882
+ if (!p.minChars || value.length >= p.minChars) {
883
+ p.getOptions(value)
884
+ .then(vals => {
885
+ setValues(vals);
886
+ }).catch(err => {
887
+ // ignore it
888
+ });
889
+ }
890
+ else {
891
+ setValues([]);
892
+ }
893
+ }, (_b = p.getOptionsDebounceMs) !== null && _b !== void 0 ? _b : 0, { leading: false, trailing: true }));
894
+ const getNextTabElement = (fromIndex, direction) => {
895
+ var _a, _b, _c;
896
+ if (fromIndex === -1) {
897
+ let buttonIndex = 0;
898
+ if (direction === -1) {
899
+ buttonIndex = shownValues.length - 1;
900
+ }
901
+ return (_a = list.current) === null || _a === void 0 ? void 0 : _a.querySelector(`.${buttonMarkerClass}${buttonIndex}`);
902
+ }
903
+ else {
904
+ const nextIndex = fromIndex + direction;
905
+ if (nextIndex >= shownValues.length || nextIndex < 0) {
906
+ return (_b = input.current) !== null && _b !== void 0 ? _b : undefined;
907
+ }
908
+ else {
909
+ return (_c = list.current) === null || _c === void 0 ? void 0 : _c.querySelector(`.${buttonMarkerClass}${nextIndex}`);
910
+ }
911
+ }
912
+ };
913
+ React.useEffect(() => {
914
+ const clearItems = () => {
915
+ if (values.length > 0) {
916
+ setValues([]);
917
+ }
918
+ };
919
+ document.addEventListener('click', clearItems);
920
+ return () => {
921
+ document.removeEventListener('click', clearItems);
922
+ };
923
+ }, [values]);
924
+ let listBorderRadius = '';
925
+ if (p.round || theme.controls.borderRadius) {
926
+ listBorderRadius = theme.controls.borderRadius || '0.5rem';
927
+ }
928
+ return (React.createElement("div", { onClick: e => {
929
+ e.stopPropagation();
930
+ }, onKeyDown: e => {
931
+ var _a;
932
+ if (e.key === 'Escape') {
933
+ setValues([]);
934
+ (_a = input.current) === null || _a === void 0 ? void 0 : _a.focus();
935
+ }
936
+ }, ref: element, className: cx(css({
937
+ position: 'relative',
938
+ width: '100%',
939
+ label: 'Autocomplete'
940
+ }), 'autocomplete') },
941
+ React.createElement(TabLocker, { disabled: !showValues, style: { position: 'relative' } },
942
+ React.createElement(Input, { autoFocus: p.autoFocus, inputAriaAttributes: p.inputAriaAttributes, ref: input, debounceMs: 0, type: "text", value: getAutocompleteValueText(p.value), round: p.round, rightControl: p.rightControl, placeholder: p.placeholder, id: p.id, disabled: p.disabled, className: p.className, inputClassName: p.inputClassName, maxLength: p.maxLength, required: p.required, onChange: v => {
943
+ const value = v;
944
+ p.onChange(value);
945
+ onChangeForOptions.current(value);
946
+ }, onKeyDown: e => {
947
+ var _a, _b, _c;
948
+ if (showValues) {
949
+ if (e.key === 'ArrowDown') {
950
+ e.preventDefault();
951
+ e.stopPropagation();
952
+ (_a = getNextTabElement(-1, 1)) === null || _a === void 0 ? void 0 : _a.focus();
953
+ }
954
+ else if (e.key === 'ArrowUp') {
955
+ e.preventDefault();
956
+ e.stopPropagation();
957
+ (_b = getNextTabElement(-1, -1)) === null || _b === void 0 ? void 0 : _b.focus();
958
+ }
959
+ }
960
+ (_c = p.onKeyDown) === null || _c === void 0 ? void 0 : _c.call(p, e);
961
+ }, onKeyPress: e => { var _a; return (_a = p.onKeyPress) === null || _a === void 0 ? void 0 : _a.call(p, e); } }),
962
+ showValues && (React.createElement(List, { ref: list, className: cx(css({
963
+ position: 'absolute',
964
+ width: '100%',
965
+ border: theme.controls.border,
966
+ borderRadius: listBorderRadius,
967
+ boxShadow: theme.controls.boxShadow,
968
+ backgroundColor: theme.colors.bg,
969
+ marginTop: `-4px !important`,
970
+ zIndex: theme.zIndexes.backdrop,
971
+ 'li:first-child button': {
972
+ borderTopRightRadius: listBorderRadius,
973
+ borderTopLeftRadius: listBorderRadius,
974
+ },
975
+ 'li:last-child button': {
976
+ borderBottomRightRadius: listBorderRadius,
977
+ borderBottomLeftRadius: listBorderRadius,
978
+ }
979
+ }), p.allowScroll && shownValues.length > maxShowValues && css({
980
+ overflowY: 'scroll',
981
+ maxHeight: `calc(${theme.controls.height} * ${maxShowValues})`
982
+ })) },
983
+ shownValues.map((value, listItemIndex) => {
984
+ return (React.createElement(ListItem, { key: getAutocompleteValueId(value), variant: "full" },
985
+ React.createElement(Button, { onKeyDown: e => {
986
+ var _a, _b;
987
+ if (e.key === 'ArrowDown') {
988
+ e.stopPropagation();
989
+ e.preventDefault();
990
+ (_a = getNextTabElement(listItemIndex, 1)) === null || _a === void 0 ? void 0 : _a.focus();
991
+ }
992
+ else if (e.key === 'ArrowUp') {
993
+ e.stopPropagation();
994
+ e.preventDefault();
995
+ (_b = getNextTabElement(listItemIndex, -1)) === null || _b === void 0 ? void 0 : _b.focus();
996
+ }
997
+ }, className: cx(buttonMarkerClass + listItemIndex, css({
998
+ borderRadius: 0,
999
+ })), onClick: () => {
1000
+ p.onPick(value);
1001
+ setValues([]);
1002
+ setTimeout(() => {
1003
+ var _a;
1004
+ // we need to wait until the component is re-rendered.
1005
+ // outside changes to Inputs will be ignored if the component has focus.
1006
+ (_a = input.current) === null || _a === void 0 ? void 0 : _a.focus();
1007
+ }, 0);
1008
+ } },
1009
+ React.createElement(Text, { tag: "div", ellipsis: true, align: "left" }, getAutocompleteValueText(value)))));
1010
+ }),
1011
+ !p.allowScroll && shownValues.length < values.length && (React.createElement(ListItem, null,
1012
+ React.createElement(Text, { tag: "div", italics: true, align: "center" },
1013
+ "Showing ",
1014
+ shownValues.length.toLocaleString(),
1015
+ " of ",
1016
+ values.length.toLocaleString(),
1017
+ " results."))))))));
1018
+ };
1019
+ const getAutocompleteValueText = (v) => {
1020
+ if (!v) {
1021
+ return '';
1022
+ }
1023
+ if (typeof v === 'string') {
1024
+ return v;
1025
+ }
1026
+ return v.name;
1027
+ };
1028
+ const getAutocompleteValueId = (v) => {
1029
+ if (typeof v === 'string') {
1030
+ return v;
1031
+ }
1032
+ return v.id;
1033
+ };
1034
+
1035
+ /** @deprecated Use Backdrop2 going forward. */
1036
+ const Backdrop$1 = (props) => {
1037
+ var _a;
1038
+ const showTimeMs = (_a = props.showTimeMs) !== null && _a !== void 0 ? _a : 250;
1039
+ const backdropId = React.useRef('Backdrop' + nanoid());
1040
+ const theme = useThemeSafely();
1041
+ const backdropStyles = css `
1042
+ opacity: 0;
1043
+ position: fixed;
1044
+ top: 0;
1045
+ left: 0;
1046
+ right: 0;
1047
+ bottom: 0;
1048
+ background-color: ${theme.colors.backdrop};
1049
+ transition: opacity ${showTimeMs}ms ease-in-out;
1050
+ visibility: hidden;
1051
+ user-select: none;
1052
+ -webkit-tap-highlight-color: transparent;
1053
+ `;
1054
+ const showStyles = css `
1055
+ z-index: ${theme.zIndexes.backdrop} !important;
1056
+ opacity: 1.0 !important;
1057
+ label:${backdropId.current};
1058
+ `;
1059
+ const bodyStyles = css `
1060
+ overflow: hidden !important;
1061
+ label:${backdropId.current};
1062
+ `;
1063
+ const bodyResponsiveStyles = css `
1064
+ label:${backdropId.current};
1065
+ @media(min-width:${theme.breakpoints.desktop}) {
1066
+ overflow: auto !important;
1067
+ }
1068
+ `;
1069
+ const bodyReverseResponsiveStyles = css `
1070
+ ${bodyStyles}
1071
+ overflow: auto !important;
1072
+ @media(min-width:${theme.breakpoints.desktop}) {
1073
+ overflow: hidden !important;
1074
+ }
1075
+ `;
1076
+ const styles = css `
1077
+ ${backdropStyles}
1078
+ ${props.onClick && `
1079
+ cursor: pointer;
1080
+ `}
1081
+ ${props.transparent && `
1082
+ background-color: transparent;
1083
+ transition: none;
1084
+ `}
1085
+ ${props.children && `
1086
+ display: flex;
1087
+ justify-content:center;
1088
+ align-items: center;
1089
+ `}
1090
+ ${props.responsive && `
1091
+ @media(min-width:${theme.breakpoints.desktop}) {
1092
+ display: none;
1093
+ }
1094
+ `}
1095
+ ${props.reverseResponsive && `
1096
+ display: none;
1097
+ @media(min-width:${theme.breakpoints.desktop}) {
1098
+ display: flex;
1099
+ }
1100
+ `}
1101
+ `;
1102
+ const backdrop = React.useRef(null);
1103
+ React.useEffect(() => {
1104
+ if (backdrop && backdrop.current) {
1105
+ if (props.show && backdrop.current.style.visibility !== 'visible') {
1106
+ backdrop.current.style.visibility = 'visible';
1107
+ backdrop.current.classList.add(showStyles);
1108
+ if (!props.allowScroll) {
1109
+ document.body.classList.add(bodyStyles);
1110
+ if (props.responsive) {
1111
+ document.body.classList.add(bodyResponsiveStyles);
1112
+ }
1113
+ else if (props.reverseResponsive) {
1114
+ document.body.classList.add(bodyReverseResponsiveStyles);
1115
+ }
1116
+ }
1117
+ }
1118
+ else if (!props.show && backdrop.current.style.visibility === 'visible') {
1119
+ backdrop.current.classList.remove(showStyles);
1120
+ if (backdrop && backdrop.current) {
1121
+ backdrop.current.style.visibility = 'hidden';
1122
+ if (!props.allowScroll) {
1123
+ document.body.classList.remove(bodyStyles);
1124
+ if (props.responsive) {
1125
+ document.body.classList.remove(bodyResponsiveStyles);
1126
+ }
1127
+ else if (props.reverseResponsive) {
1128
+ document.body.classList.remove(bodyReverseResponsiveStyles);
1129
+ }
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1134
+ return () => {
1135
+ if (backdrop && backdrop.current && !props.allowScroll) {
1136
+ document.body.classList.remove(bodyStyles);
1137
+ }
1138
+ };
1139
+ }, [props.show]);
1140
+ return (React.createElement("div", { onMouseDown: e => {
1141
+ var _a;
1142
+ e.stopPropagation();
1143
+ e.preventDefault();
1144
+ (_a = props.onClick) === null || _a === void 0 ? void 0 : _a.call(props);
1145
+ }, onClick: e => {
1146
+ e.stopPropagation();
1147
+ e.preventDefault();
1148
+ }, ref: backdrop, className: cx('backdrop', styles, props.className) }, props.children));
1149
+ };
1150
+
1151
+ /** Add to fixed positioned elements so their contents do not jump around when scrolling is disabled. */
1152
+ const modalScrollFixClassName = 'modal-scroll-fix';
1153
+
1154
+ /** useEffect but ignores the first call on component mount. */
1155
+ const useIgnoreMount = (effect, deps) => {
1156
+ const mounted = React__default.useRef(false);
1157
+ React__default.useEffect(() => {
1158
+ if (!mounted.current) {
1159
+ mounted.current = true;
1160
+ }
1161
+ else {
1162
+ effect();
1163
+ }
1164
+ }, deps);
1165
+ };
1166
+
1167
+ const useLogger = (componentName, enabled) => {
1168
+ return (...messages) => {
1169
+ if (enabled) {
1170
+ // tslint:disable-next-line
1171
+ console.log(`[${componentName}]`, ...messages);
1172
+ }
1173
+ };
1174
+ };
1175
+
1176
+ const portalId = 'backdrop';
1177
+ const BackdropContext = React__default.createContext({
1178
+ showing: false,
1179
+ showCount: 0,
1180
+ portalId: portalId,
1181
+ setShow: (s, f) => {
1182
+ /* empty */
1183
+ }
1184
+ });
1185
+ const BackdropContextProvider = (p) => {
1186
+ var _a;
1187
+ const [showCount, setShowCount] = useState(0);
1188
+ const log = useLogger('BackdropContextProvider', (_a = p.__debug) !== null && _a !== void 0 ? _a : false);
1189
+ if (p.__debug) {
1190
+ useEffect(() => {
1191
+ log('mounted');
1192
+ return () => {
1193
+ log('un-mounted');
1194
+ };
1195
+ }, []);
1196
+ useIgnoreMount(() => {
1197
+ log('showCount changed', showCount);
1198
+ }, [showCount]);
1199
+ }
1200
+ return (React__default.createElement(BackdropContext.Provider, { value: {
1201
+ portalId: portalId,
1202
+ showing: showCount > 0,
1203
+ showCount: showCount,
1204
+ setShow: (show, from) => {
1205
+ if (show) {
1206
+ setShowCount(s => {
1207
+ const count = s + 1;
1208
+ log(`setShow from ${from} ${s} -> ${count}`);
1209
+ return count;
1210
+ });
1211
+ }
1212
+ else {
1213
+ setShowCount(s => {
1214
+ const count = Math.max(0, s - 1);
1215
+ log(`setShow from ${from} ${s} -> ${count}`);
1216
+ return count;
1217
+ });
1218
+ }
1219
+ }
1220
+ } },
1221
+ p.children,
1222
+ p.__debug && (React__default.createElement("p", { className: cx(modalScrollFixClassName, css({
1223
+ position: 'fixed',
1224
+ top: 0, right: 0,
1225
+ backgroundColor: '#ff00004f',
1226
+ color: 'white',
1227
+ padding: '0.5rem',
1228
+ margin: 0,
1229
+ zIndex: 9999
1230
+ })) },
1231
+ "Backdrop showCount: ",
1232
+ showCount))));
1233
+ };
1234
+ const BackdropOverlay = (p) => {
1235
+ var _a, _b;
1236
+ const context = useContext(BackdropContext);
1237
+ const theme = useThemeSafely();
1238
+ const showTimeMs = (_a = p.showTimeMs) !== null && _a !== void 0 ? _a : 250;
1239
+ const log = useLogger('BackdropOverlay', (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
1240
+ if (p.__debug) {
1241
+ useEffect(() => {
1242
+ log('mounted');
1243
+ return () => {
1244
+ log('unmounted');
1245
+ };
1246
+ }, []);
1247
+ useIgnoreMount(() => {
1248
+ log('context.showing changed', context.showing);
1249
+ }, [context.showing]);
1250
+ }
1251
+ return (React__default.createElement("div", { onClick: () => {
1252
+ context === null || context === void 0 ? void 0 : context.setShow(false, 'BackdropOverlay');
1253
+ log('onClick', 'setShow', false);
1254
+ }, id: context === null || context === void 0 ? void 0 : context.portalId, className: css({
1255
+ cursor: 'pointer',
1256
+ position: 'fixed',
1257
+ top: 0, right: 0, bottom: 0, left: 0,
1258
+ backgroundColor: theme.colors.backdrop,
1259
+ zIndex: (context === null || context === void 0 ? void 0 : context.showing) ? theme.zIndexes.backdrop : -1,
1260
+ visibility: (context === null || context === void 0 ? void 0 : context.showing) ? 'visible' : 'hidden',
1261
+ opacity: (context === null || context === void 0 ? void 0 : context.showing) ? 1 : 0,
1262
+ transition: `opacity ${showTimeMs}ms ease-in-out`,
1263
+ userSelect: 'none',
1264
+ WebkitTapHighlightColor: 'transparent'
1265
+ }) }));
1266
+ };
1267
+ const Backdrop = (p) => {
1268
+ return (React__default.createElement(BackdropContextProvider, { __debug: p.__debug },
1269
+ React__default.createElement("div", { className: css({
1270
+ height: '100%'
1271
+ }) },
1272
+ p.children,
1273
+ React__default.createElement(BackdropOverlay, { showTimeMs: p.showTimeMs, __debug: p.__debug }))));
1274
+ };
1275
+
1276
+ const Calendar = (p) => {
1277
+ var _a, _b, _c, _d;
1278
+ const theme = useThemeSafely();
1279
+ const cellSize = (_a = p.cellSize) !== null && _a !== void 0 ? _a : '3rem';
1280
+ const showTitle = (_b = p.title) !== null && _b !== void 0 ? _b : true;
1281
+ const calendarStyles = css({
1282
+ display: 'grid',
1283
+ gridTemplateColumns: `repeat(7, ${cellSize})`,
1284
+ });
1285
+ const cellStyles = css({
1286
+ border: 'none',
1287
+ backgroundColor: 'transparent',
1288
+ fontFamily: theme.fonts.family,
1289
+ fontSize: theme.fonts.size,
1290
+ color: theme.colors.font,
1291
+ display: 'flex',
1292
+ alignItems: 'center',
1293
+ justifyContent: 'center',
1294
+ borderTop: theme.controls.border,
1295
+ borderLeft: theme.controls.border,
1296
+ width: cellSize,
1297
+ height: cellSize
1298
+ });
1299
+ const rightCellStyles = css({
1300
+ borderRight: theme.controls.border
1301
+ });
1302
+ const bottomCellStyles = css({
1303
+ borderBottom: theme.controls.border
1304
+ });
1305
+ const nonDayStyles = css({
1306
+ backgroundColor: theme.colors.disabled
1307
+ });
1308
+ const clickableStyles = css({
1309
+ cursor: 'pointer',
1310
+ ':focus': {
1311
+ outline: 'none',
1312
+ boxShadow: theme.controls.focusOutlineShadow,
1313
+ position: 'relative',
1314
+ zIndex: 2
1315
+ },
1316
+ ":active": {
1317
+ boxShadow: 'none'
1318
+ },
1319
+ ":hover": {
1320
+ backgroundColor: theme.colors.lightBg,
1321
+ color: theme.colors.font,
1322
+ }
1323
+ });
1324
+ const monthStart = new Date(p.year, p.month - 1, 1);
1325
+ const days = getDaysInMonth(monthStart);
1326
+ const startDayOfWeek = getDay(monthStart);
1327
+ const dayEndIndex = startDayOfWeek + days - 1;
1328
+ const cellCount = p.fixedRows ? 42 : Math.ceil((days + startDayOfWeek) / 7) * 7;
1329
+ const bottomRowStartIndex = cellCount - 7;
1330
+ const currentDate = p.showCurrent ? new Date() : undefined;
1331
+ const selectedDate = p.selectedValue && isSameMonth(p.selectedValue, monthStart) ? new Date(p.selectedValue) : undefined;
1332
+ const cells = [];
1333
+ for (let i = 0; i < cellCount; i++) {
1334
+ const dayOfMonth = i >= startDayOfWeek && i <= dayEndIndex ? i - startDayOfWeek + 1 : undefined;
1335
+ const cellDate = dayOfMonth ? new Date(p.year, p.month - 1, dayOfMonth) : undefined;
1336
+ let canInteract = !!cellDate;
1337
+ if (p.min && isBefore(cellDate !== null && cellDate !== void 0 ? cellDate : 0, p.min)) {
1338
+ canInteract = false;
1339
+ }
1340
+ if (p.max && isAfter(cellDate !== null && cellDate !== void 0 ? cellDate : 0, p.max)) {
1341
+ canInteract = false;
1342
+ }
1343
+ if (cellDate && p.onClick && ((_c = p.isCellDisabled) === null || _c === void 0 ? void 0 : _c.call(p, cellDate))) {
1344
+ canInteract = false;
1345
+ }
1346
+ const styles = css(cellStyles, (i + 1) % 7 === 0 && rightCellStyles, i >= bottomRowStartIndex && bottomCellStyles, !canInteract && nonDayStyles, currentDate && cellDate && isSameDay(currentDate, cellDate) && { fontWeight: 'bold' }, selectedDate && cellDate && isSameDay(selectedDate, cellDate) && { backgroundColor: theme.colors.primary, color: theme.colors.primaryFont }, canInteract && p.onClick && clickableStyles);
1347
+ const onClick = canInteract && p.onClick ? () => {
1348
+ var _a;
1349
+ if (cellDate) {
1350
+ (_a = p.onClick) === null || _a === void 0 ? void 0 : _a.call(p, cellDate);
1351
+ }
1352
+ } : undefined;
1353
+ const cellContent = cellDate && (p.renderCell ? p.renderCell(cellDate) : cellDate.getDate());
1354
+ const cell = onClick ? (React.createElement("button", { type: "button", key: i, className: styles, onClick: onClick }, cellContent)) : (React.createElement("div", { key: i, className: styles }, cellContent));
1355
+ cells.push(cell);
1356
+ }
1357
+ return (React.createElement("div", { id: p.id, className: "calendar" },
1358
+ showTitle && ((_d = p.customTitle) !== null && _d !== void 0 ? _d : React.createElement(Text, { larger: true, align: "center" },
1359
+ format(monthStart, 'LLLL'),
1360
+ p.yearInTitle && ` ${p.year}`)),
1361
+ React.createElement("div", { className: calendarStyles },
1362
+ ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => {
1363
+ return (React.createElement(Text, { style: { paddingBottom: '0.25rem' }, key: day, smaller: p.smallHeader, noPad: true, align: "center" }, day));
1364
+ }),
1365
+ cells)));
1366
+ };
1367
+
1368
+ const Checkbox = (props) => {
1369
+ const inputProps = __rest(props, ["checked", "onChange", "label", "checkedIcon", "uncheckedIcon", "checkedThemeColor", "checkedColor", "readOnly"]);
1370
+ const selected = props.checkedIcon || 'selected';
1371
+ const unselected = props.uncheckedIcon || 'unselected';
1372
+ const theme = useThemeSafely();
1373
+ let finalCheckedColor;
1374
+ if (props.checked) {
1375
+ if (props.checkedThemeColor) {
1376
+ finalCheckedColor = theme.colors[props.checkedThemeColor];
1377
+ }
1378
+ else {
1379
+ finalCheckedColor = props.checkedColor;
1380
+ }
1381
+ }
1382
+ const checkboxStyles = css `
1383
+ display: inline-block;
1384
+ ${!props.disabled && !props.readOnly && `
1385
+ &:hover {
1386
+ filter: ${theme.controls.hoverBrightness};
1387
+ }
1388
+ `}
1389
+ `;
1390
+ const labelStyles = css `
1391
+ cursor: pointer;
1392
+ user-select: none;
1393
+ display: flex;
1394
+ align-items: center;
1395
+ ${props.disabled && `
1396
+ cursor: not-allowed;
1397
+ `}
1398
+ ${props.readOnly && `
1399
+ cursor: default;
1400
+ `}
1401
+ `;
1402
+ // had to reference a marker class ('checkboxIcon') here for the sibling selector to work.
1403
+ // opacity: 0 required for firefox. ignores the width of 0.
1404
+ const nativeCheckboxStyles = css `
1405
+ margin: 0;
1406
+ padding: 0;
1407
+ width: 0;
1408
+ opacity: 0;
1409
+
1410
+ ${!props.readOnly && `
1411
+ &:focus + .checkboxIcon {
1412
+ box-shadow: ${theme.controls.focusOutlineShadow};
1413
+ }
1414
+ `}
1415
+ `;
1416
+ const iconStyles = css `
1417
+ ${!!props.label && `
1418
+ margin-right: 0.5rem;
1419
+ `}
1420
+ ${props.disabled && `
1421
+ background-color: ${theme.colors.disabled};
1422
+ cursor: not-allowed;
1423
+ `}
1424
+ ${props.readOnly && `
1425
+ cursor: default;
1426
+ `}
1427
+ ${props.checked && `
1428
+ color: ${finalCheckedColor}
1429
+ `}
1430
+ `;
1431
+ return (React.createElement("span", { className: cx('checkbox', checkboxStyles, props.className) },
1432
+ React.createElement("label", { className: labelStyles },
1433
+ React.createElement("input", Object.assign({}, inputProps, { tabIndex: props.readOnly ? -1 : undefined, className: nativeCheckboxStyles, type: "checkbox", onChange: e => {
1434
+ if (props.readOnly) {
1435
+ e.preventDefault();
1436
+ return;
1437
+ }
1438
+ return props.onChange(e.currentTarget.checked, e);
1439
+ } })),
1440
+ React.createElement(Icon, { className: cx('checkboxIcon', iconStyles), id: props.checked ? selected : unselected }),
1441
+ props.label,
1442
+ props.children)));
1443
+ };
1444
+
1445
+ /** useEffect but it will only fire when the actual truthiness of the value changes.
1446
+ * Use for comparing previous states to next states without all the bullshit around useEffect and component mounting.
1447
+ */
1448
+ const useBooleanChanged = (effect, dep) => {
1449
+ /*
1450
+ Why?
1451
+ useEffect with a dependency array will fire once on mount even though the dependency list doesn't change.
1452
+ Components like Modal need to communicate when their show status changes.
1453
+ useIgnoreMount is not enough because it only ignores the first render and is therefore a kludge.
1454
+ This is what we want regardless of mount status:
1455
+ true > false = Change
1456
+ false > true = Change
1457
+ true > true = No Change
1458
+ false > false = No Change
1459
+ undefined > false = No Change
1460
+ undefined > true = Change
1461
+ */
1462
+ const lastValue = useRef(undefined);
1463
+ useEffect(() => {
1464
+ //console.log('[useBooleanChanged] useEffect called with', dep, 'was', lastValue.current ?? 'undefined')
1465
+ if (!!lastValue.current !== !!dep) {
1466
+ const previous = lastValue.current;
1467
+ lastValue.current = dep;
1468
+ effect(!!lastValue.current, !!previous);
1469
+ //console.log('[useBooleanChanged] change called')
1470
+ }
1471
+ }, [dep]);
1472
+ };
1473
+
1474
+ // Taken from: https://github.com/react-bootstrap/dom-helpers/blob/master/src/scrollbarSize.ts
1475
+ const canUseDom = !!(typeof window !== 'undefined' &&
1476
+ window.document &&
1477
+ window.document.createElement);
1478
+ let size;
1479
+ /** Tells you actual width of the scroll bar. This can vary by browser. */
1480
+ const useScrollbarSize = (recalc) => {
1481
+ if ((!size && size !== 0) || recalc) {
1482
+ if (canUseDom) {
1483
+ const scrollDiv = document.createElement('div');
1484
+ scrollDiv.style.position = 'absolute';
1485
+ scrollDiv.style.top = '-9999px';
1486
+ scrollDiv.style.width = '50px';
1487
+ scrollDiv.style.height = '50px';
1488
+ scrollDiv.style.overflow = 'scroll';
1489
+ document.body.appendChild(scrollDiv);
1490
+ size = scrollDiv.offsetWidth - scrollDiv.clientWidth;
1491
+ document.body.removeChild(scrollDiv);
1492
+ }
1493
+ }
1494
+ return size;
1495
+ };
1496
+
1497
+ const Modal = (p) => {
1498
+ var _a, _b, _c, _d;
1499
+ const backdrop = useContext(BackdropContext);
1500
+ const mouseDownElement = useRef(undefined);
1501
+ const theme = useThemeSafely();
1502
+ const hasHeader = p.closeButton || p.heading;
1503
+ const contentRef = React__default.useRef(null);
1504
+ const log = useLogger((_a = p.id) !== null && _a !== void 0 ? _a : 'Modal', (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
1505
+ const showing = useRef(p.show);
1506
+ const bodyStyles = useRef('');
1507
+ const fixedElementStyles = useRef('');
1508
+ const addScrollStyles = () => {
1509
+ var _a, _b, _c, _d;
1510
+ if (!bodyStyles.current) {
1511
+ bodyStyles.current = css({
1512
+ label: 'ModalBodyOverrides_' + ((_b = (_a = p.id) === null || _a === void 0 ? void 0 : _a.replace(/\s+/, '')) !== null && _b !== void 0 ? _b : nanoid()),
1513
+ overflow: 'hidden',
1514
+ paddingRight: `${useScrollbarSize()}px`
1515
+ });
1516
+ log('creating singleton bodyStyles', bodyStyles.current);
1517
+ }
1518
+ if (!fixedElementStyles.current) {
1519
+ fixedElementStyles.current = css({
1520
+ label: 'ModalElementOverrides_' + ((_d = (_c = p.id) === null || _c === void 0 ? void 0 : _c.replace(/\s+/, '')) !== null && _d !== void 0 ? _d : nanoid()),
1521
+ paddingRight: `${useScrollbarSize()}px`
1522
+ });
1523
+ }
1524
+ document.body.classList.add(bodyStyles.current);
1525
+ Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
1526
+ e.classList.add(fixedElementStyles.current);
1527
+ });
1528
+ };
1529
+ const tryRemoveScrollStyles = () => {
1530
+ if (bodyStyles.current) {
1531
+ log('removing singleton', bodyStyles.current);
1532
+ document.body.classList.remove(bodyStyles.current);
1533
+ }
1534
+ if (fixedElementStyles.current) {
1535
+ Array.from(document.querySelectorAll(`.${modalScrollFixClassName}`)).forEach(e => {
1536
+ e.classList.remove(fixedElementStyles.current);
1537
+ });
1538
+ }
1539
+ };
1540
+ useEffect(() => {
1541
+ log('mounted');
1542
+ return () => {
1543
+ var _a;
1544
+ if (showing.current) {
1545
+ log(`un-mount in progress and this modal is showing. decrement the backdrop and try to remove singleton body styles.`);
1546
+ backdrop.setShow(false, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
1547
+ log('backdrop.setShow', false);
1548
+ tryRemoveScrollStyles();
1549
+ }
1550
+ else {
1551
+ log(`un-mount in progress but this modal is not showing. do nothing with the backdrop.`);
1552
+ }
1553
+ log('un-mounted');
1554
+ };
1555
+ }, []);
1556
+ useBooleanChanged((show, previousShow) => {
1557
+ var _a;
1558
+ log('show changed', `${previousShow !== null && previousShow !== void 0 ? previousShow : 'undefined'} > ${show}`);
1559
+ backdrop.setShow(show, (_a = p.id) !== null && _a !== void 0 ? _a : 'Modal');
1560
+ showing.current = show;
1561
+ log('backdrop.setShow', show);
1562
+ if (show) {
1563
+ log('this modal is showing. adding singleton bodyStyles', bodyStyles.current);
1564
+ addScrollStyles();
1565
+ }
1566
+ else {
1567
+ log('this modal is hiding. try removing singleton bodyStyles');
1568
+ tryRemoveScrollStyles();
1569
+ }
1570
+ }, p.show);
1571
+ React__default.useLayoutEffect(() => {
1572
+ var _a;
1573
+ if (p.show === true) {
1574
+ const focusSelector = (_a = p.focusSelector) !== null && _a !== void 0 ? _a : '.modalCloseButton';
1575
+ // still need to wait for the next tick so the children are all rendered.
1576
+ setTimeout(() => {
1577
+ var _a;
1578
+ const element = (_a = contentRef.current) === null || _a === void 0 ? void 0 : _a.querySelector(focusSelector);
1579
+ element === null || element === void 0 ? void 0 : element.focus();
1580
+ log('set focus', focusSelector);
1581
+ });
1582
+ }
1583
+ }, [p.show]);
1584
+ const modalBodyStyles = css({
1585
+ maxHeight: p.scrollable ? undefined : '99vh',
1586
+ overflow: 'hidden',
1587
+ zIndex: theme.zIndexes.modal,
1588
+ cursor: 'default',
1589
+ margin: '1rem',
1590
+ backgroundColor: p.noBackground ? undefined : theme.colors.modalBg,
1591
+ border: p.noBackground ? undefined : theme.controls.border,
1592
+ boxShadow: p.noBackground ? undefined : theme.controls.boxShadow,
1593
+ maxWidth: (_c = p.maxWidth) !== null && _c !== void 0 ? _c : theme.breakpoints.tablet,
1594
+ minWidth: (_d = p.minWidth) !== null && _d !== void 0 ? _d : (hasHeader ? '250px' : undefined),
1595
+ opacity: p.show ? 1 : 0,
1596
+ fontSize: theme.fonts.size,
1597
+ fontFamily: theme.fonts.family,
1598
+ fontWeight: 'normal',
1599
+ '&:focus': {
1600
+ outline: 'none'
1601
+ }
1602
+ });
1603
+ const modalHeaderStyles = cx(css({
1604
+ display: 'flex',
1605
+ justifyContent: 'space-between',
1606
+ alignItems: 'center',
1607
+ backgroundColor: theme.colors.header,
1608
+ padding: '1rem',
1609
+ color: theme.colors.headerFont
1610
+ }), p.headerClassName);
1611
+ const modalContainerStyles = css([{
1612
+ position: 'fixed',
1613
+ height: '100%',
1614
+ width: '100%',
1615
+ backgroundColor: "transparent",
1616
+ display: 'flex',
1617
+ justifyContent: 'center',
1618
+ alignItems: 'center',
1619
+ cursor: p.onClick ? 'pointer' : 'default'
1620
+ }, p.scrollable && {
1621
+ overflowY: 'auto',
1622
+ overflowX: 'hidden',
1623
+ alignItems: 'flex-start'
1624
+ }]);
1625
+ if (p.show) {
1626
+ const backdropContainer = document.getElementById(backdrop.portalId);
1627
+ if (backdropContainer) {
1628
+ return createPortal((React__default.createElement("div", { onClick: e => {
1629
+ e.stopPropagation();
1630
+ if (!mouseDownElement.current) {
1631
+ if (p.onClick) {
1632
+ log('backdropContainer onClick');
1633
+ p.onClick();
1634
+ }
1635
+ }
1636
+ mouseDownElement.current = undefined;
1637
+ }, className: cx('modalContainer', modalContainerStyles) },
1638
+ React__default.createElement("div", { id: p.id, ref: contentRef, onClick: e => e.stopPropagation(), onMouseDown: e => {
1639
+ mouseDownElement.current = e.target;
1640
+ e.stopPropagation();
1641
+ }, onMouseUp: e => {
1642
+ mouseDownElement.current = undefined;
1643
+ /*
1644
+ MouseDown and MouseUp stopPropagation was added to fix bugs while clicking within the modal.
1645
+ At least in the case of MouseUp, this breaks sliders and the handle grab logic appears to live on window.
1646
+ Turning this off now. Should not cause any issues for MouseUp unlike MouseDown.
1647
+ */
1648
+ // e.stopPropagation()
1649
+ }, className: cx('modalBody', modalBodyStyles, p.className) },
1650
+ React__default.createElement(TabLocker, null,
1651
+ hasHeader && (React__default.createElement("header", { className: cx('modalHeader', modalHeaderStyles) },
1652
+ p.heading ? React__default.createElement(Text, { className: css({
1653
+ margin: 0,
1654
+ flexGrow: 1
1655
+ }), tag: "h1", bold: true }, p.heading) : React__default.createElement("span", null),
1656
+ p.closeButton && p.onClick ? React__default.createElement(Button, { className: cx('modalCloseButton', css({
1657
+ color: theme.colors.headerFont,
1658
+ marginLeft: '1rem',
1659
+ backgroundColor: 'transparent'
1660
+ })), variant: "icon", onClick: p.onClick },
1661
+ React__default.createElement(Icon, { id: "close" })) : React__default.createElement("span", null))),
1662
+ p.children)))), backdropContainer);
1663
+ }
1664
+ }
1665
+ return null;
1666
+ };
1667
+
1668
+ const ConfirmModal = (props) => {
1669
+ const theme = useThemeSafely();
1670
+ const modalStyle = css `
1671
+ ${props.variant === 'omg' && `
1672
+ .modalHeader {
1673
+ background-color:${theme.colors.omg};
1674
+ color:${theme.colors.omgFont};
1675
+ }
1676
+ `}
1677
+ `;
1678
+ return (React.createElement(Modal, { id: props.id, __debug: props.__debug, className: cx('confirmModal', modalStyle, props.className), heading: props.header, closeButton: true, show: props.show, onClick: props.onCancel },
1679
+ React.createElement("div", { className: css({ padding: '1rem' }) },
1680
+ React.createElement(Text, { align: "center" }, props.text),
1681
+ React.createElement("div", { className: css({ textAlign: 'center' }) },
1682
+ React.createElement(Button, { className: css({ margin: '0 0.5rem' }), enforceMinWidth: true, variant: props.variant === 'omg' ? "omg" : 'primary2', onClick: props.onConfirm }, props.confirmText || 'OK'),
1683
+ React.createElement(Button, { className: css({ margin: '0 0.5rem' }), enforceMinWidth: true, onClick: props.onCancel }, props.cancelText || 'Cancel')))));
1684
+ };
1685
+
1686
+ const CopyButton = (props) => {
1687
+ const [copied, setCopied] = React.useState(false);
1688
+ return (React.createElement(Button, { title: copied ? 'Copied!' : (props.title || 'Copy to clipboard'), variant: "icon", onBlur: () => {
1689
+ setCopied(false);
1690
+ }, onClick: e => {
1691
+ const button = e.currentTarget;
1692
+ let copySuccess = false;
1693
+ try {
1694
+ const text = document.querySelector(props.selector);
1695
+ text.select();
1696
+ copySuccess = document.execCommand('copy');
1697
+ // the input gets focus on select. bring it back.
1698
+ button.focus();
1699
+ }
1700
+ catch (err) {
1701
+ // You done wrong.
1702
+ }
1703
+ setCopied(copySuccess);
1704
+ } },
1705
+ React.createElement(Icon, { id: copied ? 'paste' : 'copy' })));
1706
+ };
1707
+
1708
+ const Divider = (p) => {
1709
+ const theme = useThemeSafely();
1710
+ return (React.createElement("hr", Object.assign({}, p, { className: cx("divider", css({
1711
+ label: 'Divider',
1712
+ margin: theme.controls.dividerMargin,
1713
+ border: theme.controls.dividerBorder
1714
+ }), p.className) })));
1715
+ };
1716
+
1717
+ const ErrorModal = (props) => {
1718
+ const { message } = props;
1719
+ const theme = useThemeSafely();
1720
+ const modalStyles = css `
1721
+ .modalHeader {
1722
+ background-color: ${theme.colors.omg};
1723
+ color: ${theme.colors.omgFont};
1724
+ }
1725
+ .modalCloseButton {
1726
+ color: ${theme.colors.omgFont};
1727
+ }
1728
+ `;
1729
+ return (React.createElement(Modal, { id: props.id, __debug: props.__debug, className: cx('errorModal', modalStyles), heading: "Error", closeButton: true, show: props.show, onClick: props.close },
1730
+ React.createElement("div", { className: css({ padding: '1rem' }) },
1731
+ React.createElement(Text, { align: "center" }, message))));
1732
+ };
1733
+
1734
+ /** Use this instead of <form> directly. If we need to fight Chrome's autofill, we can do so at a global level. */
1735
+ const Form = React.forwardRef((props, ref) => {
1736
+ const { inline, children, onSubmit, className, ajax, unstyled } = props, rest = __rest(props, ["inline", "children", "onSubmit", "className", "ajax", "unstyled"]);
1737
+ const theme = useThemeSafely();
1738
+ let defaultStyle;
1739
+ if (!props.unstyled) {
1740
+ defaultStyle = css({
1741
+ label: 'Form',
1742
+ display: 'flex',
1743
+ flexDirection: props.inline ? 'row' : 'column',
1744
+ alignItems: props.inline ? 'flex-end' : 'normal',
1745
+ gap: theme.controls.gap
1746
+ });
1747
+ }
1748
+ return (React.createElement("form", Object.assign({}, rest, { ref: ref, className: cx('form', defaultStyle, props.className), onSubmit: e => {
1749
+ if (ajax !== false) {
1750
+ e.preventDefault();
1751
+ e.stopPropagation();
1752
+ }
1753
+ if (onSubmit) {
1754
+ onSubmit(e);
1755
+ }
1756
+ } }), children));
1757
+ });
1758
+ /** @deprecated Just style normally using flex, grid, etc. */
1759
+ const FormFlexRow = (props) => {
1760
+ var _a;
1761
+ const theme = useThemeSafely();
1762
+ return (React.createElement("div", { style: props.style, className: cx('formFlexRow', css({
1763
+ display: 'flex',
1764
+ gap: theme.controls.gap,
1765
+ justifyContent: (_a = props.justifyContent) !== null && _a !== void 0 ? _a : 'normal'
1766
+ }), props.className) }, props.children));
1767
+ };
1768
+ /** @deprecated Just style normally using flex, grid, etc. */
1769
+ const FormColumnRow = (props) => {
1770
+ const theme = useThemeSafely();
1771
+ return (React.createElement("div", { style: props.style, className: cx('formColumnRow', css({
1772
+ display: 'grid',
1773
+ gap: theme.controls.gap,
1774
+ gridTemplateColumns: `repeat(${props.cols},1fr)`
1775
+ }), props.className) }, props.children));
1776
+ };
1777
+
1778
+ const InfoPanel = (props) => {
1779
+ const theme = useThemeSafely();
1780
+ const styles = css `
1781
+ border:${theme.colors.border};
1782
+ padding:1rem;
1783
+ color: rgba(0, 0, 0, 0.7);
1784
+ margin: 0 !important;
1785
+ ${props.variant === 'info' && `
1786
+ background-color:${theme.colors.info};
1787
+ color:${theme.colors.infoFont};
1788
+ `}
1789
+ ${props.variant === 'warning' && `
1790
+ background-color:${theme.colors.warning};
1791
+ color:${theme.colors.warningFont};
1792
+ `}
1793
+ ${props.variant === 'error' && `
1794
+ background-color:${theme.colors.omg};
1795
+ color:${theme.colors.omgFont};
1796
+ `}
1797
+ ${props.variant === 'negative' && `
1798
+ background-color:${theme.colors.negative};
1799
+ color:${theme.colors.negativeFont};
1800
+ `}
1801
+ ${props.variant === 'positive' && `
1802
+ background-color:${theme.colors.positive};
1803
+ color:${theme.colors.positiveFont};
1804
+ `}
1805
+ `;
1806
+ return (React.createElement(Text, { tag: props.tag || 'p', style: props.style, align: "center", className: cx('infoPanel', styles, props.className) }, props.children));
1807
+ };
1808
+
1809
+ class FileListPlus {
1810
+ constructor(_raw, _args = {}) {
1811
+ this._raw = _raw;
1812
+ this._args = _args;
1813
+ this._files = Array.from(this._raw).map(f => {
1814
+ return { name: f.name, size: f.size, type: f.type };
1815
+ });
1816
+ if (this._args.accept) {
1817
+ const acceptTypes = this._args.accept.split(',');
1818
+ this._invalidFiles = this._files.filter(f => {
1819
+ if (acceptTypes.includes(f.type)) {
1820
+ return false;
1821
+ }
1822
+ if (acceptTypes.some(t => f.name.endsWith(t))) {
1823
+ return false;
1824
+ }
1825
+ return true;
1826
+ });
1827
+ }
1828
+ else {
1829
+ this._invalidFiles = [];
1830
+ }
1831
+ }
1832
+ get raw() {
1833
+ return this._raw;
1834
+ }
1835
+ get length() {
1836
+ return this._files.length;
1837
+ }
1838
+ get files() {
1839
+ return this._files;
1840
+ }
1841
+ get invalidFiles() {
1842
+ return this._invalidFiles;
1843
+ }
1844
+ get totalBytes() {
1845
+ return sumBy(this.files, f => f.size);
1846
+ }
1847
+ get overMaxBytes() {
1848
+ var _a;
1849
+ return this.totalBytes >= ((_a = this._args.maxBytes) !== null && _a !== void 0 ? _a : Infinity);
1850
+ }
1851
+ get overFileLimit() {
1852
+ return this.length > (this._args.multiple ? Infinity : 1);
1853
+ }
1854
+ get hasErrors() {
1855
+ return this.overMaxBytes || this.overFileLimit || !!this.invalidFiles.length;
1856
+ }
1857
+ }
1858
+
1859
+ const DEFAULT_FAILURE_MESSAGE = 'Upload failed.';
1860
+ const hoverClass = css({
1861
+ backgroundColor: 'rgba(0,0,0,0.25) !important'
1862
+ });
1863
+ const FileUploader = (p) => {
1864
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1865
+ const [message, setMessage] = useState(undefined);
1866
+ const [canUpload, setCanUpload] = useState(false);
1867
+ const [uploading, setUploading] = useState(false);
1868
+ const [files, setFiles] = useState(undefined);
1869
+ const [fullFailureMessage, setFullFailureMessage] = useState(undefined);
1870
+ const form = useRef(null);
1871
+ const input = useRef(null);
1872
+ const totalFileSize = (_a = files === null || files === void 0 ? void 0 : files.totalBytes) !== null && _a !== void 0 ? _a : 0;
1873
+ const theme = useThemeSafely();
1874
+ let filesDisplay = '';
1875
+ if (!message) {
1876
+ if (!(files === null || files === void 0 ? void 0 : files.length)) {
1877
+ filesDisplay = (_b = p.instructionMessage) !== null && _b !== void 0 ? _b : `Click or drag and drop files.`;
1878
+ }
1879
+ else {
1880
+ filesDisplay = `${files.length.toLocaleString()} file${files.length > 1 ? 's' : ''} selected (${getFileSizeDisplay(totalFileSize)}): ${files.files.map(f => f.name).join(', ')}`;
1881
+ }
1882
+ }
1883
+ const setAllFiles = (inputFiles) => {
1884
+ if (input.current && inputFiles === undefined) {
1885
+ input.current.value = '';
1886
+ }
1887
+ setFiles(inputFiles);
1888
+ };
1889
+ const onFilesChange = (rawFiles) => {
1890
+ setAllFiles(rawFiles ? createFileList(rawFiles) : undefined);
1891
+ setCanUpload(!!(rawFiles === null || rawFiles === void 0 ? void 0 : rawFiles.length));
1892
+ setMessage(undefined);
1893
+ setFullFailureMessage(undefined);
1894
+ };
1895
+ const createFileList = (rawFiles) => {
1896
+ return new FileListPlus(rawFiles, {
1897
+ accept: p.accept,
1898
+ multiple: p.multiple,
1899
+ maxBytes: p.maxBytes
1900
+ });
1901
+ };
1902
+ const showInfoOnPick = (_c = p.showInfoOnPick) !== null && _c !== void 0 ? _c : true;
1903
+ let infoMessage;
1904
+ if (p.infoMessage && (!files || showInfoOnPick)) {
1905
+ if (typeof p.infoMessage === 'string') {
1906
+ infoMessage = React__default.createElement(Text, { noPad: true }, p.infoMessage);
1907
+ }
1908
+ else {
1909
+ infoMessage = p.infoMessage;
1910
+ }
1911
+ }
1912
+ return (React__default.createElement(Form, { ref: form, className: css({
1913
+ border: theme.controls.border,
1914
+ borderStyle: 'dashed',
1915
+ alignItems: 'center',
1916
+ justifyContent: 'center',
1917
+ position: 'relative',
1918
+ padding: '1rem',
1919
+ overflow: 'hidden',
1920
+ backgroundColor: theme.colors.lightBg,
1921
+ }, p.disabled && {
1922
+ opacity: theme.controls.disabledOpacity
1923
+ }), onDragOver: e => {
1924
+ var _a, _b;
1925
+ e.preventDefault();
1926
+ // we're using onDragOver instead of onDragEnter because *Enter and *Leave apparently can fire multiple times even though you're over the target.
1927
+ // *Over is continuous.
1928
+ if (!((_a = form.current) === null || _a === void 0 ? void 0 : _a.classList.contains(hoverClass))) {
1929
+ (_b = form.current) === null || _b === void 0 ? void 0 : _b.classList.add(hoverClass);
1930
+ }
1931
+ }, onDragLeave: e => {
1932
+ var _a;
1933
+ (_a = form.current) === null || _a === void 0 ? void 0 : _a.classList.remove(hoverClass);
1934
+ }, onDrop: e => {
1935
+ var _a;
1936
+ e.preventDefault();
1937
+ (_a = form.current) === null || _a === void 0 ? void 0 : _a.classList.remove(hoverClass);
1938
+ onFilesChange(e.dataTransfer.files);
1939
+ }, onSubmit: e => {
1940
+ if (!files) {
1941
+ return;
1942
+ }
1943
+ setUploading(true);
1944
+ p.onUpload(files.raw).then(() => {
1945
+ setMessage('success');
1946
+ setAllFiles(undefined);
1947
+ }).catch(err => {
1948
+ var _a;
1949
+ setMessage('failure');
1950
+ setFullFailureMessage(`${(_a = p.failureMessage) !== null && _a !== void 0 ? _a : DEFAULT_FAILURE_MESSAGE} Error: ${err instanceof Error ? err.message : err}`);
1951
+ }).finally(() => {
1952
+ setUploading(false);
1953
+ setCanUpload(false);
1954
+ });
1955
+ } },
1956
+ React__default.createElement("input", { disabled: p.disabled, ref: input, className: css({
1957
+ position: 'absolute',
1958
+ top: -50,
1959
+ left: 0,
1960
+ bottom: 0,
1961
+ right: 0,
1962
+ width: '100%',
1963
+ cursor: 'pointer',
1964
+ opacity: 0
1965
+ }, p.disabled && {
1966
+ cursor: 'not-allowed'
1967
+ }), type: "file", multiple: p.multiple, accept: p.accept, onChange: e => {
1968
+ try {
1969
+ if (!e.target.files) {
1970
+ onFilesChange(undefined);
1971
+ return;
1972
+ }
1973
+ onFilesChange(e.target.files);
1974
+ }
1975
+ catch (err) {
1976
+ // tslint:disable-next-line
1977
+ console.error(err);
1978
+ onFilesChange(undefined);
1979
+ }
1980
+ } }),
1981
+ !message && (React__default.createElement("span", { className: css({
1982
+ display: 'flex',
1983
+ flexDirection: 'column',
1984
+ gap: '1rem',
1985
+ alignItems: 'center',
1986
+ zIndex: !!(files === null || files === void 0 ? void 0 : files.length) ? 1 : undefined
1987
+ }) },
1988
+ !(files === null || files === void 0 ? void 0 : files.length) && React__default.createElement(Icon, { style: { fontSize: '2rem' }, id: "upload" }),
1989
+ React__default.createElement(Text, { align: "center", noPad: true, spacedOut: true, className: css({
1990
+ width: '100%'
1991
+ }) },
1992
+ filesDisplay,
1993
+ !!(files === null || files === void 0 ? void 0 : files.length) && (React__default.createElement(Button, { onClick: e => {
1994
+ e.stopPropagation();
1995
+ onFilesChange(undefined);
1996
+ }, className: css({ marginLeft: '1rem', color: theme.colors.negative }), rightIcon: React__default.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear"))),
1997
+ infoMessage,
1998
+ !!(files === null || files === void 0 ? void 0 : files.invalidFiles.length) && (React__default.createElement(InfoPanel, { variant: "error", className: css({ width: '100%' }) },
1999
+ "Invalid files: ",
2000
+ files.invalidFiles.map(f => f.name).join(', '),
2001
+ ".")),
2002
+ (files === null || files === void 0 ? void 0 : files.overMaxBytes) && (React__default.createElement(InfoPanel, { variant: "error", className: css({ width: '100%' }) },
2003
+ "Max file size exceeded (",
2004
+ getFileSizeDisplay((_d = p.maxBytes) !== null && _d !== void 0 ? _d : 0),
2005
+ ").")))),
2006
+ canUpload && !(files === null || files === void 0 ? void 0 : files.hasErrors) && (React__default.createElement(Button, { className: css({
2007
+ width: (_e = p.buttonWidth) !== null && _e !== void 0 ? _e : '10rem',
2008
+ zIndex: 1,
2009
+ }), waiting: uploading, type: "submit", variant: (_f = p.buttonVariant) !== null && _f !== void 0 ? _f : 'primary' }, (_g = p.buttonText) !== null && _g !== void 0 ? _g : 'Upload')),
2010
+ message === 'success' && (React__default.createElement(UploadInfoPanel, { variant: "positive", message: (_h = p.successMessage) !== null && _h !== void 0 ? _h : 'Upload successful.', onClear: () => setMessage(undefined) })),
2011
+ message === 'failure' && (React__default.createElement(UploadInfoPanel, { variant: "error", message: fullFailureMessage, onClear: () => setMessage(undefined) }))));
2012
+ };
2013
+ const UploadInfoPanel = (p) => {
2014
+ return (React__default.createElement(InfoPanel, { variant: p.variant, className: css({ zIndex: 1 }) },
2015
+ p.message,
2016
+ React__default.createElement(Button, { className: css({
2017
+ color: 'inherit',
2018
+ marginTop: '1rem',
2019
+ width: '100%'
2020
+ }), onClick: e => {
2021
+ e.stopPropagation();
2022
+ p.onClear();
2023
+ }, rightIcon: React__default.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear")));
2024
+ };
2025
+ // OMG this is dumb. We're sticking with decimals for ease of use.
2026
+ // https://stackoverflow.com/questions/40949135/gigabyte-or-gibibyte-1000-or-1024
2027
+ const getFileSizeDisplay = (size) => {
2028
+ let value = 0;
2029
+ let suffix = '';
2030
+ if (size >= 1000000000) {
2031
+ value = size / 1000000000;
2032
+ suffix = 'GB';
2033
+ }
2034
+ else if (size >= 1000000) {
2035
+ value = size / 1000000;
2036
+ suffix = 'MB';
2037
+ }
2038
+ else if (size >= 1000) {
2039
+ value = size / 1000;
2040
+ suffix = 'KB';
2041
+ }
2042
+ else {
2043
+ value = size;
2044
+ suffix = 'B';
2045
+ }
2046
+ return value.toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ` ${suffix}`;
2047
+ };
2048
+
2049
+ const Header = (props) => {
2050
+ var _a;
2051
+ const theme = useThemeSafely();
2052
+ const bodyStyles = css `
2053
+ padding-top: calc(${theme.layout.headerHeight} + ${theme.layout.headerBodyOffset});
2054
+ label: Header;
2055
+ `;
2056
+ React.useEffect(() => {
2057
+ document.body.classList.add(bodyStyles);
2058
+ return () => {
2059
+ document.body.classList.remove(bodyStyles);
2060
+ };
2061
+ });
2062
+ const toggleMenu = (_a = props.toggleMenu) !== null && _a !== void 0 ? _a : (() => {
2063
+ /* noop */
2064
+ });
2065
+ const headerStyles = css `
2066
+ display: flex;
2067
+ gap: ${theme.controls.gap};
2068
+ justify-content: flex-start;
2069
+ align-items: center;
2070
+ position: fixed;
2071
+ top: 0;
2072
+ right: 0;
2073
+ left: 0;
2074
+ height: ${theme.layout.headerHeight};
2075
+ background-color: ${theme.colors.header};
2076
+ box-shadow: ${theme.controls.headerBoxShadow};
2077
+ color: ${theme.colors.headerFont};
2078
+ padding: 1rem;
2079
+ z-index: ${theme.zIndexes.header};
2080
+ flex-wrap: wrap;
2081
+ .button {
2082
+ color: ${theme.colors.headerFont};
2083
+ background-color:transparent;
2084
+ }
2085
+ ${props.inline && `
2086
+ position: relative;
2087
+ `}
2088
+ ${!!props.rightElements && `
2089
+ justify-content: space-between;
2090
+ `}
2091
+ ${props.responsive && `
2092
+ @media(min-width:${theme.breakpoints.desktop}) {
2093
+ justify-content: flex-end;
2094
+ }
2095
+ `}
2096
+ `;
2097
+ const headerButtonStyles = !props.noMenu ? css `
2098
+ ${props.responsive && `
2099
+ @media(min-width:${theme.breakpoints.desktop}) {
2100
+ display: none;
2101
+ }
2102
+ `}
2103
+ ` : undefined;
2104
+ const rightElementStyles = props.rightElements && css `
2105
+ margin-left: 1rem;
2106
+ display: grid;
2107
+ grid-gap: 0.5rem;
2108
+ grid-auto-flow: column;
2109
+ align-items: center;
2110
+ `;
2111
+ const leftElementStyles = props.leftElements && css({
2112
+ display: 'grid',
2113
+ gap: '0.5rem',
2114
+ gridAutoFlow: 'column',
2115
+ alignItems: 'center'
2116
+ });
2117
+ const centerElementsStyles = props.centerOffsetElements && css `
2118
+ position: absolute;
2119
+ display: flex;
2120
+ justify-content: center;
2121
+ left: 0;
2122
+ right: 0;
2123
+ bottom: calc(${theme.layout.headerHeight} / 2 * -1);
2124
+ `;
2125
+ let title;
2126
+ if (props.title) {
2127
+ if (typeof props.title === 'string') {
2128
+ title = (React.createElement(Text, { className: css({
2129
+ margin: 0,
2130
+ fontSize: '2rem',
2131
+ flexGrow: 1,
2132
+ flexBasis: '33%'
2133
+ }), tag: "h1", ellipsis: !props.fillTitle, align: props.fillTitle ? 'center' : 'left' }, props.title));
2134
+ }
2135
+ else {
2136
+ title = props.title;
2137
+ }
2138
+ }
2139
+ return (React.createElement("div", { className: cx('header', headerStyles, props.className) },
2140
+ !props.noMenu && (React.createElement(Button, { variant: "icon", className: headerButtonStyles, onClick: toggleMenu },
2141
+ React.createElement(Icon, { id: "menu" }))),
2142
+ props.leftElements && React.createElement("div", { className: leftElementStyles }, props.leftElements),
2143
+ title,
2144
+ props.rightElements && React.createElement("div", { className: rightElementStyles }, props.rightElements),
2145
+ props.centerOffsetElements && (React.createElement("div", { className: centerElementsStyles },
2146
+ React.createElement("div", null, props.centerOffsetElements)))));
2147
+ };
2148
+
2149
+ const Highlight = (props) => {
2150
+ const theme = useThemeSafely();
2151
+ const highlightStyles = css `
2152
+ > mark {
2153
+ background-color: ${theme.colors.textHighlight};
2154
+ }
2155
+ `;
2156
+ let text = props.text;
2157
+ if (props.text && props.highlightText) {
2158
+ const replaceText = props.highlightText.trim();
2159
+ if (replaceText) {
2160
+ text = props.text.replace(new RegExp(`(${replaceText})`, 'gi'), `<mark>$1</mark>`);
2161
+ }
2162
+ }
2163
+ return (React.createElement("span", { className: cx('highlight', highlightStyles), dangerouslySetInnerHTML: { __html: text } }));
2164
+ };
2165
+
2166
+ const Image = React.forwardRef((p, ref) => {
2167
+ return (React.createElement("img", Object.assign({}, p, { ref: ref, className: cx('image', css({
2168
+ label: 'Image',
2169
+ maxWidth: '100%',
2170
+ maxHeight: '100%',
2171
+ }), p.className) })));
2172
+ });
2173
+
2174
+ const Popover = (p) => {
2175
+ var _a, _b;
2176
+ const theme = useThemeSafely();
2177
+ const resposition = (_a = p.reposition) !== null && _a !== void 0 ? _a : true;
2178
+ return (React.createElement(Popover$1, { containerClassName: css({
2179
+ zIndex: theme.zIndexes.tooltip
2180
+ }), reposition: resposition, isOpen: p.isOpen, positions: (_b = p.positions) !== null && _b !== void 0 ? _b : ['right', 'top', 'left', 'bottom'], onClickOutside: p.onClickOutside, content: ({ position, childRect, popoverRect }) => {
2181
+ var _a, _b, _c, _d;
2182
+ return (React.createElement(ArrowContainer, { position: position, childRect: childRect, popoverRect: popoverRect, arrowColor: (_a = p.arrorColor) !== null && _a !== void 0 ? _a : theme.colors.border, arrowSize: 10 },
2183
+ React.createElement(TabLocker, null,
2184
+ React.createElement("div", { className: css({
2185
+ border: (_b = p.border) !== null && _b !== void 0 ? _b : theme.controls.border,
2186
+ borderRadius: (_c = p.border) !== null && _c !== void 0 ? _c : theme.controls.borderRadius,
2187
+ boxShadow: theme.controls.boxShadow,
2188
+ backgroundColor: (_d = p.backgroundColor) !== null && _d !== void 0 ? _d : theme.colors.bg,
2189
+ }) }, p.content))));
2190
+ } },
2191
+ React.createElement("span", null, p.parent)));
2192
+ };
2193
+
2194
+ const InfoTip = (props) => {
2195
+ var _a, _b, _c;
2196
+ const [showTip, setShowTip] = React.useState(false);
2197
+ const theme = useThemeSafely();
2198
+ const bgColor = (_a = props.bgColor) !== null && _a !== void 0 ? _a : theme.colors.nav;
2199
+ const fontColor = (_b = props.fontColor) !== null && _b !== void 0 ? _b : theme.colors.navFont;
2200
+ const onClick = () => {
2201
+ if (props.onClick) {
2202
+ props.onClick();
2203
+ }
2204
+ else if (props.content) {
2205
+ openTip();
2206
+ }
2207
+ };
2208
+ const onMouseOver = () => {
2209
+ if (props.variant === 'modal') {
2210
+ return;
2211
+ }
2212
+ if (props.loadOnHover) {
2213
+ props.loadOnHover().then(() => {
2214
+ openTip();
2215
+ }).catch(err => {
2216
+ /* Not my responsiblity. */
2217
+ });
2218
+ }
2219
+ else {
2220
+ openTip();
2221
+ }
2222
+ };
2223
+ const onMouseOut = () => {
2224
+ if (props.variant === 'modal') {
2225
+ return;
2226
+ }
2227
+ closeTip();
2228
+ };
2229
+ const openTip = () => {
2230
+ if (!props.content) {
2231
+ return;
2232
+ }
2233
+ setShowTip(props.disabled ? false : true);
2234
+ };
2235
+ const closeTip = () => {
2236
+ if (!props.content) {
2237
+ return;
2238
+ }
2239
+ setShowTip(false);
2240
+ if (props.onClose) {
2241
+ props.onClose();
2242
+ }
2243
+ };
2244
+ const buttonStyles = css `
2245
+ font-weight: bold;
2246
+ width: 1.5rem;
2247
+ min-width:1.5rem;
2248
+ height: 1.5rem;
2249
+ padding: 0 !important;
2250
+ font-family: serif;
2251
+ display:inline-block;
2252
+ `;
2253
+ const button = React.createElement(Button, { className: buttonStyles, disabled: props.disabled, variant: 'circle', tabIndex: props.tabIndex, onClick: onClick, onMouseEnter: onMouseOver, onMouseLeave: onMouseOut }, "i");
2254
+ if (props.variant === 'modal') {
2255
+ return (React.createElement(React.Fragment, null,
2256
+ button,
2257
+ React.createElement(Modal, { id: props.modalId, __debug: props.__modalDebug, show: showTip, heading: props.modalHeader, onClick: closeTip, className: css({
2258
+ whiteSpace: 'normal'
2259
+ }), closeButton: true },
2260
+ React.createElement("div", { className: css({ padding: '1rem' }) }, props.content))));
2261
+ }
2262
+ else {
2263
+ return (React.createElement(Popover, { positions: props.positions, reposition: (_c = props.reposition) !== null && _c !== void 0 ? _c : false, isOpen: showTip, onClickOutside: closeTip, arrorColor: bgColor, border: '', backgroundColor: bgColor, parent: button, content: (React.createElement("div", { className: css({
2264
+ padding: '0.5rem',
2265
+ fontSize: '0.75rem',
2266
+ maxWidth: '22rem'
2267
+ }), style: { backgroundColor: bgColor, color: fontColor }, onMouseOut: closeTip }, props.content)) }));
2268
+ }
2269
+ };
2270
+
2271
+ const InputErrorDisplay = (props) => {
2272
+ const theme = useThemeSafely();
2273
+ return (React.createElement(Text, { className: css({
2274
+ minHeight: theme.controls.inputErrorMinHeight,
2275
+ lineHeight: theme.controls.inputErrorMinHeight,
2276
+ color: theme.colors.negative
2277
+ }), smaller: true, noPad: true }, props.error));
2278
+ };
2279
+
2280
+ const defaultMaxLength$1 = 100;
2281
+ const BaseInput = React.forwardRef((props, ref) => {
2282
+ var _a, _b;
2283
+ const theme = useThemeSafely();
2284
+ const { rightControl, round, wrapperClassName, showErrorDisplay } = props, nativeProps = __rest(props, ["rightControl", "round", "wrapperClassName", "showErrorDisplay"]);
2285
+ const inputStyles = css({
2286
+ backgroundColor: theme.colors.bg,
2287
+ fontFamily: theme.fonts.family,
2288
+ fontSize: theme.fonts.size,
2289
+ width: '100%',
2290
+ border: theme.controls.border,
2291
+ borderRadius: theme.controls.borderRadius,
2292
+ color: theme.colors.font,
2293
+ paddingLeft: theme.controls.padding,
2294
+ paddingRight: theme.controls.padding,
2295
+ height: theme.controls.height,
2296
+ transition: theme.controls.transition,
2297
+ ':focus': {
2298
+ outline: 'none',
2299
+ boxShadow: theme.controls.focusOutlineShadow
2300
+ },
2301
+ ':disabled': {
2302
+ backgroundColor: theme.colors.disabled,
2303
+ cursor: 'not-allowed'
2304
+ },
2305
+ ':invalid': {
2306
+ borderColor: theme.colors.required,
2307
+ ':focus': {
2308
+ boxShadow: theme.controls.focusOutlineRequiredShadow
2309
+ }
2310
+ },
2311
+ }, props.round && {
2312
+ borderRadius: theme.controls.roundRadius,
2313
+ paddingLeft: `calc(${theme.controls.padding} * 2)`,
2314
+ paddingRight: `calc(${theme.controls.padding} * 2)`
2315
+ }, props.readOnly && {
2316
+ backgroundColor: 'transparent',
2317
+ cursor: 'default',
2318
+ border: 'none',
2319
+ ':focus': {
2320
+ outline: 'none',
2321
+ boxShadow: 'none'
2322
+ },
2323
+ // FF fix to hide spinner on number elements
2324
+ appearance: props.type === 'number' ? 'none' : undefined,
2325
+ '::-webkit-outer-spin-button': {
2326
+ appearance: 'none'
2327
+ },
2328
+ '::-webkit-inner-spin-button': {
2329
+ appearance: 'none'
2330
+ }
2331
+ }, props.rightControl && {
2332
+ paddingRight: theme.controls.height
2333
+ });
2334
+ const inputElement = React.createElement("input", Object.assign({}, nativeProps, { ref: ref, autoComplete: (_a = nativeProps.autoComplete) !== null && _a !== void 0 ? _a : 'off', tabIndex: nativeProps.readOnly ? -1 : nativeProps.tabIndex, maxLength: nativeProps.maxLength || defaultMaxLength$1, className: cx(inputStyles, props.className) }));
2335
+ const inputWrapperStyles = css `
2336
+ width:100%;
2337
+ ${props.rightControl && `
2338
+ position: relative;
2339
+ `}
2340
+ `;
2341
+ const rightControlStyles = props.rightControl && css `
2342
+ position: absolute;
2343
+ right: ${theme.controls.padding};
2344
+ top: 0;
2345
+ bottom: 0;
2346
+ display: flex;
2347
+ align-items: center;
2348
+ ${props.round && `
2349
+ right: calc(${theme.controls.padding} * 2);
2350
+ `}
2351
+ `;
2352
+ return (React.createElement("div", { className: css({
2353
+ width: '100%',
2354
+ label: 'BaseInput'
2355
+ }) },
2356
+ React.createElement("div", { className: cx('input', inputWrapperStyles, wrapperClassName) },
2357
+ inputElement,
2358
+ props.rightControl && (React.createElement("div", { className: rightControlStyles }, props.rightControl))),
2359
+ ((_b = props.showErrorDisplay) !== null && _b !== void 0 ? _b : true) && React.createElement(InputErrorDisplay, { error: props.readOnly ? undefined : props.error })));
2360
+ });
2361
+
2362
+ const tryClampRange = (value, min, max) => {
2363
+ if (value === undefined) {
2364
+ return value;
2365
+ }
2366
+ if (isNaN(value)) {
2367
+ return undefined;
2368
+ }
2369
+ if (min !== undefined && value < min) {
2370
+ return min;
2371
+ }
2372
+ if (max !== undefined && value > max) {
2373
+ return max;
2374
+ }
2375
+ return value;
2376
+ };
2377
+ const isOutOfRange = (value, min, max) => {
2378
+ if (min !== undefined && value < min) {
2379
+ return true;
2380
+ }
2381
+ if (max !== undefined && value > max) {
2382
+ return true;
2383
+ }
2384
+ return false;
2385
+ };
2386
+ const getStepDecimalPlaces = (step) => {
2387
+ var _a, _b;
2388
+ if (!step) {
2389
+ return 0;
2390
+ }
2391
+ const strStep = typeof step === 'number' ? step.toString() : step;
2392
+ return (_b = (_a = strStep.split('.')[1]) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0;
2393
+ };
2394
+ const tryClampDecimals = (value, step = 0) => {
2395
+ if (value === undefined) {
2396
+ return value;
2397
+ }
2398
+ if (isNaN(value)) {
2399
+ return undefined;
2400
+ }
2401
+ const decimals = getStepDecimalPlaces(step);
2402
+ if (decimals === 0) {
2403
+ return Math.floor(value);
2404
+ }
2405
+ return parseFloat(value.toFixed(decimals));
2406
+ };
2407
+
2408
+ /** Common state handling for displaying validation messages with inputs. */
2409
+ const useInputValidationMessage = (ref, props) => {
2410
+ const [validationError, setValidationError] = React__default.useState('');
2411
+ const updateErrorMessage = (customErrorOverride) => {
2412
+ var _a;
2413
+ const customError = customErrorOverride || props.customError || '';
2414
+ // set it OR clear it. either way, update it.
2415
+ (_a = ref.current) === null || _a === void 0 ? void 0 : _a.setCustomValidity(customError);
2416
+ setValidationError(customError || getValidationMessage(ref.current, props.patternErrorMessage));
2417
+ };
2418
+ useEffect(() => {
2419
+ updateErrorMessage();
2420
+ }, [props.customError]);
2421
+ React__default.useEffect(() => {
2422
+ updateErrorMessage();
2423
+ }, []);
2424
+ return [validationError, updateErrorMessage];
2425
+ };
2426
+ const getValidationMessage = (element, patternErrorMessage) => {
2427
+ var _a;
2428
+ if (!element) {
2429
+ return '';
2430
+ }
2431
+ const validity = element.validity;
2432
+ if (validity.valid) {
2433
+ return '';
2434
+ }
2435
+ if (validity.customError) {
2436
+ return element.validationMessage;
2437
+ }
2438
+ if (validity.typeMismatch) {
2439
+ switch (element.type) {
2440
+ case 'url':
2441
+ return `Invalid URL.`;
2442
+ case 'email':
2443
+ return `Invalid email.`;
2444
+ default:
2445
+ return element.validationMessage;
2446
+ }
2447
+ }
2448
+ if (element instanceof HTMLInputElement) {
2449
+ if (validity.rangeOverflow) {
2450
+ return `Must be less than or equal to ${element.max}.`;
2451
+ }
2452
+ if (validity.rangeUnderflow) {
2453
+ return `Must be greater than or equal to ${element.min}.`;
2454
+ }
2455
+ if (validity.stepMismatch) {
2456
+ const decimalPlaces = getStepDecimalPlaces(element.step);
2457
+ if (decimalPlaces > 0) {
2458
+ const place = decimalPlaces === 1 ? 'place' : 'places';
2459
+ return `Limited to ${getStepDecimalPlaces(element.step)} decimal ${place}.`;
2460
+ }
2461
+ else {
2462
+ /*
2463
+ step is buggy!
2464
+
2465
+ at least in Chrome, setting step=5 will cause the browser to mark the field as invalid if the number is not divisible
2466
+ by 5. 55 is ok. 50 is ok. 59 is not ok.
2467
+
2468
+ to make things worse, if you enter an invalid number like 59 with step=5 and then clear the input, Chrome will tell
2469
+ you the number 5 is now invalid and should be 4 or 9.
2470
+ */
2471
+ return `Must be an integer.`;
2472
+ }
2473
+ }
2474
+ }
2475
+ if (validity.tooShort) {
2476
+ return `Must be at least ${((_a = element.minLength) !== null && _a !== void 0 ? _a : 0).toLocaleString()} characters in length.`;
2477
+ }
2478
+ if (validity.valueMissing) {
2479
+ return 'Required.';
2480
+ }
2481
+ if (validity.patternMismatch && patternErrorMessage) {
2482
+ return patternErrorMessage;
2483
+ }
2484
+ // unhandled. let the browser decide.
2485
+ return element.validationMessage;
2486
+ };
2487
+
2488
+ const dateRegex = /(\d{1,2})(?:\/|-)(\d{1,2})(?:\/|-)(\d{4})/;
2489
+ const datePattern = dateRegex.source;
2490
+ const invalidDateMessage = 'Invalid date.';
2491
+ const DateInput = React.forwardRef((props, ref) => {
2492
+ var _a;
2493
+ const [dateValue, setDateValue] = React.useState(props.value);
2494
+ const [textValue, setTextValue] = React.useState(parseDateString(props.value));
2495
+ const updateValues = React.useCallback((value) => {
2496
+ let newDateValue;
2497
+ let newTextValue;
2498
+ if (typeof value === 'number') {
2499
+ newDateValue = value;
2500
+ newTextValue = parseDateString(value);
2501
+ }
2502
+ else if (typeof value === 'string') {
2503
+ newDateValue = parseDateNumber(value);
2504
+ newTextValue = value;
2505
+ }
2506
+ setDateValue(newDateValue);
2507
+ setTextValue(newTextValue);
2508
+ }, []);
2509
+ const inputRef = (ref !== null && ref !== void 0 ? ref : React.useRef(null));
2510
+ const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, { customError: props.customError, patternErrorMessage: invalidDateMessage });
2511
+ const updateDateErrorMessages = React.useCallback(() => {
2512
+ let dateError = '';
2513
+ if (dateValue === undefined) {
2514
+ if (!!textValue) {
2515
+ // the text pattern is a valid date format, but the numbers are wrong. example: 05/35/2000.
2516
+ dateError = invalidDateMessage;
2517
+ }
2518
+ }
2519
+ else if (isOutOfRange(dateValue, props.min, props.max)) {
2520
+ // out of range
2521
+ if (props.min !== undefined && props.max !== undefined) {
2522
+ dateError = `Must be be between ${parseDateString(props.min)} and ${parseDateString(props.max)}.`;
2523
+ }
2524
+ else if (props.min !== undefined) {
2525
+ dateError = `Must be greater than or equal to ${parseDateString(props.min)}.`;
2526
+ }
2527
+ else {
2528
+ dateError = `Must be less than or equal to ${parseDateString(props.max)}.`;
2529
+ }
2530
+ }
2531
+ updateErrorMessage(dateError);
2532
+ }, [dateValue, textValue]);
2533
+ const [showCalendar, setShowCalendar] = React.useState(false);
2534
+ const [calendarDate, setCalendarDate] = React.useState(getCalendarDate(props.value, props.min, props.max));
2535
+ // controls the one-time focus set on show
2536
+ const needsFocus = React.useRef(false);
2537
+ const toggleCalendar = React.useCallback((show) => {
2538
+ var _a;
2539
+ if (show === showCalendar) {
2540
+ return;
2541
+ }
2542
+ needsFocus.current = show;
2543
+ setShowCalendar(show);
2544
+ if (show) {
2545
+ setCalendarDate(getCalendarDate(dateValue, props.min, props.max));
2546
+ }
2547
+ else {
2548
+ (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
2549
+ }
2550
+ }, [dateValue, showCalendar]);
2551
+ const popover = React.useRef(null);
2552
+ const nativeProps = __rest(props, ["customError", "reposition", "onValueChange"]);
2553
+ useIgnoreMount(() => {
2554
+ var _a;
2555
+ /*
2556
+ Run this right away.
2557
+ If not, our value may be valid but our error system reports it otherwise due to not being updated yet.
2558
+ */
2559
+ updateDateErrorMessages();
2560
+ if ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.checkValidity()) {
2561
+ props.onValueChange(dateValue);
2562
+ }
2563
+ else {
2564
+ props.onValueChange(undefined);
2565
+ }
2566
+ }, [dateValue]);
2567
+ useIgnoreMount(() => {
2568
+ updateDateErrorMessages();
2569
+ }, [textValue]);
2570
+ useIgnoreMount(() => {
2571
+ var _a;
2572
+ if (document.activeElement !== inputRef.current) {
2573
+ updateValues(props.value);
2574
+ setCalendarDate(getCalendarDate((_a = props.value) !== null && _a !== void 0 ? _a : 0, props.min, props.max));
2575
+ }
2576
+ updateDateErrorMessages();
2577
+ }, [props.value]);
2578
+ React.useLayoutEffect(() => {
2579
+ var _a, _b;
2580
+ if (needsFocus.current) {
2581
+ (_b = (_a = popover.current) === null || _a === void 0 ? void 0 : _a.querySelector('button')) === null || _b === void 0 ? void 0 : _b.focus();
2582
+ needsFocus.current = false;
2583
+ }
2584
+ });
2585
+ const input = (React.createElement(BaseInput, Object.assign({}, nativeProps, { error: validationError, type: "text", ref: inputRef, value: textValue !== null && textValue !== void 0 ? textValue : '', maxLength: 10, placeholder: (_a = props.placeholder) !== null && _a !== void 0 ? _a : 'MM/DD/YYYY', pattern: datePattern, rightControl: (!props.readOnly && !props.disabled) ? (React.createElement(Button, { variant: "icon", readOnly: props.readOnly, disabled: props.disabled, small: true, style: {
2586
+ fontSize: '1rem'
2587
+ }, onClick: () => {
2588
+ toggleCalendar(!showCalendar);
2589
+ } },
2590
+ React.createElement(Icon, { id: "pickDate" }))) : undefined, onChange: e => {
2591
+ var _a;
2592
+ updateValues(e.target.value || undefined);
2593
+ (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
2594
+ }, onFocus: e => {
2595
+ var _a;
2596
+ toggleCalendar(false);
2597
+ (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
2598
+ }, onBlur: e => {
2599
+ var _a, _b;
2600
+ if (!((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.checkValidity())) {
2601
+ if (dateValue !== undefined) {
2602
+ if (isOutOfRange(dateValue, props.min, props.max)) {
2603
+ // try and fix the range
2604
+ updateValues(tryClampRange(dateValue, props.min, props.max));
2605
+ }
2606
+ }
2607
+ else {
2608
+ // just wipe it all
2609
+ updateValues(undefined);
2610
+ }
2611
+ }
2612
+ (_b = props.onBlur) === null || _b === void 0 ? void 0 : _b.call(props, e);
2613
+ } })));
2614
+ return (React.createElement(Popover, { reposition: props.reposition, isOpen: showCalendar, onClickOutside: () => {
2615
+ toggleCalendar(false);
2616
+ }, parent: input, content: (React.createElement("div", { ref: popover, className: css({
2617
+ paddingLeft: '1rem',
2618
+ paddingRight: '1rem',
2619
+ paddingBottom: '1rem'
2620
+ }) },
2621
+ React.createElement(Calendar, { onClick: date => {
2622
+ updateValues(date.valueOf());
2623
+ toggleCalendar(false);
2624
+ }, customTitle: React.createElement("div", { className: css({
2625
+ display: 'flex',
2626
+ justifyContent: 'space-between',
2627
+ alignItems: 'center'
2628
+ }) },
2629
+ React.createElement(Button, { disabled: !!props.min && isBefore(endOfMonth(addMonths(calendarDate, -1)), props.min), small: true, variant: "icon", onClick: () => setCalendarDate(addMonths(calendarDate, -1)) },
2630
+ React.createElement(Icon, { id: "pagerLeft" })),
2631
+ React.createElement(Text, { align: "center" },
2632
+ format(calendarDate, 'LLLL'),
2633
+ " ",
2634
+ calendarDate.getFullYear()),
2635
+ React.createElement(Button, { disabled: !!props.max && isAfter(startOfMonth(addMonths(calendarDate, 1)), props.max), small: true, variant: "icon", onClick: () => setCalendarDate(addMonths(calendarDate, 1)) },
2636
+ React.createElement(Icon, { id: "pagerRight" }))), month: calendarDate.getMonth() + 1, year: calendarDate.getFullYear(), showCurrent: true, smallHeader: true, selectedValue: dateValue, cellSize: '2rem', min: props.min, max: props.max }))) }));
2637
+ });
2638
+ const parseDateNumber = (rawValue) => {
2639
+ if (!rawValue) {
2640
+ return undefined;
2641
+ }
2642
+ let dateMs;
2643
+ const dateParts = dateRegex.exec(rawValue);
2644
+ if (dateParts) {
2645
+ const month = parseInt(dateParts[1], 10) - 1;
2646
+ const day = parseInt(dateParts[2], 10);
2647
+ const year = parseInt(dateParts[3], 10);
2648
+ if (isExists(year, month, day)) {
2649
+ dateMs = new Date(year, month, day).valueOf();
2650
+ }
2651
+ }
2652
+ return dateMs;
2653
+ };
2654
+ const parseDateString = (dateMs) => {
2655
+ if (typeof dateMs === 'number') {
2656
+ return format(dateMs, 'MM/dd/yyyy');
2657
+ }
2658
+ return undefined;
2659
+ };
2660
+ const getCalendarDate = (date, min, max) => {
2661
+ let calendarDate = date ? new Date(date) : new Date();
2662
+ // if there is a min/max we don't want the calendar to open to the current date if it's out of range
2663
+ if (min && isBefore(calendarDate, min)) {
2664
+ calendarDate = new Date(min);
2665
+ }
2666
+ else if (max && isAfter(calendarDate, max)) {
2667
+ calendarDate = new Date(max);
2668
+ }
2669
+ return calendarDate;
2670
+ };
2671
+
2672
+ const NumberInput = React.forwardRef((props, ref) => {
2673
+ const [localValue, setLocalValue] = React.useState(props.value);
2674
+ const inputRef = (ref !== null && ref !== void 0 ? ref : React.useRef(null));
2675
+ const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, props);
2676
+ const nativeProps = __rest(props, ["customError", "onValueChange"]);
2677
+ useIgnoreMount(() => {
2678
+ var _a;
2679
+ if ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.checkValidity()) {
2680
+ props.onValueChange(localValue);
2681
+ }
2682
+ else {
2683
+ props.onValueChange(undefined);
2684
+ }
2685
+ updateErrorMessage();
2686
+ }, [localValue]);
2687
+ useIgnoreMount(() => {
2688
+ if (document.activeElement !== inputRef.current) {
2689
+ setLocalValue(props.value);
2690
+ }
2691
+ updateErrorMessage();
2692
+ }, [props.value]);
2693
+ return (React.createElement(BaseInput, Object.assign({}, nativeProps, { error: validationError, type: "number", ref: inputRef, value: localValue !== null && localValue !== void 0 ? localValue : '', maxLength: 20, onChange: e => {
2694
+ var _a;
2695
+ const newLocalValue = parseNumber(e.target.value);
2696
+ setLocalValue(newLocalValue);
2697
+ (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
2698
+ }, onBlur: e => {
2699
+ var _a, _b;
2700
+ let adjustedValue = localValue;
2701
+ if (e.target.validity.customError) {
2702
+ // if we're invalid due to a custom error, just wipe everything
2703
+ adjustedValue = undefined;
2704
+ }
2705
+ else {
2706
+ // try and fix the value
2707
+ adjustedValue = tryClampRange(adjustedValue, props.min, props.max);
2708
+ adjustedValue = tryClampDecimals(adjustedValue, props.step);
2709
+ }
2710
+ setLocalValue(adjustedValue);
2711
+ // makes our displayed value always matches the adjusted value
2712
+ // examples of failures are 'e', '-', and 5.0 in an integer (step=0) field.
2713
+ e.target.value = (_a = adjustedValue === null || adjustedValue === void 0 ? void 0 : adjustedValue.toString()) !== null && _a !== void 0 ? _a : '';
2714
+ (_b = props.onBlur) === null || _b === void 0 ? void 0 : _b.call(props, e);
2715
+ } })));
2716
+ });
2717
+ const parseNumber = (rawValue) => {
2718
+ let value;
2719
+ if (rawValue) {
2720
+ value = parseFloat(rawValue);
2721
+ if (isNaN(value)) {
2722
+ return undefined;
2723
+ }
2724
+ }
2725
+ return value;
2726
+ };
2727
+
2728
+ const TextInput = React.forwardRef((props, ref) => {
2729
+ var _a;
2730
+ const [localValue, setLocalValue] = React.useState(props.value);
2731
+ const inputRef = (ref !== null && ref !== void 0 ? ref : React.useRef(null));
2732
+ const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, props);
2733
+ const nativeProps = __rest(props, ["emptyString", "onValueChange", "customError", "patternErrorMessage"]);
2734
+ useIgnoreMount(() => {
2735
+ var _a;
2736
+ if ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.checkValidity()) {
2737
+ props.onValueChange(localValue);
2738
+ }
2739
+ else {
2740
+ props.onValueChange(undefined);
2741
+ }
2742
+ updateErrorMessage();
2743
+ }, [localValue]);
2744
+ useIgnoreMount(() => {
2745
+ if (document.activeElement !== inputRef.current) {
2746
+ setLocalValue(props.value);
2747
+ }
2748
+ updateErrorMessage();
2749
+ }, [props.value]);
2750
+ return (React.createElement(BaseInput, Object.assign({}, nativeProps, { error: validationError, type: (_a = props.type) !== null && _a !== void 0 ? _a : 'text', ref: inputRef, value: localValue !== null && localValue !== void 0 ? localValue : '', onChange: e => {
2751
+ var _a;
2752
+ setLocalValue(props.emptyString ? e.target.value : e.target.value || undefined);
2753
+ (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
2754
+ }, onBlur: e => {
2755
+ var _a, _b;
2756
+ if (!e.target.checkValidity()) {
2757
+ setLocalValue(undefined);
2758
+ }
2759
+ else if ((_a = props.trim) !== null && _a !== void 0 ? _a : true) {
2760
+ setLocalValue(currentValue => {
2761
+ return currentValue === null || currentValue === void 0 ? void 0 : currentValue.trim();
2762
+ });
2763
+ }
2764
+ (_b = props.onBlur) === null || _b === void 0 ? void 0 : _b.call(props, e);
2765
+ } })));
2766
+ });
2767
+
2768
+ const Label = (props) => {
2769
+ var _a, _b, _c;
2770
+ const labelProps = __rest(props, ["text", "static", "orientation", "align", "noWrap", "subText", "optional", "controlAlign"]);
2771
+ const theme = useThemeSafely();
2772
+ const orientationChoice = (_a = props.orientation) !== null && _a !== void 0 ? _a : 'vertical';
2773
+ const alignChoice = (_b = props.align) !== null && _b !== void 0 ? _b : 'left';
2774
+ const padding = '0.25rem';
2775
+ const labelStyles = css `
2776
+ label: Label;
2777
+ ${padding};
2778
+ display: flex;
2779
+ ${orientationChoice === 'vertical' && `
2780
+ flex-direction: column;
2781
+ `}
2782
+ ${orientationChoice === 'horizontal' && `
2783
+ flex-direction: row;
2784
+ align-items: center;
2785
+ `}
2786
+ ${orientationChoice === 'horizontalReverse' && `
2787
+ flex-direction: row-reverse;
2788
+ align-items: center;
2789
+ `}
2790
+ `;
2791
+ const labelTextStyles = css `
2792
+ label: LabelText;
2793
+ flex-shrink:0;
2794
+ ${orientationChoice === 'vertical' && `
2795
+ margin-bottom: ${padding};
2796
+ `}
2797
+ ${orientationChoice === 'horizontal' && `
2798
+ flex-direction: row;
2799
+ margin-right:${padding};
2800
+ ${props.static && `
2801
+ margin-right:0.5rem;
2802
+ `}
2803
+ `}
2804
+ ${orientationChoice === 'horizontalReverse' && `
2805
+ margin-left:${padding};
2806
+ `}
2807
+ ${(props.subText || props.optional) && `
2808
+ margin-right: 0.5rem;
2809
+ `}
2810
+ `;
2811
+ const labelContentStyles = css `
2812
+ label: LabelContent;
2813
+ display:inline-block;
2814
+ width:100%;
2815
+ ${props.controlAlign && `
2816
+ height:${theme.controls.height};
2817
+ line-height:${theme.controls.height};
2818
+ `}
2819
+ `;
2820
+ const outerClass = props.className;
2821
+ let labelText = React.createElement(Text, { align: alignChoice, className: labelTextStyles, tag: "div", bold: true }, props.text);
2822
+ let subTextChoice;
2823
+ if (props.subText) {
2824
+ if (typeof props.subText === 'string') {
2825
+ subTextChoice = React.createElement(Text, { tag: "div" }, props.subText);
2826
+ }
2827
+ else {
2828
+ subTextChoice = props.subText;
2829
+ }
2830
+ }
2831
+ else if (props.optional) {
2832
+ subTextChoice = React.createElement(Text, { tag: "div" },
2833
+ React.createElement("em", null, "(optional)"));
2834
+ }
2835
+ if (subTextChoice) {
2836
+ labelText = React.createElement("span", { className: css({ display: 'flex' }) },
2837
+ labelText,
2838
+ React.createElement("span", { className: css({
2839
+ fontSize: '90%',
2840
+ marginBottom: padding
2841
+ }) }, subTextChoice));
2842
+ }
2843
+ if (props.noWrap) {
2844
+ return (React.createElement("span", { className: cx(labelStyles, outerClass) },
2845
+ React.createElement("label", { htmlFor: props.htmlFor, className: outerClass }, labelText),
2846
+ props.children));
2847
+ }
2848
+ const content = React.createElement(React.Fragment, null,
2849
+ labelText,
2850
+ React.createElement("span", { className: css(labelContentStyles) }, props.children));
2851
+ if (props.static) {
2852
+ return (React.createElement("span", { className: cx(labelStyles, outerClass) }, content));
2853
+ }
2854
+ // labels without htmlFor can cause focus, hover, and click issues when wrapping other elements.
2855
+ // this fixes the issues.
2856
+ const htmlFor = (_c = props.htmlFor) !== null && _c !== void 0 ? _c : nanoid();
2857
+ return (React.createElement("label", Object.assign({}, labelProps, { htmlFor: htmlFor, className: cx('label', labelStyles, outerClass) }), content));
2858
+ };
2859
+
2860
+ /*
2861
+ From https://fireship.io/snippets/use-media-query-hook/.
2862
+ Tried using https://www.npmjs.com/package/react-media, but it cause Webpack build issues.
2863
+ */
2864
+ /** React wrapper around window resizing and window.matchMedia.
2865
+ * https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia
2866
+ */
2867
+ const useMediaQuery = (query) => {
2868
+ const [matches, setMatches] = useState(window.matchMedia(query).matches);
2869
+ useEffect(() => {
2870
+ const media = window.matchMedia(query);
2871
+ if (media.matches !== matches) {
2872
+ setMatches(media.matches);
2873
+ }
2874
+ const listener = () => setMatches(media.matches);
2875
+ window.addEventListener("resize", listener);
2876
+ return () => window.removeEventListener("resize", listener);
2877
+ }, [matches, query]);
2878
+ return matches;
2879
+ };
2880
+
2881
+ const Nav = (props) => {
2882
+ var _a, _b, _c;
2883
+ const nav = React.useRef(null);
2884
+ const theme = useThemeSafely();
2885
+ const totalNavOffset = `calc(${theme.layout.navWidth} + 20px)`;
2886
+ const backdrop = React.useContext(BackdropContext);
2887
+ const isLargeScreen = useMediaQuery(`(min-width:${theme.breakpoints.desktop})`);
2888
+ const log = useLogger(`Nav ${(_a = props.id) !== null && _a !== void 0 ? _a : '?'}`, (_b = props.__debug) !== null && _b !== void 0 ? _b : false);
2889
+ const slideMs = (_c = props.slideMs) !== null && _c !== void 0 ? _c : theme.timings.nav.slideMs;
2890
+ const slideRight = keyframes `
2891
+ 0% {
2892
+ transform: translateX(0);
2893
+ }
2894
+
2895
+ 100% {
2896
+ transform: translateX(${totalNavOffset});
2897
+ }
2898
+ `;
2899
+ const slideLeft = keyframes `
2900
+ 0% {
2901
+ transform: translateX(${totalNavOffset});
2902
+ }
2903
+
2904
+ 100% {
2905
+ transform: translateX(0px);
2906
+ }
2907
+ `;
2908
+ const classNavShowing = css `
2909
+ animation: ${slideRight} ${slideMs}ms cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
2910
+ `;
2911
+ const classNavNotShowing = css `
2912
+ animation: ${slideLeft} ${slideMs}ms cubic-bezier(0.250, 0.460, 0.450, 0.940) both;
2913
+ `;
2914
+ // the padding-top here is to offset the navs' content from the header. the shadow creeps over it.
2915
+ const navStyles = css `
2916
+ label: Nav;
2917
+ position: fixed;
2918
+ top: 0;
2919
+ left: calc(${totalNavOffset} * -1);
2920
+ bottom: 0;
2921
+ background-color: ${theme.colors.nav};
2922
+ color: ${theme.colors.navFont};
2923
+ width: ${theme.layout.navWidth};
2924
+ min-width: ${theme.layout.navWidth};
2925
+ box-shadow: 4px 2px 12px 6px rgba(0, 0, 0, 0.2);
2926
+ z-index: ${theme.zIndexes.nav};
2927
+ overflow-y: auto;
2928
+ .omniLink, .omniLink:active, .omniLink:focus, .omniLink:visited {
2929
+ color: ${theme.colors.navFont};
2930
+ }
2931
+ padding-top:0;
2932
+ ${props.responsive && `
2933
+ @media(min-width:${theme.breakpoints.desktop}) {
2934
+ position: relative;
2935
+ left: 0;
2936
+ box-shadow: none;
2937
+ z-index: 1;
2938
+ animation: none !important;
2939
+ margin-right: 1rem;
2940
+ overflow-y: visible;
2941
+ margin-top:-2rem;
2942
+ padding-top: 1rem;
2943
+ }
2944
+ `}
2945
+ `;
2946
+ React.useEffect(() => {
2947
+ if (!backdrop.showing) {
2948
+ props.toggle(false);
2949
+ }
2950
+ }, [backdrop.showing]);
2951
+ useBooleanChanged((current, previous) => {
2952
+ var _a;
2953
+ log('show changed', `${previous !== null && previous !== void 0 ? previous : 'undefined'} > ${current}`);
2954
+ backdrop.setShow(current, (_a = props.id) !== null && _a !== void 0 ? _a : 'Nav');
2955
+ }, props.show);
2956
+ React.useLayoutEffect(() => {
2957
+ if (nav && nav.current) {
2958
+ if (props.show) {
2959
+ if (!nav.current.classList.contains(classNavShowing)) {
2960
+ nav.current.classList.add(classNavShowing);
2961
+ }
2962
+ }
2963
+ else {
2964
+ if (nav.current.classList.contains(classNavShowing)) {
2965
+ nav.current.classList.remove(classNavShowing);
2966
+ nav.current.classList.add(classNavNotShowing);
2967
+ setTimeout(() => {
2968
+ if (nav && nav.current) {
2969
+ nav.current.classList.remove(classNavNotShowing);
2970
+ }
2971
+ }, slideMs);
2972
+ }
2973
+ }
2974
+ }
2975
+ });
2976
+ if (props.responsive) {
2977
+ React.useEffect(() => {
2978
+ if (backdrop.showing) {
2979
+ props.toggle(false);
2980
+ }
2981
+ }, [isLargeScreen]);
2982
+ }
2983
+ return (React.createElement("nav", { ref: nav, className: cx('nav', navStyles, props.className) }, props.children));
2984
+ };
2985
+
2986
+ const LinkContent = (props) => {
2987
+ return (React.createElement(React.Fragment, null,
2988
+ React.createElement("span", null,
2989
+ props.leftIcon && React.createElement("span", { className: css({
2990
+ label: 'LinkLeftIcon',
2991
+ marginRight: '1rem',
2992
+ display: 'inline-block',
2993
+ minWidth: '1.5rem',
2994
+ verticalAlign: 'middle'
2995
+ }) }, props.leftIcon),
2996
+ props.children),
2997
+ props.rightIcon && React.createElement("span", { className: css({
2998
+ label: 'LinkRightIcon',
2999
+ marginLeft: '1rem',
3000
+ verticalAlign: 'middle'
3001
+ }) }, props.rightIcon)));
3002
+ };
3003
+
3004
+ //TB: FUTURE de-dup these styles. create individual styles and compose them manually.
3005
+ const generateLinkStyles = (props, theme) => {
3006
+ var _a, _b, _c, _d, _e;
3007
+ const linkStyles = css `
3008
+ label: Link;
3009
+ display: inline-block;
3010
+ cursor: pointer;
3011
+ text-decoration: none;
3012
+ color: ${(_a = props.colorOverride) !== null && _a !== void 0 ? _a : theme.colors.link};
3013
+ transition: ${theme.controls.transition};
3014
+ &:hover {
3015
+ filter: ${theme.controls.hoverBrightness};
3016
+ text-decoration: underline;
3017
+ }
3018
+ &:active,
3019
+ &:visited {
3020
+ color: ${(_b = props.colorOverride) !== null && _b !== void 0 ? _b : theme.colors.link};
3021
+ }
3022
+ &:focus {
3023
+ text-decoration: underline;
3024
+ outline: none;
3025
+ color: ${(_c = props.colorOverride) !== null && _c !== void 0 ? _c : theme.colors.link};
3026
+ }
3027
+ ${!!props.variant && `
3028
+ padding-left: ${theme.controls.padding};
3029
+ padding-right: ${theme.controls.padding};
3030
+ border: ${theme.controls.border};
3031
+ border-radius: ${theme.controls.borderRadius};
3032
+ box-shadow: ${theme.controls.buttonBoxShadow};
3033
+ height: ${theme.controls.height};
3034
+ line-height: ${theme.controls.height};
3035
+ font-weight: bold;
3036
+ color: ${(_d = props.colorOverride) !== null && _d !== void 0 ? _d : theme.colors.font};
3037
+ &:active,
3038
+ &:focus,
3039
+ &:visited {
3040
+ color: ${(_e = props.colorOverride) !== null && _e !== void 0 ? _e : theme.colors.font};
3041
+ }
3042
+ &:focus {
3043
+ outline: none;
3044
+ box-shadow: ${theme.controls.focusOutlineShadow};
3045
+ text-decoration: none;
3046
+ }
3047
+ &:active {
3048
+ box-shadow: none;
3049
+ }
3050
+ &:hover {
3051
+ text-decoration: none;
3052
+ }
3053
+ `}
3054
+ ${props.variant === 'button' && `
3055
+ text-align: center;
3056
+ &:hover {
3057
+ background-color: ${theme.controls.hoverBackground};
3058
+ }
3059
+ `}
3060
+ ${props.variant === 'circle' && `
3061
+ text-align: center;
3062
+ &:hover {
3063
+ background-color: ${theme.controls.hoverBackground};
3064
+ }
3065
+ width: ${theme.controls.height};
3066
+ border-radius: 100%;
3067
+ display: flex;
3068
+ justify-content: center;
3069
+ align-items: center;
3070
+ ${props.small && `
3071
+ width: ${theme.controls.heightSmall};
3072
+ min-width: ${theme.controls.heightSmall};
3073
+ `}
3074
+ `}
3075
+ ${props.variant === 'primary' && `
3076
+ text-align: center;
3077
+ background-color: ${theme.colors.primary};
3078
+ color: ${theme.colors.primaryFont};
3079
+ &:active,
3080
+ &:focus,
3081
+ &:visited {
3082
+ color: ${theme.colors.primaryFont};
3083
+ }
3084
+ `}
3085
+ ${props.variant === 'primary2' && `
3086
+ text-align: center;
3087
+ background-color: ${theme.colors.primary2};
3088
+ color: ${theme.colors.primary2Font};
3089
+ &:active,
3090
+ &:focus,
3091
+ &:visited {
3092
+ color: ${theme.colors.primary2Font};
3093
+ }
3094
+ `}
3095
+ ${props.variant === 'secondary' && `
3096
+ text-align: center;
3097
+ background-color: ${theme.colors.secondary};
3098
+ color: ${theme.colors.secondary2Font};
3099
+ &:active,
3100
+ &:focus,
3101
+ &:visited {
3102
+ color: ${theme.colors.secondary2Font};
3103
+ }
3104
+ `}
3105
+ ${props.variant === 'positive' && `
3106
+ text-align: center;
3107
+ background-color: ${theme.colors.positive};
3108
+ color: ${theme.colors.positiveFont};
3109
+ &:active,
3110
+ &:focus,
3111
+ &:visited {
3112
+ color: ${theme.colors.positiveFont};
3113
+ }
3114
+ `}
3115
+ ${props.variant === 'negative' && `
3116
+ text-align: center;
3117
+ background-color: ${theme.colors.negative};
3118
+ color: ${theme.colors.negativeFont};
3119
+ &:active,
3120
+ &:focus,
3121
+ &:visited {
3122
+ color: ${theme.colors.negativeFont};
3123
+ }
3124
+ `}
3125
+ ${props.variant === 'omg' && `
3126
+ text-align: center;
3127
+ background-color: ${theme.colors.omg};
3128
+ color: ${theme.colors.omgFont};
3129
+ &:active,
3130
+ &:focus,
3131
+ &:visited {
3132
+ color: ${theme.colors.omgFont};
3133
+ }
3134
+ `}
3135
+ ${props.variant === 'label' && `
3136
+ box-shadow: none;
3137
+ border: none;
3138
+ &:hover {
3139
+ background-color: ${theme.controls.hoverBackground};
3140
+ }
3141
+ `}
3142
+ ${props.variant === 'text' && `
3143
+ font-weight: normal;
3144
+ box-shadow: none;
3145
+ border: none;
3146
+ cursor: auto;
3147
+ `}
3148
+ ${props.variant === 'icon' && `
3149
+ box-shadow: none;
3150
+ border: none;
3151
+ border-radius: 100%;
3152
+ width: ${theme.controls.height};
3153
+ text-align: center;
3154
+ font-size: 1.6rem;
3155
+ padding-left: 0;
3156
+ padding-right: 0;
3157
+ &:hover {
3158
+ background-color: ${theme.controls.hoverBackground};
3159
+ }
3160
+ `}
3161
+ ${props.block && `
3162
+ display: block;
3163
+ width:100%;
3164
+ `}
3165
+ ${props.iconBlock && `
3166
+ display: flex;
3167
+ justify-content: space-between;
3168
+ align-items: center;
3169
+ `}
3170
+ ${props.round && `
3171
+ border-radius: ${theme.controls.roundRadius};
3172
+ `}
3173
+ ${props.small && `
3174
+ font-size: 0.8rem;
3175
+ height: ${theme.controls.heightSmall};
3176
+ line-height: ${theme.controls.heightSmall};
3177
+ `}
3178
+ `;
3179
+ return linkStyles;
3180
+ };
3181
+
3182
+ const OmniLink = (props) => {
3183
+ const { noRouter, rightIcon, leftIcon, block, iconBlock, variant, round, small, colorOverride, children, ref } = props, linkProps = __rest(props, ["noRouter", "rightIcon", "leftIcon", "block", "iconBlock", "variant", "round", "small", "colorOverride", "children", "ref"]);
3184
+ const theme = useThemeSafely();
3185
+ const linkStyles = generateLinkStyles(props, theme);
3186
+ const mainClassName = cx('omniLink', linkStyles, props.className);
3187
+ if (variant === 'text') {
3188
+ return React.createElement(Text, { className: mainClassName, tag: "div" }, props.children);
3189
+ }
3190
+ const content = React.createElement(LinkContent, Object.assign({}, props));
3191
+ if (props.noRouter) {
3192
+ return (React.createElement("a", Object.assign({}, linkProps, { target: props.target, className: mainClassName }), content));
3193
+ }
3194
+ return (React.createElement(Link$1, Object.assign({}, linkProps, { className: mainClassName, to: props.href }), content));
3195
+ };
3196
+
3197
+ const roundPxPadding = '4px';
3198
+ const Picker = (props) => {
3199
+ const selectProps = __rest(props
3200
+ // if we put numbers in, we expect them out
3201
+ , ["value", "options", "onValueChange", "readOnly", "round", "controlAlign", "wrapperClassName", "iconClassName"]);
3202
+ // if we put numbers in, we expect them out
3203
+ let isNumber = false;
3204
+ if (props.options && props.options.length) {
3205
+ const testOption = props.options[0];
3206
+ if (typeof testOption === 'object') {
3207
+ isNumber = typeof testOption.id === 'number';
3208
+ }
3209
+ else {
3210
+ isNumber = typeof testOption === 'number';
3211
+ }
3212
+ }
3213
+ const theme = useThemeSafely();
3214
+ const selectStyles = css({
3215
+ // fix the arrow so browser all look the same
3216
+ appearance: 'none',
3217
+ paddingLeft: theme.controls.padding,
3218
+ paddingRight: `calc(${theme.controls.padding} + 1rem)`,
3219
+ backgroundColor: theme.colors.bg,
3220
+ color: theme.colors.font,
3221
+ height: theme.controls.height,
3222
+ fontSize: theme.controls.fontSize,
3223
+ width: '100%',
3224
+ border: theme.controls.border,
3225
+ borderRadius: theme.controls.borderRadius || 0,
3226
+ transition: theme.controls.transition,
3227
+ '&:disabled': {
3228
+ color: theme.colors.font,
3229
+ opacity: 1,
3230
+ backgroundColor: theme.colors.disabled,
3231
+ cursor: 'not-allowed'
3232
+ },
3233
+ '&:focus': {
3234
+ outline: 'none',
3235
+ boxShadow: theme.controls.focusOutlineShadow
3236
+ }
3237
+ }, props.round && {
3238
+ borderRadius: theme.controls.roundRadius,
3239
+ paddingLeft: `calc(${theme.controls.padding} + ${roundPxPadding})`,
3240
+ paddingRight: `calc(${theme.controls.padding} + 1rem + ${roundPxPadding})`,
3241
+ }, props.readOnly && {
3242
+ backgroundColor: 'transparent !important',
3243
+ backgroundImage: 'unset',
3244
+ border: 'none',
3245
+ '&:focus': {
3246
+ outline: 'none',
3247
+ boxShadow: 'none'
3248
+ }
3249
+ });
3250
+ const select = (React.createElement("select", Object.assign({}, selectProps, { tabIndex: props.readOnly ? -1 : selectProps.tabIndex, className: cx('picker', selectStyles, props.className), value: props.value, onKeyDown: e => {
3251
+ var _a;
3252
+ if (props.readOnly) {
3253
+ if (e.keyCode === 9) {
3254
+ //TAB
3255
+ return;
3256
+ }
3257
+ e.preventDefault();
3258
+ e.stopPropagation();
3259
+ }
3260
+ else {
3261
+ (_a = selectProps.onKeyDown) === null || _a === void 0 ? void 0 : _a.call(selectProps, e);
3262
+ }
3263
+ }, onMouseDown: e => {
3264
+ var _a;
3265
+ if (props.readOnly) {
3266
+ e.preventDefault();
3267
+ e.stopPropagation();
3268
+ }
3269
+ else {
3270
+ (_a = selectProps.onMouseDown) === null || _a === void 0 ? void 0 : _a.call(selectProps, e);
3271
+ }
3272
+ }, onChange: e => {
3273
+ var _a;
3274
+ let val = e.target.value;
3275
+ if (isNumber) {
3276
+ val = parseInt(val, 10);
3277
+ if (isNaN(val)) {
3278
+ val = '';
3279
+ }
3280
+ }
3281
+ props.onValueChange(val);
3282
+ (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
3283
+ } }), (props.options || []).map(o => {
3284
+ var _a;
3285
+ let val;
3286
+ let label;
3287
+ if (typeof o === 'object') {
3288
+ val = o.id;
3289
+ label = (_a = o.name) !== null && _a !== void 0 ? _a : o.id;
3290
+ }
3291
+ else {
3292
+ val = o;
3293
+ label = o;
3294
+ }
3295
+ return React.createElement("option", { key: val, value: val }, label);
3296
+ })));
3297
+ let iconHeight;
3298
+ if (props.controlAlign) {
3299
+ iconHeight = `calc(100% - ${theme.controls.inputErrorMinHeight})`;
3300
+ }
3301
+ else {
3302
+ iconHeight = '100%';
3303
+ }
3304
+ return (React.createElement("span", { className: cx(css({
3305
+ label: 'PickerWrapper',
3306
+ position: 'relative',
3307
+ display: 'inline-block',
3308
+ width: '100%',
3309
+ paddingBottom: props.controlAlign ? theme.controls.inputErrorMinHeight : undefined
3310
+ }), props.wrapperClassName) },
3311
+ select,
3312
+ !props.readOnly && (React.createElement(Icon, { id: "sortDesc", className: cx(css({
3313
+ position: 'absolute',
3314
+ right: `calc(${theme.controls.padding} + ${props.round ? roundPxPadding : '0px'})`,
3315
+ height: iconHeight,
3316
+ pointerEvents: 'none',
3317
+ color: theme.colors.font
3318
+ }), props.iconClassName) }))));
3319
+ };
3320
+
3321
+ const Pager = (props) => {
3322
+ var _a;
3323
+ const canGoNext = props.canGoNext && props.totalItems > 0;
3324
+ const canGoPrevious = props.canGoPrevious && props.totalItems > 0;
3325
+ const dividerText = props.itemDividerText || 'of';
3326
+ let itemText = '';
3327
+ if (props.totalItems > 0) {
3328
+ itemText = `${props.minItem.toLocaleString()}-${props.maxItem.toLocaleString()} ${dividerText} ${props.totalItems.toLocaleString()}`;
3329
+ }
3330
+ else {
3331
+ itemText = props.noResultsText || 'No Results';
3332
+ }
3333
+ let pageText;
3334
+ if (props.pageIndex !== undefined && props.totalPages) {
3335
+ pageText = `${(_a = props.pageText) !== null && _a !== void 0 ? _a : 'Page'} ${(props.pageIndex + 1).toLocaleString()} ${dividerText} ${props.totalPages.toLocaleString()}`;
3336
+ }
3337
+ const theme = useThemeSafely();
3338
+ const pagerStyles = css `
3339
+ display: grid;
3340
+ grid-template-columns: ${theme.controls.height} 1fr ${theme.controls.height};
3341
+ grid-column-gap: ${theme.controls.gap};
3342
+ border: ${theme.controls.border};
3343
+ border-radius: ${theme.controls.borderRadius};
3344
+ padding: 0.5rem;
3345
+ background-color: ${theme.colors.pagerBg};
3346
+ @media(min-width: ${theme.breakpoints.tablet}) {
3347
+ grid-template-columns: ${theme.controls.height} 1fr 1fr 1fr ${theme.controls.height};
3348
+ }
3349
+ `;
3350
+ const controlStyles = css `
3351
+ display: none;
3352
+ padding:0 1rem;
3353
+ justify-self: center;
3354
+ @media(min-width: ${theme.breakpoints.tablet}) {
3355
+ display: block;
3356
+ }
3357
+ `;
3358
+ const buttonStyles = css({
3359
+ backgroundColor: 'transparent'
3360
+ });
3361
+ return (React.createElement("div", { className: cx(pagerStyles, props.className) },
3362
+ React.createElement(Button, { className: buttonStyles, disabled: !canGoPrevious, onClick: () => props.page(-1), variant: "icon" },
3363
+ React.createElement(Icon, { id: "pagerLeft" })),
3364
+ React.createElement("div", { className: controlStyles }, props.leftControls),
3365
+ React.createElement("div", { className: css({
3366
+ alignSelf: 'center',
3367
+ justifySelf: 'center',
3368
+ display: 'flex',
3369
+ gap: '0.5rem',
3370
+ flexDirection: 'column'
3371
+ }) },
3372
+ React.createElement(Text, { tag: "span", align: "center" }, itemText),
3373
+ pageText && (React.createElement(Text, { tag: "span", smaller: true, align: "center" }, pageText))),
3374
+ React.createElement("div", { className: controlStyles }, props.rightControls),
3375
+ React.createElement(Button, { className: buttonStyles, disabled: !canGoNext, onClick: () => props.page(1), variant: "icon" },
3376
+ React.createElement(Icon, { id: "pagerRight" }))));
3377
+ };
3378
+
3379
+ const BoundMemoryPager = (p) => {
3380
+ var _a, _b, _c;
3381
+ const { pager, showPageText } = p, rest = __rest(p, ["pager", "showPageText"]);
3382
+ return (React.createElement(Pager, Object.assign({}, rest, { pageIndex: p.showPageText ? pager.page : undefined, totalPages: p.showPageText ? pager.totalPages : undefined, canGoNext: pager.hasNext, canGoPrevious: pager.hasPrevious, minItem: pager.minItemIndex + 1, maxItem: pager.maxItemIndex + 1, totalItems: pager.totalItems, leftControls: pager.limitOptions.length > 1 && p.onLimit ? (React.createElement(Label, { text: (_a = p.limitText) !== null && _a !== void 0 ? _a : 'Limit', orientation: "horizontal" },
3383
+ React.createElement(Picker, { value: pager.limit, options: pager.limitOptions, onValueChange: v => { var _a; return (_a = p.onLimit) === null || _a === void 0 ? void 0 : _a.call(p, v); } }))) : undefined, rightControls: pager.sortOptions.length > 1 && p.onSort ? (React.createElement(Label, { text: (_b = p.sortText) !== null && _b !== void 0 ? _b : 'Sort', orientation: "horizontalReverse" },
3384
+ React.createElement(Picker, { value: (_c = pager.sort) !== null && _c !== void 0 ? _c : '', options: pager.sortOptions, onValueChange: v => { var _a; return (_a = p.onSort) === null || _a === void 0 ? void 0 : _a.call(p, v); } }))) : undefined, page: d => {
3385
+ p.onPage(d);
3386
+ } })));
3387
+ };
3388
+
3389
+ const BoundStaticPager = (p) => {
3390
+ var _a, _b, _c, _d, _e, _f;
3391
+ const { result, showPageText } = p, rest = __rest(p, ["result", "showPageText"]);
3392
+ const showLimit = !!(result.limit && p.limitOptions && p.limitOptions.length > 1 && p.onLimit);
3393
+ const showSort = !!(p.sort !== undefined && p.sortOptions && p.sortOptions.length > 1 && p.onSort);
3394
+ return (React.createElement(Pager, Object.assign({}, rest, { pageIndex: p.showPageText ? result.page : undefined, totalPages: p.showPageText ? result.totalPages : undefined, canGoNext: result.hasNext, canGoPrevious: result.hasPrevious, minItem: result.minPageItemIndex + 1, maxItem: result.maxPageItemIndex + 1, totalItems: result.total, leftControls: showLimit ? (React.createElement(Label, { text: (_a = p.limitText) !== null && _a !== void 0 ? _a : 'Limit', orientation: "horizontal" },
3395
+ React.createElement(Picker, { value: (_b = result.limit) !== null && _b !== void 0 ? _b : 1, options: (_c = p.limitOptions) !== null && _c !== void 0 ? _c : [1], onValueChange: v => { var _a; return (_a = p.onLimit) === null || _a === void 0 ? void 0 : _a.call(p, v); } }))) : undefined, rightControls: showSort ? (React.createElement(Label, { text: (_d = p.sortText) !== null && _d !== void 0 ? _d : 'Sort', orientation: "horizontalReverse" },
3396
+ React.createElement(Picker, { value: (_e = p.sort) !== null && _e !== void 0 ? _e : '', options: (_f = p.sortOptions) !== null && _f !== void 0 ? _f : [], onValueChange: v => {
3397
+ var _a;
3398
+ (_a = p.onSort) === null || _a === void 0 ? void 0 : _a.call(p, v);
3399
+ } }))) : undefined, page: d => {
3400
+ p.onPage(d);
3401
+ } })));
3402
+ };
3403
+
3404
+ /** A page of data with helpers props. */
3405
+ class PagedResult {
3406
+ constructor(items = [], total = 0, page = 0, limit = 0) {
3407
+ this.items = items;
3408
+ this.page = page;
3409
+ this.limit = limit;
3410
+ this.total = total;
3411
+ }
3412
+ static fromDto(dto) {
3413
+ return new PagedResult(dto.items, dto.total, dto.page, dto.limit);
3414
+ }
3415
+ /** Helper for items.length */
3416
+ get length() {
3417
+ return this.items ? this.items.length : 0;
3418
+ }
3419
+ get hasItems() {
3420
+ return !!this.length;
3421
+ }
3422
+ get previousPage() {
3423
+ return Math.max(this.page - 1, 0);
3424
+ }
3425
+ get hasPrevious() {
3426
+ return this.previousPage !== this.page;
3427
+ }
3428
+ get nextPage() {
3429
+ if (!this.total || !this.limit) {
3430
+ return 0;
3431
+ }
3432
+ return Math.min(this.page + 1, this.lastPage);
3433
+ }
3434
+ get hasNext() {
3435
+ return this.page !== this.lastPage;
3436
+ }
3437
+ get lastPage() {
3438
+ return Math.max(this.totalPages - 1, 0);
3439
+ }
3440
+ get totalPages() {
3441
+ if (!this.total || !this.limit) {
3442
+ return 0;
3443
+ }
3444
+ return Math.floor(this.total / this.limit) + (this.total % this.limit ? 1 : 0);
3445
+ }
3446
+ get minPageItemIndex() {
3447
+ if (!this.total || !this.limit) {
3448
+ return 0;
3449
+ }
3450
+ return this.page * this.limit;
3451
+ }
3452
+ get maxPageItemIndex() {
3453
+ if (!this.total || !this.limit) {
3454
+ return 0;
3455
+ }
3456
+ return Math.min(this.minPageItemIndex + this.limit - 1, this.total - 1);
3457
+ }
3458
+ /** Returns the first item on the current page. */
3459
+ get firstPageItem() {
3460
+ return this.items[0];
3461
+ }
3462
+ /** Returns the last item on the current page */
3463
+ get lastPageItem() {
3464
+ return this.items[this.items.length - 1];
3465
+ }
3466
+ getPageRelativeItemIndex(item) {
3467
+ return this.items.indexOf(item);
3468
+ }
3469
+ getResultRelativeItemIndex(item) {
3470
+ const index = this.getPageRelativeItemIndex(item);
3471
+ if (index >= 0) {
3472
+ return this.getPageRelativeItemIndex(item) + this.limit * this.page;
3473
+ }
3474
+ return undefined;
3475
+ }
3476
+ getPreviousItem(fromItem) {
3477
+ const index = this.getPageRelativeItemIndex(fromItem);
3478
+ if (index <= 0) {
3479
+ return undefined;
3480
+ }
3481
+ return this.items[index - 1];
3482
+ }
3483
+ getNextItem(fromItem) {
3484
+ const index = this.getPageRelativeItemIndex(fromItem);
3485
+ if (index < 0) {
3486
+ return undefined;
3487
+ }
3488
+ return this.items[index + 1];
3489
+ }
3490
+ getPagingRange(radius) {
3491
+ const minItems = radius * 2 + 1;
3492
+ const indexes = [];
3493
+ let startIndex = 0;
3494
+ let endIndex = 0;
3495
+ if (this.totalPages > minItems) {
3496
+ startIndex = this.page - radius;
3497
+ endIndex = this.page + radius;
3498
+ if (endIndex > this.lastPage) {
3499
+ startIndex -= endIndex - this.lastPage;
3500
+ endIndex = this.lastPage;
3501
+ }
3502
+ }
3503
+ else {
3504
+ startIndex = 0;
3505
+ endIndex = this.lastPage;
3506
+ }
3507
+ for (let i = startIndex; i <= endIndex; i++) {
3508
+ if (i < 0) {
3509
+ endIndex++;
3510
+ }
3511
+ else {
3512
+ indexes.push(i);
3513
+ }
3514
+ }
3515
+ return indexes;
3516
+ }
3517
+ toJSON() {
3518
+ const json = {
3519
+ items: this.items.map(item => {
3520
+ if (typeof item === 'object') {
3521
+ if ('toJSON' in item) {
3522
+ return item.toJSON();
3523
+ }
3524
+ }
3525
+ return item;
3526
+ }),
3527
+ page: this.page,
3528
+ limit: this.limit,
3529
+ total: this.total
3530
+ };
3531
+ return json;
3532
+ }
3533
+ clone(newItems) {
3534
+ return new PagedResult(newItems || this.items.slice(), this.total, this.page, this.limit);
3535
+ }
3536
+ }
3537
+
3538
+ /** In-memory pager. */
3539
+ class ItemPager {
3540
+ constructor(allItems, options) {
3541
+ var _a, _b, _c, _d;
3542
+ this._page = 0;
3543
+ this._allItems = allItems || [];
3544
+ this._limitOptions = ((_a = options === null || options === void 0 ? void 0 : options.previous) === null || _a === void 0 ? void 0 : _a.limitOptions) || (options === null || options === void 0 ? void 0 : options.limitOptions) || [10];
3545
+ this._limit = ((_b = options === null || options === void 0 ? void 0 : options.previous) === null || _b === void 0 ? void 0 : _b.limit) || (options === null || options === void 0 ? void 0 : options.limit) || 10;
3546
+ this._filteredItems = [...allItems];
3547
+ this._pageResult = this.createPageResult();
3548
+ this._sortOptions = ((_c = options === null || options === void 0 ? void 0 : options.previous) === null || _c === void 0 ? void 0 : _c.sortOptions) || (options === null || options === void 0 ? void 0 : options.sortOptions) || [];
3549
+ this.sort = ((_d = options === null || options === void 0 ? void 0 : options.previous) === null || _d === void 0 ? void 0 : _d.sort) || (options === null || options === void 0 ? void 0 : options.sort);
3550
+ if (options === null || options === void 0 ? void 0 : options.previous) {
3551
+ if (options.previous._currentFilter) {
3552
+ this.applyFilter(options.previous._currentFilter);
3553
+ }
3554
+ this.page = options.previous.page;
3555
+ }
3556
+ }
3557
+ get allItems() {
3558
+ return this._allItems;
3559
+ }
3560
+ /** The ID of the current sort within the sortOptions. */
3561
+ get sort() {
3562
+ return this._sort;
3563
+ }
3564
+ set sort(sortId) {
3565
+ const option = this._sortOptions.find(o => o.id === sortId);
3566
+ if (!option) {
3567
+ return;
3568
+ }
3569
+ this._sort = sortId;
3570
+ this._allItems = option.sort(this._allItems);
3571
+ this._filteredItems = option.sort(this._filteredItems);
3572
+ this.page = 0;
3573
+ }
3574
+ /** The direction of the current sort when using an option created from addToggleSortOption */
3575
+ get sortDirection() {
3576
+ if (!this.sort) {
3577
+ return undefined;
3578
+ }
3579
+ const [, direction] = this.sort.split('-');
3580
+ return direction;
3581
+ }
3582
+ get sortOptions() {
3583
+ return this._sortOptions;
3584
+ }
3585
+ get limit() {
3586
+ return this._limit;
3587
+ }
3588
+ set limit(value) {
3589
+ if (value >= 0) {
3590
+ this._limit = value;
3591
+ this.page = 0;
3592
+ }
3593
+ }
3594
+ get limitOptions() {
3595
+ return this._limitOptions;
3596
+ }
3597
+ get totalPages() {
3598
+ return this._pageResult.totalPages;
3599
+ }
3600
+ /** The zero-based page index. */
3601
+ get page() {
3602
+ return this._page;
3603
+ }
3604
+ /** The zero-based page index. */
3605
+ set page(value) {
3606
+ if (value >= 0) {
3607
+ this._page = Math.min(value, this._pageResult.lastPage);
3608
+ this._pageResult = this.createPageResult();
3609
+ }
3610
+ }
3611
+ get pageItems() {
3612
+ return this._pageResult.items;
3613
+ }
3614
+ get totalItems() {
3615
+ return this._pageResult.total;
3616
+ }
3617
+ get minItemIndex() {
3618
+ return this._pageResult.minPageItemIndex;
3619
+ }
3620
+ get maxItemIndex() {
3621
+ return this._pageResult.maxPageItemIndex;
3622
+ }
3623
+ get hasNext() {
3624
+ return this._pageResult.hasNext;
3625
+ }
3626
+ get hasPrevious() {
3627
+ return this._pageResult.hasPrevious;
3628
+ }
3629
+ /**
3630
+ * Adds both asc and des sort options
3631
+ *
3632
+ * @param propPath Prop name or path ('resource.title').
3633
+ * @param name Name to display or asc Name and desc Name.
3634
+ */
3635
+ addToggleSortOption(propPath, name) {
3636
+ if (!this._sortOptions) {
3637
+ this._sortOptions = [];
3638
+ }
3639
+ const sort = (item) => {
3640
+ const value = get(item, propPath);
3641
+ if (typeof value === 'string') {
3642
+ return value.toLocaleLowerCase();
3643
+ }
3644
+ return value;
3645
+ };
3646
+ let ascName;
3647
+ let descName;
3648
+ if (name) {
3649
+ if (Array.isArray(name)) {
3650
+ ascName = name[0];
3651
+ descName = name[1];
3652
+ }
3653
+ else {
3654
+ ascName = name;
3655
+ descName = name;
3656
+ }
3657
+ }
3658
+ this._sortOptions.push({ id: `${propPath}-asc`, name: ascName, sort: (items) => orderBy(items, sort, 'asc') });
3659
+ this._sortOptions.push({ id: `${propPath}-desc`, name: descName, sort: (items) => orderBy(items, sort, 'desc') });
3660
+ }
3661
+ /**
3662
+ * Toggles between asc and desc.
3663
+ * @param sortId The ID or partial ID (no '-asc' or '-desc') of a sort option created through addToggleSortOption
3664
+ */
3665
+ toggleSort(sortId) {
3666
+ const [id,] = sortId.split('-');
3667
+ let nextSort;
3668
+ if (!this.sort || !this.sort.startsWith(id)) {
3669
+ nextSort = `${id}-asc`;
3670
+ }
3671
+ else {
3672
+ if (this.sort.endsWith('-asc')) {
3673
+ nextSort = `${id}-desc`;
3674
+ }
3675
+ else {
3676
+ nextSort = `${id}-asc`;
3677
+ }
3678
+ }
3679
+ this.sort = nextSort;
3680
+ }
3681
+ applyFilter(filter, keepPage) {
3682
+ this._currentFilter = filter;
3683
+ if (!this._currentFilter) {
3684
+ this._filteredItems = [...this.allItems];
3685
+ }
3686
+ else {
3687
+ this._filteredItems = this.allItems.filter(this._currentFilter);
3688
+ }
3689
+ if (!keepPage) {
3690
+ this.page = 0;
3691
+ }
3692
+ else {
3693
+ // recreate the page results manually since we're not resetting the page
3694
+ this._pageResult = this.createPageResult();
3695
+ }
3696
+ }
3697
+ next() {
3698
+ if (this._pageResult.hasNext) {
3699
+ this.page = this._pageResult.nextPage;
3700
+ }
3701
+ }
3702
+ previous() {
3703
+ if (this._pageResult.hasPrevious) {
3704
+ this.page = this._pageResult.previousPage;
3705
+ }
3706
+ }
3707
+ pageByOffset(direction) {
3708
+ if (direction === 1) {
3709
+ this.next();
3710
+ }
3711
+ else {
3712
+ this.previous();
3713
+ }
3714
+ }
3715
+ /** Adds the item optionally keeping the current page. */
3716
+ add(item, position = 'end', keepPage = true) {
3717
+ if (position === 'start') {
3718
+ this._allItems.unshift(item);
3719
+ }
3720
+ else {
3721
+ this._allItems.push(item);
3722
+ }
3723
+ this.applyFilter(this._currentFilter, keepPage);
3724
+ }
3725
+ /** Removes the matched item(s). */
3726
+ remove(comparer, keepPage = true) {
3727
+ this._allItems = this.allItems.filter(i => comparer(i) === false);
3728
+ this.applyFilter(this._currentFilter, keepPage);
3729
+ if (!this.pageItems.length) {
3730
+ this.pageByOffset(-1);
3731
+ }
3732
+ }
3733
+ replace(newItem, comparer) {
3734
+ this.allItems.forEach((item, i) => {
3735
+ if (comparer && comparer(item)) {
3736
+ this.allItems[i] = newItem;
3737
+ }
3738
+ });
3739
+ this.applyFilter(this._currentFilter, true);
3740
+ }
3741
+ createPageResult() {
3742
+ const page = new PagedResult([], this._filteredItems.length, this.page, this.limit);
3743
+ page.items = this._filteredItems.slice(page.minPageItemIndex, page.maxPageItemIndex + 1) || [];
3744
+ return page;
3745
+ }
3746
+ }
3747
+
3748
+ const ProgressBar = (props) => {
3749
+ const bar = React.useRef(null);
3750
+ React.useEffect(() => {
3751
+ const pct = cleanPct(props.pct);
3752
+ if (bar.current) {
3753
+ bar.current.style.width = `${pct}%`;
3754
+ }
3755
+ });
3756
+ const theme = useThemeSafely();
3757
+ const barStyles = css `
3758
+ width: 100%;
3759
+ height: 1rem;
3760
+ border: ${theme.controls.border};
3761
+ background-color: ${theme.colors.progressBg};
3762
+ ${props.round && `
3763
+ border-radius: 1rem;
3764
+ `}
3765
+ `;
3766
+ const fillStyles = css `
3767
+ transition: width 0.5s ease-in-out;
3768
+ height: 100%;
3769
+ background-color: ${theme.colors.progressFill};
3770
+ ${props.round && `
3771
+ border-radius: 1rem;
3772
+ `}
3773
+ `;
3774
+ return (React.createElement("div", { className: "progressBar" },
3775
+ React.createElement("div", { className: cx(barStyles, props.className) },
3776
+ React.createElement("div", { ref: bar, className: fillStyles })),
3777
+ props.showPct && React.createElement(Text, { align: "center", tag: "div", spacedOut: true },
3778
+ props.pct,
3779
+ "\u00A0%")));
3780
+ };
3781
+ const cleanPct = (value) => {
3782
+ if (value) {
3783
+ return Math.min(Math.max(value, 0), 100);
3784
+ }
3785
+ return 0;
3786
+ };
3787
+
3788
+ /** Provides stateful notifications around async calls. */
3789
+ const useWaiting = (func) => {
3790
+ // Guard against the owner of this hook being unmounted at the time of .finally.
3791
+ const isCancelled = useRef(false);
3792
+ const [waiting, setWaiting] = useState(false);
3793
+ const wrappedFunc = (...args) => {
3794
+ setWaiting(true);
3795
+ return func(...args).finally(() => {
3796
+ if (!isCancelled.current) {
3797
+ setWaiting(false);
3798
+ }
3799
+ });
3800
+ };
3801
+ useEffect(() => {
3802
+ isCancelled.current = false;
3803
+ return () => {
3804
+ isCancelled.current = true;
3805
+ };
3806
+ }, []);
3807
+ return [waiting, wrappedFunc];
3808
+ };
3809
+
3810
+ const SearchBox = (props) => {
3811
+ const [waiting, onSubmit] = useWaiting(async () => {
3812
+ var _a, _b, _c;
3813
+ // the active element will be the input. if the submit action changes props.value it will
3814
+ // be ignored due to Inputs focused handling.
3815
+ if (document.activeElement) {
3816
+ (_b = (_a = document.activeElement).blur) === null || _b === void 0 ? void 0 : _b.call(_a);
3817
+ }
3818
+ return (_c = props.onSubmit) === null || _c === void 0 ? void 0 : _c.call(props);
3819
+ });
3820
+ const theme = useThemeSafely();
3821
+ const submitButton = (React.createElement(Button, { tabIndex: -1, disabled: waiting, readOnly: !props.onSubmit, type: "submit", className: cx(css({
3822
+ color: `${theme.colors.font} !important;`,
3823
+ fontSize: '1rem'
3824
+ }), props.buttonClassName), variant: "icon", small: true },
3825
+ React.createElement(Icon, { id: waiting ? 'waiting' : 'search', spin: waiting })));
3826
+ //TB: FUTURE replace with new inputs
3827
+ //TB: change the search icon to x when value is present
3828
+ const input = (React.createElement(Input, { inputClassName: props.inputClassName, autoFocus: props.autoFocus, id: props.id, debounceMs: props.debounceMs, disabled: waiting, type: "text", value: props.value, placeholder: props.placeholder, round: props.round, onChange: props.onChange, rightControl: submitButton }));
3829
+ const wrapperClassName = cx('searchBox', props.className);
3830
+ if (!props.noForm) {
3831
+ return (React.createElement(Form, { role: "search", className: wrapperClassName, onSubmit: onSubmit }, input));
3832
+ }
3833
+ return (React.createElement("div", { className: wrapperClassName }, input));
3834
+ };
3835
+
3836
+ const GlobalStyles = (p) => {
3837
+ const theme = useThemeSafely();
3838
+ injectGlobal({
3839
+ '*': {
3840
+ boxSizing: 'border-box'
3841
+ },
3842
+ 'html,body': {
3843
+ backgroundColor: theme.colors.bg,
3844
+ color: theme.colors.font,
3845
+ margin: 0
3846
+ },
3847
+ 'html,body,main': {
3848
+ height: '100%',
3849
+ fontFamily: theme.fonts.family,
3850
+ fontSize: theme.fonts.size
3851
+ },
3852
+ 'main': {
3853
+ height: 'auto',
3854
+ minHeight: '100%',
3855
+ padding: '0 1rem'
3856
+ },
3857
+ 'pre': {
3858
+ backgroundColor: theme.colors.lightBg
3859
+ }
3860
+ });
3861
+ return null;
3862
+ };
3863
+
3864
+ const Slider = (p) => {
3865
+ var _a;
3866
+ const theme = useThemeSafely();
3867
+ const currentValue = useRef(p.value);
3868
+ const sliderContainer = useRef(null);
3869
+ const sliderHandleSize = (_a = p.sliderHandleSize) !== null && _a !== void 0 ? _a : theme.controls.height;
3870
+ const height = p.showValue ? `calc(${sliderHandleSize} + 1.5rem)` : sliderHandleSize;
3871
+ return (React__default.createElement("div", { ref: sliderContainer, className: cx(css({
3872
+ label: 'Slider',
3873
+ width: '100%',
3874
+ height,
3875
+ }), p.className) },
3876
+ React__default.createElement(ReactSlider, { step: p.step, min: p.min, max: p.max, value: p.value, onAfterChange: (value, index) => {
3877
+ p.onChange(value);
3878
+ }, onChange: p.onUpdate || p.showValue ? (value, index) => {
3879
+ var _a;
3880
+ currentValue.current = value;
3881
+ (_a = p.onUpdate) === null || _a === void 0 ? void 0 : _a.call(p, value);
3882
+ } : undefined, renderTrack: (props, state) => {
3883
+ const { className } = props, rest = __rest(props, ["className"]);
3884
+ return (React__default.createElement("div", Object.assign({ className: cx(className, p.trackClassName, css({
3885
+ display: 'flex',
3886
+ alignItems: 'center',
3887
+ height: sliderHandleSize
3888
+ })) }, rest),
3889
+ React__default.createElement("div", { className: css({
3890
+ backgroundColor: theme.colors.secondary,
3891
+ height: `calc(${sliderHandleSize} / 4)`,
3892
+ borderRadius: theme.controls.roundRadius,
3893
+ width: '100%'
3894
+ }, p.innerTrackClassName && rest.key === 'track-1' && Array.isArray(p.value) ? p.innerTrackClassName : undefined) })));
3895
+ }, renderThumb: (props, state) => {
3896
+ const { className } = props, rest = __rest(props, ["className"]);
3897
+ let specificThumbStyles;
3898
+ if (p.handle1ClassName && props.key === 'thumb-0') {
3899
+ specificThumbStyles = p.handle1ClassName;
3900
+ }
3901
+ else if (p.handle2ClassName && props.key === 'thumb-1') {
3902
+ specificThumbStyles = p.handle2ClassName;
3903
+ }
3904
+ return (React__default.createElement("div", Object.assign({ className: cx(className, css({
3905
+ borderRadius: theme.controls.roundRadius,
3906
+ backgroundColor: 'white',
3907
+ border: theme.controls.border,
3908
+ cursor: 'grab',
3909
+ boxShadow: theme.controls.buttonBoxShadow,
3910
+ transition: theme.controls.transition,
3911
+ '&:focus': {
3912
+ outline: 'none',
3913
+ boxShadow: theme.controls.focusOutlineShadow
3914
+ },
3915
+ '&:active': {
3916
+ boxShadow: 'none',
3917
+ cursor: 'grabbing'
3918
+ },
3919
+ '&:hover': {
3920
+ filter: theme.controls.hoverBrightness
3921
+ }
3922
+ }), specificThumbStyles, p.handleClassName, css({
3923
+ width: sliderHandleSize,
3924
+ height: sliderHandleSize,
3925
+ })) }, rest), 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 }))));
3926
+ } })));
3927
+ };
3928
+ const rectsCollideX = (r1, r2) => {
3929
+ if (r1.left >= r2.left && r1.left <= r2.right) {
3930
+ return true;
3931
+ }
3932
+ if (r1.right >= r2.left && r1.right <= r2.right) {
3933
+ return true;
3934
+ }
3935
+ return false;
3936
+ };
3937
+ const HandleText = (p) => {
3938
+ var _a, _b, _c;
3939
+ const displayValue = (_b = (_a = p.renderValue) === null || _a === void 0 ? void 0 : _a.call(p, p.value)) !== null && _b !== void 0 ? _b : p.value;
3940
+ const renderValueWidth = (_c = p.renderValueWidth) !== null && _c !== void 0 ? _c : p.sliderHandleSize;
3941
+ const renderValueLeft = useMemo(() => {
3942
+ return `calc(${renderValueWidth} * 0.5 * -1 + (${p.sliderHandleSize} * 0.5))`;
3943
+ }, [p.renderValueWidth]);
3944
+ const [flipText, setFlipText] = useState(false);
3945
+ const offset = '2px';
3946
+ useEffect(() => {
3947
+ // this needs to fire on every render due to the other handle also moving.
3948
+ var _a, _b;
3949
+ if (p.index === 1) {
3950
+ // only do this for the max/right-most handle
3951
+ 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());
3952
+ if (r1 && r2) {
3953
+ setFlipText(rectsCollideX(r1, r2));
3954
+ }
3955
+ }
3956
+ });
3957
+ return (React__default.createElement(Text, { ellipsis: true, className: cx('slider-handle', css({
3958
+ width: renderValueWidth,
3959
+ left: renderValueLeft,
3960
+ top: flipText ? undefined : `calc(${p.sliderHandleSize} + ${offset})`,
3961
+ bottom: flipText ? `calc(${p.sliderHandleSize} + ${offset})` : undefined,
3962
+ position: 'absolute',
3963
+ overflow: 'hidden',
3964
+ }), p.className), tag: "div", align: "center" }, displayValue));
3965
+ };
3966
+
3967
+ const TabHeader = (p) => {
3968
+ var _a, _b;
3969
+ const [tabIndex, setTabIndex] = React.useState((_a = p.startingIndex) !== null && _a !== void 0 ? _a : 0);
3970
+ const [tabsChanging, setTabsChanging] = React.useState(false);
3971
+ const theme = useThemeSafely();
3972
+ const variant = (_b = p.variant) !== null && _b !== void 0 ? _b : 'tab';
3973
+ const menuStyles = css({
3974
+ display: 'flex',
3975
+ gap: theme.controls.gap,
3976
+ listStyleType: 'none',
3977
+ margin: 0,
3978
+ padding: 0,
3979
+ flexWrap: variant === 'button' ? 'wrap' : 'nowrap'
3980
+ }, variant === 'button' && {
3981
+ borderBottom: theme.controls.border,
3982
+ paddingBottom: '1rem'
3983
+ });
3984
+ return (React.createElement("div", { className: "tabHeader" },
3985
+ React.createElement("ul", { className: cx(menuStyles, p.containerClassName) }, p.tabs.map((tab, index) => {
3986
+ var _a, _b;
3987
+ const active = index === tabIndex;
3988
+ let tabStyles;
3989
+ let buttonStyles;
3990
+ let buttonVariant;
3991
+ if (variant === 'tab') {
3992
+ tabStyles = css({
3993
+ paddingLeft: '1rem',
3994
+ paddingRight: '1rem',
3995
+ backgroundColor: theme.colors.bg,
3996
+ zIndex: 1,
3997
+ }, active && {
3998
+ border: theme.controls.border,
3999
+ borderRadius: theme.controls.borderRadius,
4000
+ borderBottomLeftRadius: 0,
4001
+ borderBottomRightRadius: 0,
4002
+ borderBottom: 'none',
4003
+ zIndex: 3,
4004
+ });
4005
+ buttonVariant = 'link';
4006
+ buttonStyles = css({
4007
+ maxWidth: (_a = p.maxTabWidth) !== null && _a !== void 0 ? _a : '10rem',
4008
+ overflow: 'hidden'
4009
+ });
4010
+ }
4011
+ else {
4012
+ buttonVariant = active ? 'primary' : undefined;
4013
+ buttonStyles = css({
4014
+ paddingLeft: '1rem',
4015
+ paddingRight: '1rem',
4016
+ maxWidth: (_b = p.maxTabWidth) !== null && _b !== void 0 ? _b : '10rem',
4017
+ overflow: 'hidden',
4018
+ });
4019
+ }
4020
+ let title = tab.title;
4021
+ let buttonContent;
4022
+ if (typeof tab.name === 'string') {
4023
+ title !== null && title !== void 0 ? title : (title = tab.name);
4024
+ buttonContent = React.createElement(Text, { tag: "div", align: "center", ellipsis: true }, tab.name);
4025
+ }
4026
+ else {
4027
+ buttonContent = tab.name;
4028
+ }
4029
+ return (React.createElement("li", { key: index, className: cx(tabStyles, p.tabClassName) },
4030
+ React.createElement(Button, { disabled: tabsChanging, className: buttonStyles, variant: buttonVariant, title: title, readOnly: active, onClick: () => {
4031
+ const onChange = () => {
4032
+ var _a;
4033
+ setTabIndex(index);
4034
+ (_a = p.onTabChanged) === null || _a === void 0 ? void 0 : _a.call(p, index);
4035
+ };
4036
+ if (p.onBeforeTabChanged) {
4037
+ setTabsChanging(true);
4038
+ p.onBeforeTabChanged(index)
4039
+ .then(() => {
4040
+ onChange();
4041
+ })
4042
+ .catch(err => {
4043
+ /* do nothing */
4044
+ })
4045
+ .finally(() => {
4046
+ setTabsChanging(false);
4047
+ });
4048
+ }
4049
+ else {
4050
+ onChange();
4051
+ }
4052
+ } }, buttonContent)));
4053
+ })),
4054
+ variant === 'tab' && (React.createElement("div", { className: cx(css({
4055
+ label: 'TabHeaderDivider',
4056
+ borderBottom: theme.controls.border,
4057
+ marginTop: `calc(${theme.controls.borderWidth} * -1)`,
4058
+ zIndex: 2,
4059
+ position: 'relative'
4060
+ }), p.tabHeaderDividerClassName) }))));
4061
+ };
4062
+
4063
+ const Table = (props) => {
4064
+ const theme = useThemeSafely();
4065
+ const tableStyles = css `
4066
+ label: Table;
4067
+ width: 100%;
4068
+ border-collapse: collapse;
4069
+ ${props.noCellBorder && `
4070
+ .table__td {
4071
+ border-left: none;
4072
+ border-right: none;
4073
+ }
4074
+ `}
4075
+ ${props.altRows && `
4076
+ .table__tr:nth-of-type(even) {
4077
+ background-color: ${theme.colors.lightBg};
4078
+ }
4079
+ `}
4080
+ `;
4081
+ const wrapperStyles = css `
4082
+ label: TableScrollWrapper;
4083
+ width:100%;
4084
+ overflow-y: auto;
4085
+ padding:0 1px; //fixes always showing of the table scroller
4086
+ `;
4087
+ return (React.createElement("div", { className: cx(wrapperStyles, props.wrapperClassName) },
4088
+ React.createElement("table", { className: cx(tableStyles, props.className) },
4089
+ props.caption && React.createElement("caption", { className: css({
4090
+ fontWeight: 'bold',
4091
+ padding: theme.controls.padding
4092
+ }) }, props.caption),
4093
+ props.children)));
4094
+ };
4095
+ const Tr = (props) => {
4096
+ return (React.createElement("tr", { className: cx('table__tr', props.className) }, props.children));
4097
+ };
4098
+ const Th = (props) => {
4099
+ var _a;
4100
+ let style = props.style;
4101
+ if (props.width) {
4102
+ if (style) {
4103
+ style = Object.assign(Object.assign({}, style), { width: props.width, minWidth: props.width });
4104
+ }
4105
+ else {
4106
+ style = { width: props.width, minWidth: props.width };
4107
+ }
4108
+ }
4109
+ const theme = useThemeSafely();
4110
+ const thStyles = css `
4111
+ border-bottom: ${theme.controls.border};
4112
+ padding: ${theme.controls.padding};
4113
+ font-weight: bold;
4114
+ text-align: ${(_a = props.align) !== null && _a !== void 0 ? _a : 'center'};
4115
+ > .button {
4116
+ font-weight: bold;
4117
+ }
4118
+ `;
4119
+ return (React.createElement("th", { className: cx(thStyles, props.className), style: style }, props.children));
4120
+ };
4121
+ const Td = (props) => {
4122
+ var _a;
4123
+ const theme = useThemeSafely();
4124
+ const tdStyles = css `
4125
+ border: ${theme.controls.border};
4126
+ padding: ${theme.controls.padding};
4127
+ vertical-align: middle;
4128
+ text-align: ${(_a = props.align) !== null && _a !== void 0 ? _a : 'center'};
4129
+ `;
4130
+ return (React.createElement("td", { colSpan: props.colSpan, style: props.style, className: cx('table__td', tdStyles, props.className) }, props.children));
4131
+ };
4132
+
4133
+ const TdCurrency = (props) => {
4134
+ let actualValue = props.value || 0;
4135
+ if (props.cents) {
4136
+ actualValue = actualValue / 100;
4137
+ }
4138
+ const displayValue = actualValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
4139
+ return (React.createElement(Td, { align: "right" },
4140
+ "$",
4141
+ displayValue));
4142
+ };
4143
+
4144
+ const TdNumber = (props) => {
4145
+ return React.createElement(Td, { align: "right" }, props.value || props.value === 0 ? props.value.toLocaleString() : '');
4146
+ };
4147
+
4148
+ const ThSort = (props) => {
4149
+ let iconId = '';
4150
+ if (props.direction) {
4151
+ if (props.direction === 'asc') {
4152
+ iconId = 'sortAsc';
4153
+ }
4154
+ else {
4155
+ iconId = 'sortDesc';
4156
+ }
4157
+ }
4158
+ let rightContentSpecialJustify = 'center';
4159
+ switch (props.align) {
4160
+ case 'left':
4161
+ rightContentSpecialJustify = 'flex-start';
4162
+ break;
4163
+ case 'right':
4164
+ rightContentSpecialJustify = 'flex-end';
4165
+ break;
4166
+ }
4167
+ return (React.createElement(Th, { align: props.align, style: props.style, width: props.width },
4168
+ React.createElement("div", { className: props.rightContent && css({
4169
+ display: 'flex',
4170
+ alignItems: 'center',
4171
+ justifyContent: rightContentSpecialJustify,
4172
+ gap: '0.5rem'
4173
+ }) },
4174
+ React.createElement(Button, { onClick: props.onClick, variant: "link" },
4175
+ props.text,
4176
+ iconId && React.createElement(Icon, { className: css({
4177
+ marginLeft: '0.5rem'
4178
+ }), id: iconId })),
4179
+ props.rightContent)));
4180
+ };
4181
+
4182
+ const defaultMaxLength = 200;
4183
+ const defaultRows = 10;
4184
+ const TextArea = React.forwardRef((props, ref) => {
4185
+ var _a, _b, _c;
4186
+ const [localValue, setLocalValue] = React.useState(props.value);
4187
+ const inputRef = (ref !== null && ref !== void 0 ? ref : React.useRef(null));
4188
+ const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, props);
4189
+ const nativeProps = __rest(props, ["onValueChange", "customError", "reportValueOnError", "showErrorDisplay"]);
4190
+ const theme = useThemeSafely();
4191
+ React.useEffect(() => {
4192
+ updateErrorMessage();
4193
+ }, []);
4194
+ useIgnoreMount(() => {
4195
+ var _a;
4196
+ if ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.checkValidity()) {
4197
+ props.onValueChange(localValue);
4198
+ }
4199
+ else {
4200
+ if (props.reportValueOnError) {
4201
+ //TB: temp, add a custom list of validators that will be run for all inputs if a pattern cannot be decided.
4202
+ props.onValueChange(localValue);
4203
+ }
4204
+ else {
4205
+ props.onValueChange(undefined);
4206
+ }
4207
+ }
4208
+ updateErrorMessage();
4209
+ }, [localValue]);
4210
+ useIgnoreMount(() => {
4211
+ if (document.activeElement !== inputRef.current) {
4212
+ setLocalValue(props.value);
4213
+ }
4214
+ updateErrorMessage();
4215
+ }, [props.value]);
4216
+ const styles = css({
4217
+ backgroundColor: theme.colors.bg,
4218
+ maxWidth: '100%',
4219
+ minHeight: theme.controls.height,
4220
+ fontFamily: theme.fonts.family,
4221
+ fontSize: theme.fonts.size,
4222
+ width: '100%',
4223
+ border: theme.controls.border,
4224
+ borderRadius: theme.controls.borderRadius,
4225
+ color: theme.colors.font,
4226
+ paddingTop: '0.75rem',
4227
+ paddingLeft: theme.controls.padding,
4228
+ paddingRight: theme.controls.padding,
4229
+ height: 'auto',
4230
+ transition: theme.controls.transition,
4231
+ ':focus': {
4232
+ outline: 'none',
4233
+ boxShadow: theme.controls.focusOutlineShadow
4234
+ },
4235
+ ':disabled': {
4236
+ backgroundColor: theme.colors.disabled,
4237
+ cursor: 'not-allowed'
4238
+ },
4239
+ ':invalid': {
4240
+ borderColor: theme.colors.required,
4241
+ ':focus': {
4242
+ boxShadow: theme.controls.focusOutlineRequiredShadow
4243
+ }
4244
+ },
4245
+ }, props.readOnly && {
4246
+ backgroundColor: 'transparent',
4247
+ cursor: 'default',
4248
+ border: 'none',
4249
+ ':focus': {
4250
+ outline: 'none',
4251
+ boxShadow: 'none'
4252
+ }
4253
+ });
4254
+ return (React.createElement("span", { className: css({
4255
+ display: 'inline-block',
4256
+ width: '100%'
4257
+ }) },
4258
+ 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 => {
4259
+ var _a;
4260
+ setLocalValue(e.target.value || undefined);
4261
+ (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e);
4262
+ }, onBlur: e => {
4263
+ var _a;
4264
+ if (!props.noTrim) {
4265
+ setLocalValue(currentValue => {
4266
+ return currentValue === null || currentValue === void 0 ? void 0 : currentValue.trim();
4267
+ });
4268
+ }
4269
+ (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
4270
+ } })),
4271
+ ((_c = props.showErrorDisplay) !== null && _c !== void 0 ? _c : true) && React.createElement(InputErrorDisplay, { error: validationError })));
4272
+ });
4273
+
4274
+ const ToggleButton = React.forwardRef((props, ref) => {
4275
+ var _a, _b;
4276
+ const buttonProps = __rest(props, ["checked", "checkedText", "uncheckedText", "checkedChildren", "uncheckedChildren", "checkedVariant", "checkedClassName", "checkedStyle", "checkedIcon", "uncheckedIcon"]);
4277
+ let children;
4278
+ if (props.checked) {
4279
+ children = (_a = props.checkedChildren) !== null && _a !== void 0 ? _a : props.checkedText;
4280
+ }
4281
+ else {
4282
+ children = (_b = props.uncheckedChildren) !== null && _b !== void 0 ? _b : props.uncheckedText;
4283
+ }
4284
+ return (React.createElement(Button, Object.assign({}, buttonProps, { ref: ref, className: cx('toggleButton', props.checked && 'toggleButton--checked', props.className, props.checked && props.checkedClassName), rightIcon: props.checked ? props.checkedIcon : props.uncheckedIcon, variant: props.checked ? props.checkedVariant : props.variant, style: props.checked ? props.checkedStyle : props.style }), children));
4285
+ });
4286
+
4287
+ const ToggleButtonGroup = (props) => {
4288
+ const theme = useThemeSafely();
4289
+ const groupStyles = css `
4290
+ display: flex;
4291
+ box-shadow: ${theme.controls.buttonBoxShadow};
4292
+ border-radius: ${theme.controls.borderRadius};
4293
+ ${props.round && `
4294
+ border-radius: ${theme.controls.roundRadius};
4295
+ `}
4296
+ `;
4297
+ const buttonStyles = css `
4298
+ flex-grow:1;
4299
+ box-shadow: none;
4300
+ &:nth-of-type(1n+2){
4301
+ margin-left: -1px;
4302
+ }
4303
+ border-radius: 0;
4304
+ &:first-of-type{
4305
+ border-top-left-radius: ${theme.controls.borderRadius};
4306
+ border-bottom-left-radius: ${theme.controls.borderRadius};
4307
+ }
4308
+ &:last-child {
4309
+ border-top-right-radius: ${theme.controls.borderRadius};
4310
+ border-bottom-right-radius: ${theme.controls.borderRadius};
4311
+ }
4312
+ ${props.round && `
4313
+ &:first-of-type{
4314
+ border-top-left-radius: ${theme.controls.roundRadius};
4315
+ border-bottom-left-radius: ${theme.controls.roundRadius};
4316
+ padding-left: 1rem;
4317
+ }
4318
+ &:last-child {
4319
+ border-top-right-radius: ${theme.controls.roundRadius};
4320
+ border-bottom-right-radius: ${theme.controls.roundRadius};
4321
+ padding-right: 1rem;
4322
+ }
4323
+ `}
4324
+ `;
4325
+ return (React.createElement("div", { className: cx('toggleButtonGroup', groupStyles, props.className) }, props.options.map(o => {
4326
+ const active = o.id === props.value;
4327
+ 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 `
4328
+ ${buttonStyles}
4329
+ ${active && `
4330
+ background-color: ${theme.colors.font};
4331
+ color: ${theme.colors.bg};
4332
+ cursor: default;
4333
+ &:hover:not(:disabled) {
4334
+ filter: none;
4335
+ }
4336
+ &:focus {
4337
+ outline: none;
4338
+ box-shadow: none;
4339
+ }
4340
+ `}
4341
+ `, active ? o.activeClass : undefined), disabled: props.disabled, enforceMinWidth: props.enforceMinWidth, onClick: () => {
4342
+ if (active) {
4343
+ return;
4344
+ }
4345
+ props.onChange(o.id);
4346
+ } }, o.name);
4347
+ })));
4348
+ };
4349
+
4350
+ const TogglePasswordInput = React.forwardRef((props, ref) => {
4351
+ const [show, setShow] = React.useState(false);
4352
+ return (React.createElement(TextInput, Object.assign({}, props, { ref: ref, type: show ? 'text' : 'password', rightControl: (React.createElement(Button, { small: true, style: {
4353
+ // small button is required here due to the icon pushing outside the boundries of the
4354
+ // parent textbox. increasing the font size here to fill the small button.
4355
+ fontSize: '1rem'
4356
+ }, variant: "icon", onClick: () => {
4357
+ setShow(previous => !previous);
4358
+ } },
4359
+ React.createElement(Icon, { id: show ? 'show' : 'hide' }))) })));
4360
+ });
4361
+
4362
+ const WaitingIndicator = (p) => {
4363
+ var _a, _b;
4364
+ const [show, setShow] = useState(p.show);
4365
+ const hideTimer = useRef(0);
4366
+ const lastShowStatus = useRef(false);
4367
+ const log = useLogger(`WaitingIndicator ${(_a = p.id) !== null && _a !== void 0 ? _a : '?'}`, (_b = p.__debug) !== null && _b !== void 0 ? _b : false);
4368
+ if (p.__debug) {
4369
+ useEffect(() => {
4370
+ log('mounted');
4371
+ return () => {
4372
+ log('unmounted');
4373
+ };
4374
+ }, []);
4375
+ }
4376
+ useEffect(() => {
4377
+ log('show changed', p.show);
4378
+ // we need to store the 'last props' since props.show will be captured locally and the incorrect
4379
+ // value will display in the timeout below.
4380
+ log('storing lastShowStatus', p.show);
4381
+ lastShowStatus.current = p.show;
4382
+ if (p.show) {
4383
+ log('setShow', true);
4384
+ setShow(true);
4385
+ if (p.minShowTimeMs) {
4386
+ log('staring hideTimer', 'timout in ms:', p.minShowTimeMs);
4387
+ hideTimer.current = window.setTimeout(() => {
4388
+ log('hideTimer complete', 'clearing hideTimer');
4389
+ window.clearTimeout(hideTimer.current);
4390
+ hideTimer.current = 0;
4391
+ // this check is necessary since the show status may have updated again to true.
4392
+ // if so, ignore this timeout since we're already past the min time and we're still
4393
+ // showing the component.
4394
+ if (!lastShowStatus.current) {
4395
+ log('setShow', false);
4396
+ setShow(false);
4397
+ }
4398
+ else {
4399
+ log('ignoring hideTimer handler due to hideTimer ticking');
4400
+ }
4401
+ }, p.minShowTimeMs);
4402
+ }
4403
+ }
4404
+ else {
4405
+ // ignore hiding the component since the min timer is running.
4406
+ if (!hideTimer.current) {
4407
+ log('setShow', false);
4408
+ setShow(false);
4409
+ }
4410
+ else {
4411
+ log('ignoring show change due to hideTimer ticking');
4412
+ }
4413
+ }
4414
+ }, [p.show]);
4415
+ return (React__default.createElement(Modal, { id: p.id, __debug: p.__debug, className: "waitingIndicator", show: show, noBackground: true },
4416
+ React__default.createElement("div", { className: css({
4417
+ color: 'white',
4418
+ fontSize: '3rem',
4419
+ padding: '0.7rem'
4420
+ }) },
4421
+ React__default.createElement(Icon, { id: "waiting", spin: true }))));
4422
+ };
4423
+
4424
+ /*
4425
+ pulled from https://github.com/necolas/normalize.css/blob/master/normalize.css.
4426
+ the IE-specific fixes have been removed.
4427
+ */
4428
+ /** Resets certain styles so there is more consistancy between browsers. */
4429
+ const NormalizeCss = (p) => {
4430
+ /* tslint:disable:no-unused-expression */
4431
+ injectGlobal `
4432
+ /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
4433
+
4434
+ /* Document
4435
+ ========================================================================== */
4436
+
4437
+ /**
4438
+ * 1. Correct the line height in all browsers.
4439
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
4440
+ */
4441
+
4442
+ html {
4443
+ line-height: 1.15; /* 1 */
4444
+ -webkit-text-size-adjust: 100%; /* 2 */
4445
+ }
4446
+
4447
+ /* Sections
4448
+ ========================================================================== */
4449
+
4450
+ /**
4451
+ * Remove the margin in all browsers.
4452
+ */
4453
+
4454
+ body {
4455
+ margin: 0;
4456
+ }
4457
+
4458
+ /**
4459
+ * Correct the font size and margin on 'h1' elements within 'section' and
4460
+ * 'article' contexts in Chrome, Firefox, and Safari.
4461
+ */
4462
+
4463
+ h1 {
4464
+ font-size: 2em;
4465
+ margin: 0.67em 0;
4466
+ }
4467
+
4468
+ /* Grouping content
4469
+ ========================================================================== */
4470
+
4471
+ /**
4472
+ * 1. Add the correct box sizing in Firefox.
4473
+ * 2. Show the overflow in Edge and IE.
4474
+ */
4475
+
4476
+ hr {
4477
+ box-sizing: content-box; /* 1 */
4478
+ height: 0; /* 1 */
4479
+ overflow: visible; /* 2 */
4480
+ }
4481
+
4482
+ /**
4483
+ * 1. Correct the inheritance and scaling of font size in all browsers.
4484
+ * 2. Correct the odd 'em' font sizing in all browsers.
4485
+ */
4486
+
4487
+ pre {
4488
+ font-family: monospace, monospace; /* 1 */
4489
+ font-size: 1em; /* 2 */
4490
+ }
4491
+
4492
+ /* Text-level semantics
4493
+ ========================================================================== */
4494
+
4495
+ /**
4496
+ * 1. Remove the bottom border in Chrome 57-
4497
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
4498
+ */
4499
+
4500
+ abbr[title] {
4501
+ border-bottom: none; /* 1 */
4502
+ text-decoration: underline; /* 2 */
4503
+ text-decoration: underline dotted; /* 2 */
4504
+ }
4505
+
4506
+ /**
4507
+ * Add the correct font weight in Chrome, Edge, and Safari.
4508
+ */
4509
+
4510
+ b,
4511
+ strong {
4512
+ font-weight: bolder;
4513
+ }
4514
+
4515
+ /**
4516
+ * 1. Correct the inheritance and scaling of font size in all browsers.
4517
+ * 2. Correct the odd 'em' font sizing in all browsers.
4518
+ */
4519
+
4520
+ code,
4521
+ kbd,
4522
+ samp {
4523
+ font-family: monospace, monospace; /* 1 */
4524
+ font-size: 1em; /* 2 */
4525
+ }
4526
+
4527
+ /**
4528
+ * Add the correct font size in all browsers.
4529
+ */
4530
+
4531
+ small {
4532
+ font-size: 80%;
4533
+ }
4534
+
4535
+ /**
4536
+ * Prevent 'sub' and 'sup' elements from affecting the line height in
4537
+ * all browsers.
4538
+ */
4539
+
4540
+ sub,
4541
+ sup {
4542
+ font-size: 75%;
4543
+ line-height: 0;
4544
+ position: relative;
4545
+ vertical-align: baseline;
4546
+ }
4547
+
4548
+ sub {
4549
+ bottom: -0.25em;
4550
+ }
4551
+
4552
+ sup {
4553
+ top: -0.5em;
4554
+ }
4555
+
4556
+ /* Forms
4557
+ ========================================================================== */
4558
+
4559
+ /**
4560
+ * 1. Change the font styles in all browsers.
4561
+ * 2. Remove the margin in Firefox and Safari.
4562
+ */
4563
+
4564
+ button,
4565
+ input,
4566
+ optgroup,
4567
+ select,
4568
+ textarea {
4569
+ font-family: inherit; /* 1 */
4570
+ font-size: 100%; /* 1 */
4571
+ /* line-height: 1.15; This causes issues with Chrome buttons. */
4572
+ margin: 0; /* 2 */
4573
+ }
4574
+
4575
+ /**
4576
+ * Show the overflow in IE.
4577
+ * 1. Show the overflow in Edge.
4578
+ */
4579
+
4580
+ button,
4581
+ input { /* 1 */
4582
+ overflow: visible;
4583
+ }
4584
+
4585
+ /**
4586
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
4587
+ * 1. Remove the inheritance of text transform in Firefox.
4588
+ */
4589
+
4590
+ button,
4591
+ select { /* 1 */
4592
+ text-transform: none;
4593
+ }
4594
+
4595
+ /**
4596
+ * Correct the inability to style clickable types in iOS and Safari.
4597
+ */
4598
+
4599
+ button,
4600
+ [type="button"],
4601
+ [type="reset"],
4602
+ [type="submit"] {
4603
+ -webkit-appearance: button;
4604
+ }
4605
+
4606
+ /**
4607
+ * Remove the inner border and padding in Firefox.
4608
+ */
4609
+
4610
+ button::-moz-focus-inner,
4611
+ [type="button"]::-moz-focus-inner,
4612
+ [type="reset"]::-moz-focus-inner,
4613
+ [type="submit"]::-moz-focus-inner {
4614
+ border-style: none;
4615
+ padding: 0;
4616
+ }
4617
+
4618
+ /**
4619
+ * Restore the focus styles unset by the previous rule.
4620
+ */
4621
+
4622
+ button:-moz-focusring,
4623
+ [type="button"]:-moz-focusring,
4624
+ [type="reset"]:-moz-focusring,
4625
+ [type="submit"]:-moz-focusring {
4626
+ outline: 1px dotted ButtonText;
4627
+ }
4628
+
4629
+ /**
4630
+ * Correct the padding in Firefox.
4631
+ */
4632
+
4633
+ fieldset {
4634
+ padding: 0.35em 0.75em 0.625em;
4635
+ }
4636
+
4637
+ /**
4638
+ * 1. Correct the text wrapping in Edge and IE.
4639
+ * 2. Correct the color inheritance from 'fieldset' elements in IE.
4640
+ * 3. Remove the padding so developers are not caught out when they zero out
4641
+ * 'fieldset' elements in all browsers.
4642
+ */
4643
+
4644
+ legend {
4645
+ box-sizing: border-box; /* 1 */
4646
+ color: inherit; /* 2 */
4647
+ display: table; /* 1 */
4648
+ max-width: 100%; /* 1 */
4649
+ padding: 0; /* 3 */
4650
+ white-space: normal; /* 1 */
4651
+ }
4652
+
4653
+ /**
4654
+ * Add the correct vertical alignment in Chrome, Firefox, and Opera.
4655
+ */
4656
+
4657
+ progress {
4658
+ vertical-align: baseline;
4659
+ }
4660
+
4661
+
4662
+ /**
4663
+ * Correct the cursor style of increment and decrement buttons in Chrome.
4664
+ */
4665
+
4666
+ [type="number"]::-webkit-inner-spin-button,
4667
+ [type="number"]::-webkit-outer-spin-button {
4668
+ height: auto;
4669
+ }
4670
+
4671
+ /**
4672
+ * 1. Correct the odd appearance in Chrome and Safari.
4673
+ * 2. Correct the outline style in Safari.
4674
+ */
4675
+
4676
+ [type="search"] {
4677
+ -webkit-appearance: textfield; /* 1 */
4678
+ outline-offset: -2px; /* 2 */
4679
+ }
4680
+
4681
+ /**
4682
+ * Remove the inner padding in Chrome and Safari on macOS.
4683
+ */
4684
+
4685
+ [type="search"]::-webkit-search-decoration {
4686
+ -webkit-appearance: none;
4687
+ }
4688
+
4689
+ /**
4690
+ * 1. Correct the inability to style clickable types in iOS and Safari.
4691
+ * 2. Change font properties to 'inherit' in Safari.
4692
+ */
4693
+
4694
+ ::-webkit-file-upload-button {
4695
+ -webkit-appearance: button; /* 1 */
4696
+ font: inherit; /* 2 */
4697
+ }
4698
+
4699
+ /* Interactive
4700
+ ========================================================================== */
4701
+
4702
+ /*
4703
+ * Add the correct display in Edge, IE 10+, and Firefox.
4704
+ */
4705
+
4706
+ details {
4707
+ display: block;
4708
+ }
4709
+
4710
+ /*
4711
+ * Add the correct display in all browsers.
4712
+ */
4713
+
4714
+ summary {
4715
+ display: list-item;
4716
+ }
4717
+ `;
4718
+ return null;
4719
+ };
4720
+
4721
+ /** Displays the value in American dollars. */
4722
+ const getCurrencyDisplay = (value, isCents, denomination = '$') => {
4723
+ let actualValue = value || 0;
4724
+ if (isCents) {
4725
+ actualValue /= 100;
4726
+ }
4727
+ return `${denomination}${actualValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
4728
+ };
4729
+
4730
+ /** Converts an enum to an array of entities with id and name. The enum can be an integer or string enum.*/
4731
+ const enumToEntities = (enumObj) => {
4732
+ const entities = [];
4733
+ for (const key in enumObj) {
4734
+ if (isNaN(parseInt(key, 10))) {
4735
+ entities.push({
4736
+ id: enumObj[key],
4737
+ name: key
4738
+ });
4739
+ }
4740
+ }
4741
+ return entities;
4742
+ };
4743
+
4744
+ const Link = (props) => {
4745
+ const { rightIcon, leftIcon, block, iconBlock, variant, round, small, colorOverride, children, ref } = props, linkProps = __rest(props, ["rightIcon", "leftIcon", "block", "iconBlock", "variant", "round", "small", "colorOverride", "children", "ref"]);
4746
+ const theme = useThemeSafely();
4747
+ const linkStyles = generateLinkStyles(props, theme);
4748
+ const mainClassName = cx('link', linkStyles, props.className);
4749
+ if (variant === 'text') {
4750
+ return React.createElement(Text, { className: mainClassName, tag: "div" }, props.children);
4751
+ }
4752
+ return (React.createElement("a", Object.assign({}, linkProps, { target: props.target, className: mainClassName }),
4753
+ React.createElement(LinkContent, Object.assign({}, props))));
4754
+ };
4755
+
4756
+ const ThemeRenderer = (p) => {
4757
+ const { backgroundColor, color } = p, theme = __rest(p, ["backgroundColor", "color"]);
4758
+ const flatTheme = flatten(theme);
4759
+ const entries = orderBy(Object.entries(flatTheme), x => x[0]);
4760
+ return (React.createElement(Table, { caption: (React.createElement("div", null,
4761
+ React.createElement(Text, { tag: "h1", align: "center" }, "Theme"),
4762
+ 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({
4763
+ backgroundColor: backgroundColor !== null && backgroundColor !== void 0 ? backgroundColor : '#eee7ca',
4764
+ color: color !== null && color !== void 0 ? color : 'black'
4765
+ }) },
4766
+ React.createElement("thead", null,
4767
+ React.createElement(Tr, null,
4768
+ React.createElement(Th, { align: "left" }, "Property"),
4769
+ React.createElement(Th, { align: "left" }, "Value"))),
4770
+ React.createElement("tbody", null, entries.map(([key, value]) => {
4771
+ let colorBox;
4772
+ if (/color/i.test(key)) {
4773
+ colorBox = (React.createElement("span", { className: css({
4774
+ display: 'block',
4775
+ border: '1px solid black',
4776
+ width: '100%',
4777
+ height: 24,
4778
+ background: value
4779
+ }) }));
4780
+ }
4781
+ return (React.createElement(Tr, { key: key },
4782
+ React.createElement(Td, { align: "left" }, key),
4783
+ React.createElement(Td, { align: "left" },
4784
+ React.createElement("div", { className: css({
4785
+ display: 'flex',
4786
+ alignItems: 'center',
4787
+ gap: '1rem'
4788
+ }) },
4789
+ React.createElement("span", { className: css({ flexShrink: 1 }) }, value),
4790
+ " ",
4791
+ colorBox))));
4792
+ }))));
4793
+ };
4794
+ const flatten = (obj, parent, path = 'theme') => {
4795
+ const flatObj = parent !== null && parent !== void 0 ? parent : {};
4796
+ for (const prop in obj) {
4797
+ const value = obj[prop];
4798
+ const fullPath = `${path}.${prop}`;
4799
+ if (typeof value !== 'object') {
4800
+ flatObj[fullPath] = value;
4801
+ }
4802
+ else {
4803
+ flatten(value, flatObj, fullPath);
4804
+ }
4805
+ }
4806
+ return flatObj;
4807
+ };
4808
+
4809
+ const TabContainer = (p) => {
4810
+ var _a;
4811
+ const [tabIndex, setTabIndex] = React.useState((_a = p.startingIndex) !== null && _a !== void 0 ? _a : 0);
4812
+ const theme = useThemeSafely();
4813
+ return (React.createElement("div", { className: css({
4814
+ label: 'TabContainer'
4815
+ }) },
4816
+ React.createElement(TabHeader, { 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 => {
4817
+ var _a;
4818
+ setTabIndex(newIndex);
4819
+ (_a = p.onTabChanged) === null || _a === void 0 ? void 0 : _a.call(p, newIndex);
4820
+ } }),
4821
+ React.createElement("div", { className: cx(css({
4822
+ label: 'TabContainerContent',
4823
+ padding: '1rem',
4824
+ borderLeft: theme.controls.border,
4825
+ borderRight: theme.controls.border,
4826
+ borderBottom: theme.controls.border,
4827
+ }), p.contentClassName) }, p.tabs[tabIndex].getContent())));
4828
+ };
4829
+
4830
+ export { Accordian, Autocomplete, Backdrop$1 as Backdrop, Backdrop as Backdrop2, BoundMemoryPager, BoundStaticPager, Button, Calendar, Checkbox, ConfirmModal, CopyButton, DateInput, Divider, ErrorModal, FileUploader, Form, FormColumnRow, FormFlexRow, GlobalStyles, Header, Highlight, ICONS, Icon, Image, InfoPanel, InfoTip, Input, ItemPager, Label, Link, List, ListItem, Modal, Nav, NormalizeCss, NumberInput, OmniLink, PagedResult, Pager, Picker, Popover, ProgressBar, SearchBox, Slider, TabContainer, TabHeader, TabLocker, Table, Td, TdCurrency, TdNumber, Text, TextArea, TextInput, Th, ThSort, ThemeProvider, ThemeRenderer, ToggleButton, ToggleButtonGroup, TogglePasswordInput, Tr, WaitingIndicator, calcDynamicThemeProps, defaultTheme, enumToEntities, getCurrencyDisplay, getFileSizeDisplay, modalScrollFixClassName, useAccordianState, useBooleanChanged, useIgnoreMount, useMediaQuery, useScrollbarSize, useThemeSafely, useWaiting };
4831
+ //# sourceMappingURL=index.esm.js.map