@mackin.com/styleguide 9.7.0 → 9.8.0

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