@mackin.com/styleguide 6.2.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.d.ts +11 -26
  2. package/index.js +325 -330
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -262,29 +262,11 @@ declare const ErrorModal: (props: {
262
262
 
263
263
  /** @jsx jsx */
264
264
 
265
- interface FilePickerProps {
266
- maxBytes?: number;
267
- multiple?: boolean;
268
- accept?: string;
269
- /** Defaults to 'Choose File(s)'. */
270
- buttonText?: string;
271
- /** If the handler returns true, the native file input will be cleared. */
272
- onChange?: (files: FileList | undefined) => boolean | undefined;
273
- /**
274
- * Used the facilitate communication between the FilePicker and FileUploader.
275
- * Temp solution until we merge both together. Don't use! */
276
- __passClearFilesHandle?: (func: () => void) => void;
277
- }
278
- /** Basic implementation here for later abstraction. */
279
- declare const FilePicker: (props: FilePickerProps) => jsx.JSX.Element;
280
-
281
- /** @jsx jsx */
282
-
283
265
  interface IProps extends React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement> {
284
266
  inline?: boolean;
285
267
  }
286
268
  /** Use this instead of <form> directly. If we need to fight Chrome's autofill, we can do so at a global level. */
287
- declare const Form: (props: IProps) => jsx.JSX.Element;
269
+ declare const Form: React.ForwardRefExoticComponent<Pick<IProps, "inline" | "key" | "css" | "acceptCharset" | "action" | "autoComplete" | "encType" | "method" | "name" | "noValidate" | "target" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "className" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "placeholder" | "slot" | "spellCheck" | "style" | "tabIndex" | "title" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "children" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture"> & React.RefAttributes<unknown>>;
288
270
  declare const FormFlexRow: (props: {
289
271
  children: React.ReactNode;
290
272
  className?: string;
@@ -374,9 +356,10 @@ declare const InfoTip: (props: InfoTipProps) => jsx.JSX.Element;
374
356
  /** @jsx jsx */
375
357
 
376
358
  declare type InputValue = string | number | undefined;
377
- declare type InputType = 'text' | 'number' | 'textarea' | 'date' | 'password';
359
+ declare type InputType = 'text' | 'number' | 'textarea' | 'date' | 'password' | 'url' | 'email';
378
360
  interface InputProps {
379
361
  type: InputType;
362
+ name?: string;
380
363
  style?: React.CSSProperties;
381
364
  value?: InputValue;
382
365
  rounded?: boolean;
@@ -392,7 +375,7 @@ interface InputProps {
392
375
  /** Defaults to 'off'. */
393
376
  autoComplete?: string;
394
377
  /** Called with debounce when the input changes. */
395
- onChange?: (value: InputValue) => void;
378
+ onChange?: (value: InputValue, name?: string) => void;
396
379
  onFocus?: (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
397
380
  onBlur?: (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
398
381
  /** Defaults to 100. Ignored for type=number and type=date. */
@@ -407,8 +390,9 @@ interface InputProps {
407
390
  max?: number;
408
391
  /** Only used for type=textarea. Defaults to 10. */
409
392
  rows?: number;
393
+ pattern?: string;
410
394
  }
411
- declare const Input: (props: InputProps) => jsx.JSX.Element;
395
+ declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<any>>;
412
396
 
413
397
  /** @jsx jsx */
414
398
 
@@ -816,7 +800,7 @@ declare const TabLocker: (props: {
816
800
 
817
801
  /** @jsx jsx */
818
802
 
819
- interface FileUploaderProps extends FilePickerProps {
803
+ interface FileUploaderProps {
820
804
  onUpload: (files: FileList) => Promise<void>;
821
805
  /** Defaults to 'Upload'. */
822
806
  buttonText?: string;
@@ -828,8 +812,9 @@ interface FileUploaderProps extends FilePickerProps {
828
812
  buttonWidth?: string;
829
813
  /** Defaults to 'primary'. */
830
814
  buttonVariant?: ButtonVariant;
831
- /** Will clear all files on upload success. */
832
- clearOnUpload?: boolean;
815
+ maxBytes?: number;
816
+ multiple?: boolean;
817
+ accept?: string;
833
818
  }
834
819
  declare const FileUploader: (p: FileUploaderProps) => jsx.JSX.Element;
835
820
 
@@ -974,4 +959,4 @@ declare const Backdrop: (p: {
974
959
 
975
960
  declare const useMediaQuery: (query: string) => boolean;
976
961
 
977
- export { Alignment, Backdrop$1 as Backdrop, Backdrop as Backdrop2, BoundMemoryPager, BoundStaticPager, Button, ButtonProps, Calendar, CalendarProps, Checkbox, CheckboxProps, ConfirmModal, ConfirmModalProps, CopyButton, DatePicker, DatePickerProps, Divider, ErrorModal, FilePicker, FilePickerProps, FileUploader, Form, FormColumnRow, FormFlexRow, GlobalStyles, Header, Highlight, ICONS, Icon, Image, InfoPanel, InfoTip, InfoTipProps, Input, InputProps, ItemPager, Label, LabelProps, List, ListItem, MackinTheme, Modal, Nav, OmniLink, OmniLinkProps, PagedResult, Pager, PagerProps, Picker, PickerProps, Popover, ProgressBar, ProgressBarProps, SearchBox, SearchBoxProps, Slider, TabHeader, TabHeaderProps, TabLocker, Table, Td, TdCurrency, TdNumber, Text, TextProps, Th, ThSort, ToggleButton, ToggleButtonGroup, ToggleButtonGroupProps, ToggleButtonProps, TogglePasswordInput, Tr, WaitingIndicator, calcDynamicThemeProps, defaultTheme, getCurrencyDisplay, mergeClassNames, useMediaQuery, useThemeSafely };
962
+ export { Alignment, Backdrop$1 as Backdrop, Backdrop as Backdrop2, BoundMemoryPager, BoundStaticPager, Button, ButtonProps, Calendar, CalendarProps, Checkbox, CheckboxProps, ConfirmModal, ConfirmModalProps, CopyButton, DatePicker, DatePickerProps, Divider, ErrorModal, FileUploader, Form, FormColumnRow, FormFlexRow, GlobalStyles, Header, Highlight, ICONS, Icon, Image, InfoPanel, InfoTip, InfoTipProps, Input, InputProps, ItemPager, Label, LabelProps, List, ListItem, MackinTheme, Modal, Nav, OmniLink, OmniLinkProps, PagedResult, Pager, PagerProps, Picker, PickerProps, Popover, ProgressBar, ProgressBarProps, SearchBox, SearchBoxProps, Slider, TabHeader, TabHeaderProps, TabLocker, Table, Td, TdCurrency, TdNumber, Text, TextProps, Th, ThSort, ToggleButton, ToggleButtonGroup, ToggleButtonGroupProps, ToggleButtonProps, TogglePasswordInput, Tr, WaitingIndicator, calcDynamicThemeProps, defaultTheme, getCurrencyDisplay, mergeClassNames, useMediaQuery, useThemeSafely };
package/index.js CHANGED
@@ -1607,203 +1607,12 @@ const ErrorModal = (props) => {
1607
1607
  react.jsx(Text, { align: "center" }, message))));
1608
1608
  };
1609
1609
 
1610
- /** @jsx jsx */
1611
- const InfoPanel = (props) => {
1612
- const theme = useThemeSafely();
1613
- const styles = react.css `
1614
- border:${theme.colors.border};
1615
- padding:1rem;
1616
- color: rgba(0, 0, 0, 0.7);
1617
- margin: 0 !important;
1618
- ${props.variant === 'info' && `
1619
- background-color:${theme.colors.info};
1620
- color:${theme.colors.infoFont};
1621
- `}
1622
- ${props.variant === 'warning' && `
1623
- background-color:${theme.colors.warning};
1624
- color:${theme.colors.warningFont};
1625
- `}
1626
- ${props.variant === 'error' && `
1627
- background-color:${theme.colors.omg};
1628
- color:${theme.colors.omgFont};
1629
- `}
1630
- ${props.variant === 'negative' && `
1631
- background-color:${theme.colors.negative};
1632
- color:${theme.colors.negativeFont};
1633
- `}
1634
- ${props.variant === 'positive' && `
1635
- background-color:${theme.colors.positive};
1636
- color:${theme.colors.positiveFont};
1637
- `}
1638
- `;
1639
- return (react.jsx(Text, { style: props.style, align: "center", css: styles, className: mergeClassNames('infoPanel', props.className) }, props.children));
1640
- };
1641
-
1642
- class FileListPlus {
1643
- constructor(_raw, _args = {}) {
1644
- this._raw = _raw;
1645
- this._args = _args;
1646
- this._files = Array.from(this._raw).map(f => {
1647
- return { name: f.name, size: f.size, type: f.type };
1648
- });
1649
- if (this._args.accept) {
1650
- const acceptTypes = this._args.accept.split(',');
1651
- this._invalidFiles = this._files.filter(f => {
1652
- if (acceptTypes.includes(f.type)) {
1653
- return false;
1654
- }
1655
- if (acceptTypes.some(t => f.name.endsWith(t))) {
1656
- return false;
1657
- }
1658
- return true;
1659
- });
1660
- }
1661
- else {
1662
- this._invalidFiles = [];
1663
- }
1664
- }
1665
- get raw() {
1666
- return this._raw;
1667
- }
1668
- get length() {
1669
- return this._files.length;
1670
- }
1671
- get files() {
1672
- return this._files;
1673
- }
1674
- get invalidFiles() {
1675
- return this._invalidFiles;
1676
- }
1677
- get totalBytes() {
1678
- return lodash.sumBy(this.files, f => f.size);
1679
- }
1680
- get overMaxBytes() {
1681
- var _a;
1682
- return this.totalBytes >= ((_a = this._args.maxBytes) !== null && _a !== void 0 ? _a : Infinity);
1683
- }
1684
- get overFileLimit() {
1685
- return this.length > (this._args.multiple ? Infinity : 1);
1686
- }
1687
- get hasErrors() {
1688
- return this.overMaxBytes || this.overFileLimit || !!this.invalidFiles.length;
1689
- }
1690
- }
1691
-
1692
- /** @jsx jsx */
1693
- /** Basic implementation here for later abstraction. */
1694
- const FilePicker = (props) => {
1695
- var _a, _b, _c, _d;
1696
- const input = React__namespace.useRef(null);
1697
- const [fileList, setFileList] = React__namespace.useState(undefined);
1698
- const totalFileSize = (_a = fileList === null || fileList === void 0 ? void 0 : fileList.totalBytes) !== null && _a !== void 0 ? _a : 0;
1699
- const theme = useThemeSafely();
1700
- let filesDisplay = '';
1701
- if (!(fileList === null || fileList === void 0 ? void 0 : fileList.length)) {
1702
- filesDisplay = `No file${props.multiple ? 's' : ''} chosen.`;
1703
- }
1704
- else {
1705
- filesDisplay = `${fileList.length.toLocaleString()} file${fileList.length > 1 ? 's' : ''} selected (${getSizeString(totalFileSize)}): ${fileList.files.map(f => f.name).join(', ')}`;
1706
- }
1707
- const width = '10rem';
1708
- const nativeInputStyles = react.css `
1709
- position: absolute;
1710
- top: 0;
1711
- left: 0;
1712
- bottom: 0;
1713
- right: 0;
1714
- border: 1px solid black;
1715
- cursor: pointer;
1716
- width:${width};
1717
- opacity: 0;
1718
- `;
1719
- const buttonText = (_b = props.buttonText) !== null && _b !== void 0 ? _b : `Choose File${props.multiple ? 's' : ''}`;
1720
- const clearFiles = () => {
1721
- if (input.current) {
1722
- input.current.value = '';
1723
- }
1724
- setFileList(undefined);
1725
- };
1726
- (_c = props.__passClearFilesHandle) === null || _c === void 0 ? void 0 : _c.call(props, clearFiles);
1727
- return (react.jsx("span", { css: { display: 'inline-block' }, className: "fileUploader" },
1728
- react.jsx("div", { css: {
1729
- position: 'relative',
1730
- width
1731
- } },
1732
- react.jsx(Button, { block: true, variant: "secondary", type: "button" }, buttonText),
1733
- react.jsx("input", { ref: input, css: nativeInputStyles, type: "file", multiple: props.multiple, accept: props.accept, onChange: e => {
1734
- var _a;
1735
- try {
1736
- if (!e.target.files) {
1737
- setFileList(undefined);
1738
- (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, undefined);
1739
- return;
1740
- }
1741
- const fileListPlus = new FileListPlus(e.target.files, {
1742
- accept: props.accept,
1743
- multiple: props.multiple,
1744
- maxBytes: props.maxBytes
1745
- });
1746
- if (props.onChange) {
1747
- if (fileListPlus.hasErrors) {
1748
- props.onChange(undefined);
1749
- setFileList(fileListPlus);
1750
- }
1751
- else {
1752
- const removeFiles = props.onChange(fileListPlus.raw);
1753
- if (removeFiles) {
1754
- setFileList(undefined);
1755
- }
1756
- else {
1757
- setFileList(fileListPlus);
1758
- }
1759
- }
1760
- }
1761
- else {
1762
- setFileList(fileListPlus);
1763
- }
1764
- }
1765
- catch (err) {
1766
- // tslint:disable-next-line
1767
- console.error(err);
1768
- setFileList(undefined);
1769
- }
1770
- } })),
1771
- react.jsx(Text, null,
1772
- filesDisplay,
1773
- !!(fileList === null || fileList === void 0 ? void 0 : fileList.length) && (react.jsx(Button, { onClick: () => {
1774
- var _a;
1775
- clearFiles();
1776
- (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, undefined);
1777
- }, css: { marginLeft: '1rem', color: theme.colors.negative }, rightIcon: react.jsx(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear"))),
1778
- !!(fileList === null || fileList === void 0 ? void 0 : fileList.invalidFiles.length) && react.jsx(InfoPanel, { variant: "error" },
1779
- "Invalid files: ",
1780
- fileList.invalidFiles.map(f => f.name).join(', '),
1781
- "."),
1782
- (fileList === null || fileList === void 0 ? void 0 : fileList.overMaxBytes) && react.jsx(InfoPanel, { variant: "error" },
1783
- "Max file size exceeded (",
1784
- getSizeString((_d = props.maxBytes) !== null && _d !== void 0 ? _d : 0),
1785
- ").")));
1786
- };
1787
- const bytesInMb = 1048576;
1788
- const bytesInKb = 1024;
1789
- const getSizeString = (size) => {
1790
- if (size < bytesInKb) {
1791
- return size.toLocaleString() + ' B';
1792
- }
1793
- else if (size < bytesInMb) {
1794
- return (size / bytesInKb).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' KB';
1795
- }
1796
- else {
1797
- return (size / bytesInMb).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' MB';
1798
- }
1799
- };
1800
-
1801
1610
  /** @jsx jsx */
1802
1611
  /** Use this instead of <form> directly. If we need to fight Chrome's autofill, we can do so at a global level. */
1803
- const Form = (props) => {
1612
+ const Form = React__namespace.forwardRef((props, ref) => {
1804
1613
  const { inline, children, onSubmit } = props, rest = __rest(props, ["inline", "children", "onSubmit"]);
1805
1614
  const theme = useThemeSafely();
1806
- return (react.jsx("form", Object.assign({ className: "form", css: {
1615
+ return (react.jsx("form", Object.assign({ ref: ref, className: "form", css: {
1807
1616
  display: 'flex',
1808
1617
  flexDirection: props.inline ? 'row' : 'column',
1809
1618
  alignItems: props.inline ? 'flex-end' : 'normal',
@@ -1815,7 +1624,7 @@ const Form = (props) => {
1815
1624
  onSubmit(e);
1816
1625
  }
1817
1626
  } }), children));
1818
- };
1627
+ });
1819
1628
  const FormFlexRow = (props) => {
1820
1629
  var _a;
1821
1630
  const theme = useThemeSafely();
@@ -1952,6 +1761,38 @@ const Image = (props) => {
1952
1761
  }, alt: props.alt, style: props.style, className: mergeClassNames('image', props.className), src: props.src }));
1953
1762
  };
1954
1763
 
1764
+ /** @jsx jsx */
1765
+ const InfoPanel = (props) => {
1766
+ const theme = useThemeSafely();
1767
+ const styles = react.css `
1768
+ border:${theme.colors.border};
1769
+ padding:1rem;
1770
+ color: rgba(0, 0, 0, 0.7);
1771
+ margin: 0 !important;
1772
+ ${props.variant === 'info' && `
1773
+ background-color:${theme.colors.info};
1774
+ color:${theme.colors.infoFont};
1775
+ `}
1776
+ ${props.variant === 'warning' && `
1777
+ background-color:${theme.colors.warning};
1778
+ color:${theme.colors.warningFont};
1779
+ `}
1780
+ ${props.variant === 'error' && `
1781
+ background-color:${theme.colors.omg};
1782
+ color:${theme.colors.omgFont};
1783
+ `}
1784
+ ${props.variant === 'negative' && `
1785
+ background-color:${theme.colors.negative};
1786
+ color:${theme.colors.negativeFont};
1787
+ `}
1788
+ ${props.variant === 'positive' && `
1789
+ background-color:${theme.colors.positive};
1790
+ color:${theme.colors.positiveFont};
1791
+ `}
1792
+ `;
1793
+ return (react.jsx(Text, { style: props.style, align: "center", css: styles, className: mergeClassNames('infoPanel', props.className) }, props.children));
1794
+ };
1795
+
1955
1796
  /** @jsx jsx */
1956
1797
  const Popover = (p) => {
1957
1798
  var _a, _b;
@@ -2067,19 +1908,19 @@ const formatLocalValue = (value, type) => {
2067
1908
  }
2068
1909
  return newValue;
2069
1910
  };
2070
- const Input = (props) => {
1911
+ const Input = React__namespace.forwardRef((props, ref) => {
2071
1912
  var _a, _b, _c;
2072
1913
  const [localValue, setLocalValue] = React__namespace.useState(formatLocalValue(props.value, props.type));
2073
1914
  const debounceMs = (_a = props.debounceMs) !== null && _a !== void 0 ? _a : DEFAULT_DEBOUNCE_MS;
2074
1915
  const autoComplete = (_b = props.autoComplete) !== null && _b !== void 0 ? _b : 'off';
2075
1916
  const vars = React__namespace.useRef({
2076
- wrappedOnChange: (props.onChange && debounceMs) ? lodash.debounce((value) => {
1917
+ wrappedOnChange: (props.onChange && debounceMs) ? lodash.debounce((value, name) => {
2077
1918
  var _a;
2078
- (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, value);
1919
+ (_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, value, name);
2079
1920
  }, debounceMs) : undefined,
2080
1921
  focused: false
2081
1922
  });
2082
- const onChange = (_c = vars.current.wrappedOnChange) !== null && _c !== void 0 ? _c : props.onChange;
1923
+ const outerOnChange = (_c = vars.current.wrappedOnChange) !== null && _c !== void 0 ? _c : props.onChange;
2083
1924
  const trySyncLocalValue = () => {
2084
1925
  if (vars.current.focused) {
2085
1926
  return;
@@ -2092,53 +1933,49 @@ const Input = (props) => {
2092
1933
  };
2093
1934
  React__namespace.useEffect(() => {
2094
1935
  trySyncLocalValue();
2095
- });
1936
+ }, [props.value]);
2096
1937
  const theme = useThemeSafely();
2097
- const inputStyles = react.css `
2098
- font-family: ${theme.fonts.family};
2099
- font-size: ${theme.fonts.size};
2100
- width: 100%;
2101
- border: ${theme.controls.border};
2102
- color: ${theme.colors.font};
2103
- padding-left: ${theme.controls.padding};
2104
- padding-right: ${theme.controls.padding};
2105
- height: ${theme.controls.height};
2106
- transition: ${theme.controls.transition};
2107
- &:focus {
2108
- outline: none;
2109
- box-shadow: ${theme.controls.focusOutlineShadow};
2110
- }
2111
- &:disabled {
2112
- background-color: ${theme.colors.disabled};
2113
- cursor: not-allowed;
2114
- }
2115
- ${props.required && !localValue && `
2116
- border-color: ${theme.colors.required};
2117
- &:focus {
2118
- box-shadow: ${theme.controls.focusOutlineRequiredShadow};
2119
- }
2120
- `}
2121
- ${props.round && props.type !== 'textarea' && `
2122
- border-radius: ${theme.controls.roundRadius};
2123
- padding-left: calc(${theme.controls.padding} * 2);
2124
- padding-right: calc(${theme.controls.padding} * 2);
2125
- `}
2126
- ${props.rounded && `
2127
- border-radius: ${theme.controls.roundedRadius};
2128
- `}
2129
- ${props.readOnly && `
2130
- background-color: transparent;
2131
- cursor: default;
2132
- border: none;
2133
- &:focus {
2134
- outline: none;
2135
- box-shadow: none;
2136
- }
2137
- `}
2138
- ${props.rightControl && props.type !== 'textarea' && `
2139
- padding-right: ${theme.controls.height};
2140
- `}
2141
- `;
1938
+ const inputStyles = react.css({
1939
+ fontFamily: theme.fonts.family,
1940
+ fontSize: theme.fonts.size,
1941
+ width: '100%',
1942
+ border: theme.controls.border,
1943
+ color: theme.colors.font,
1944
+ paddingLeft: theme.controls.padding,
1945
+ paddingRight: theme.controls.padding,
1946
+ height: theme.controls.height,
1947
+ transition: theme.controls.transition,
1948
+ ':focus': {
1949
+ outline: 'none',
1950
+ boxShadow: theme.controls.focusOutlineShadow
1951
+ },
1952
+ ':disabled': {
1953
+ backgroundColor: theme.colors.disabled,
1954
+ cursor: 'not-allowed'
1955
+ },
1956
+ ':invalid': {
1957
+ borderColor: theme.colors.required,
1958
+ ':focus': {
1959
+ boxShadow: theme.controls.focusOutlineRequiredShadow
1960
+ }
1961
+ },
1962
+ }, props.round && props.type !== 'textarea' && {
1963
+ borderRadius: theme.controls.roundRadius,
1964
+ paddingLeft: `calc(${theme.controls.padding} * 2)`,
1965
+ paddingRight: `calc(${theme.controls.padding} * 2)`
1966
+ }, props.rounded && {
1967
+ borderRadius: theme.controls.roundedRadius
1968
+ }, props.readOnly && {
1969
+ backgroundColor: 'transparent',
1970
+ cursor: 'default',
1971
+ border: 'none',
1972
+ ':focus': {
1973
+ outline: 'none',
1974
+ boxShadow: 'none'
1975
+ }
1976
+ }, props.rightControl && props.type !== 'textarea' && {
1977
+ paddingRight: theme.controls.height
1978
+ });
2142
1979
  let inputElement;
2143
1980
  const onFocus = (e) => {
2144
1981
  var _a;
@@ -2151,78 +1988,84 @@ const Input = (props) => {
2151
1988
  trySyncLocalValue();
2152
1989
  (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
2153
1990
  };
1991
+ let localOnChange;
2154
1992
  if (props.type === 'number') {
2155
- inputElement = react.jsx("input", { style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly,
2156
- // set fixed default to defeat pasting stupid numbers
2157
- maxLength: 50, min: props.min, max: props.max, required: props.required, disabled: props.disabled, id: props.id, css: inputStyles, className: props.inputClassName, placeholder: props.placeholder, type: "number", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: e => {
2158
- const value = e.target.value;
2159
- if (NUMBER_REGEX.test(value)) {
2160
- let numValue = parseFloat(value);
2161
- if (props.min && numValue < props.min) {
2162
- numValue = props.min;
2163
- }
2164
- if (props.max && numValue > props.max) {
2165
- numValue = props.max;
2166
- }
2167
- setLocalValue(numValue);
2168
- onChange === null || onChange === void 0 ? void 0 : onChange(numValue);
1993
+ localOnChange = e => {
1994
+ const value = e.target.value;
1995
+ if (NUMBER_REGEX.test(value)) {
1996
+ let numValue = parseFloat(value);
1997
+ if (props.min && numValue < props.min) {
1998
+ numValue = props.min;
2169
1999
  }
2170
- else if (!value) {
2171
- setLocalValue(value);
2172
- onChange === null || onChange === void 0 ? void 0 : onChange(undefined);
2000
+ if (props.max && numValue > props.max) {
2001
+ numValue = props.max;
2173
2002
  }
2174
- } });
2003
+ setLocalValue(numValue);
2004
+ outerOnChange === null || outerOnChange === void 0 ? void 0 : outerOnChange(numValue, props.name);
2005
+ }
2006
+ else if (!value) {
2007
+ setLocalValue(value);
2008
+ outerOnChange === null || outerOnChange === void 0 ? void 0 : outerOnChange(undefined, props.name);
2009
+ }
2010
+ };
2175
2011
  }
2176
2012
  else if (props.type === 'date') {
2177
- inputElement = react.jsx("input", { style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: 10, required: props.required, disabled: props.disabled, id: props.id, css: inputStyles, className: props.inputClassName, placeholder: props.placeholder, type: "text", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: e => {
2178
- const value = e.target.value;
2179
- setLocalValue(value);
2180
- if (onChange) {
2181
- const dateParts = DATE_REGEX.exec(value);
2182
- if (!dateParts) {
2183
- onChange(undefined);
2184
- }
2185
- else {
2186
- const year = parseInt(dateParts[3], 10);
2187
- const month = parseInt(dateParts[1], 10);
2188
- const day = parseInt(dateParts[2], 10);
2189
- if (dateFns.isExists(year, month, day)) {
2190
- let ms = new Date(year, month - 1, day).valueOf();
2191
- if (props.min && ms < props.min) {
2192
- ms = props.min;
2193
- }
2194
- if (props.max && ms > props.max) {
2195
- ms = props.max;
2196
- }
2197
- onChange(ms);
2013
+ localOnChange = e => {
2014
+ const value = e.target.value;
2015
+ setLocalValue(value);
2016
+ if (outerOnChange) {
2017
+ const dateParts = DATE_REGEX.exec(value);
2018
+ if (!dateParts) {
2019
+ outerOnChange(undefined, props.name);
2020
+ }
2021
+ else {
2022
+ const year = parseInt(dateParts[3], 10);
2023
+ const month = parseInt(dateParts[1], 10);
2024
+ const day = parseInt(dateParts[2], 10);
2025
+ if (dateFns.isExists(year, month, day)) {
2026
+ let ms = new Date(year, month - 1, day).valueOf();
2027
+ if (props.min && ms < props.min) {
2028
+ ms = props.min;
2198
2029
  }
2199
- else {
2200
- onChange(undefined);
2030
+ if (props.max && ms > props.max) {
2031
+ ms = props.max;
2201
2032
  }
2033
+ outerOnChange(ms, props.name);
2034
+ }
2035
+ else {
2036
+ outerOnChange(undefined, props.name);
2202
2037
  }
2203
2038
  }
2204
- } });
2039
+ }
2040
+ };
2041
+ }
2042
+ else {
2043
+ localOnChange = e => {
2044
+ const value = e.target.value;
2045
+ setLocalValue(value);
2046
+ outerOnChange === null || outerOnChange === void 0 ? void 0 : outerOnChange(value, props.name);
2047
+ };
2048
+ }
2049
+ if (props.type === 'number') {
2050
+ inputElement = react.jsx("input", { ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly,
2051
+ // set fixed default to defeat pasting stupid numbers
2052
+ maxLength: 50, min: props.min, max: props.max, required: props.required, disabled: props.disabled, id: props.id, css: inputStyles, className: props.inputClassName, placeholder: props.placeholder, type: "number", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange });
2053
+ }
2054
+ else if (props.type === 'date') {
2055
+ inputElement = react.jsx("input", { ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: 10, required: props.required, disabled: props.disabled, id: props.id, css: inputStyles, className: props.inputClassName, placeholder: props.placeholder, type: "text", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange });
2205
2056
  }
2206
2057
  else if (props.type === 'textarea') {
2207
- inputElement = react.jsx("textarea", { style: props.style, rows: props.rows || 10, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, css: react.css `
2058
+ inputElement = react.jsx("textarea", { ref: ref, name: props.name, style: props.style, rows: props.rows || 10, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, css: react.css `
2208
2059
  ${inputStyles}
2209
2060
  max-width: 100%;
2210
2061
  min-height: ${theme.controls.height};
2211
2062
  padding-top: 0.75rem;
2212
2063
  height:auto;
2213
- `, className: props.inputClassName, placeholder: props.placeholder, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: e => {
2214
- const value = e.target.value;
2215
- setLocalValue(value);
2216
- onChange === null || onChange === void 0 ? void 0 : onChange(value);
2217
- } });
2064
+ `, className: props.inputClassName, placeholder: props.placeholder, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange });
2218
2065
  }
2219
2066
  else {
2220
- // text & password
2221
- inputElement = react.jsx("input", { style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, css: inputStyles, className: props.inputClassName, placeholder: props.placeholder, type: props.type, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: e => {
2222
- const value = e.target.value;
2223
- setLocalValue(value);
2224
- onChange === null || onChange === void 0 ? void 0 : onChange(value);
2225
- } });
2067
+ // text, password, email, and url
2068
+ inputElement = react.jsx("input", { ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, css: inputStyles, className: props.inputClassName, placeholder: props.placeholder, type: props.type, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange });
2226
2069
  }
2227
2070
  const inputWrapperStyles = react.css `
2228
2071
  width:100%;
@@ -2244,7 +2087,7 @@ const Input = (props) => {
2244
2087
  return (react.jsx("div", { css: inputWrapperStyles, className: mergeClassNames('input', props.className) },
2245
2088
  inputElement,
2246
2089
  props.rightControl && props.type !== 'textarea' && react.jsx("div", { css: rightControlStyles }, props.rightControl)));
2247
- };
2090
+ });
2248
2091
 
2249
2092
  /** @jsx jsx */
2250
2093
  const List = (props) => {
@@ -3060,64 +2903,217 @@ const GlobalStyles = (p) => {
3060
2903
  }) }));
3061
2904
  };
3062
2905
 
2906
+ class FileListPlus {
2907
+ constructor(_raw, _args = {}) {
2908
+ this._raw = _raw;
2909
+ this._args = _args;
2910
+ this._files = Array.from(this._raw).map(f => {
2911
+ return { name: f.name, size: f.size, type: f.type };
2912
+ });
2913
+ if (this._args.accept) {
2914
+ const acceptTypes = this._args.accept.split(',');
2915
+ this._invalidFiles = this._files.filter(f => {
2916
+ if (acceptTypes.includes(f.type)) {
2917
+ return false;
2918
+ }
2919
+ if (acceptTypes.some(t => f.name.endsWith(t))) {
2920
+ return false;
2921
+ }
2922
+ return true;
2923
+ });
2924
+ }
2925
+ else {
2926
+ this._invalidFiles = [];
2927
+ }
2928
+ }
2929
+ get raw() {
2930
+ return this._raw;
2931
+ }
2932
+ get length() {
2933
+ return this._files.length;
2934
+ }
2935
+ get files() {
2936
+ return this._files;
2937
+ }
2938
+ get invalidFiles() {
2939
+ return this._invalidFiles;
2940
+ }
2941
+ get totalBytes() {
2942
+ return lodash.sumBy(this.files, f => f.size);
2943
+ }
2944
+ get overMaxBytes() {
2945
+ var _a;
2946
+ return this.totalBytes >= ((_a = this._args.maxBytes) !== null && _a !== void 0 ? _a : Infinity);
2947
+ }
2948
+ get overFileLimit() {
2949
+ return this.length > (this._args.multiple ? Infinity : 1);
2950
+ }
2951
+ get hasErrors() {
2952
+ return this.overMaxBytes || this.overFileLimit || !!this.invalidFiles.length;
2953
+ }
2954
+ }
2955
+
3063
2956
  /** @jsx jsx */
3064
2957
  const DEFAULT_FAILURE_MESSAGE = 'Upload failed.';
2958
+ const hoverClass = css.css({
2959
+ backgroundColor: 'rgba(0,0,0,0.25) !important'
2960
+ });
3065
2961
  const FileUploader = (p) => {
3066
- var _a;
2962
+ var _a, _b, _c, _d, _e, _f;
3067
2963
  const [message, setMessage] = React.useState(undefined);
3068
2964
  const [canUpload, setCanUpload] = React.useState(false);
3069
2965
  const [uploading, setUploading] = React.useState(false);
3070
2966
  const [files, setFiles] = React.useState(undefined);
3071
2967
  const [fullFailureMessage, setFullFailureMessage] = React.useState(undefined);
3072
- const clearFilePickerHandler = React.useRef(() => {
3073
- // noop
3074
- });
3075
- const { buttonText, successMessage, failureMessage, buttonWidth, buttonVariant, onChange } = p, filePickerProps = __rest(p, ["buttonText", "successMessage", "failureMessage", "buttonWidth", "buttonVariant", "onChange"]);
3076
- const onClear = () => {
3077
- var _a;
3078
- (_a = clearFilePickerHandler.current) === null || _a === void 0 ? void 0 : _a.call(clearFilePickerHandler);
2968
+ const form = React.useRef(null);
2969
+ const input = React.useRef(null);
2970
+ const totalFileSize = (_a = files === null || files === void 0 ? void 0 : files.totalBytes) !== null && _a !== void 0 ? _a : 0;
2971
+ const theme = useThemeSafely();
2972
+ let filesDisplay = '';
2973
+ if (!message) {
2974
+ if (!(files === null || files === void 0 ? void 0 : files.length)) {
2975
+ filesDisplay = `Click or drag and drop files.`;
2976
+ }
2977
+ else {
2978
+ filesDisplay = `${files.length.toLocaleString()} file${files.length > 1 ? 's' : ''} selected (${getSizeString(totalFileSize)}): ${files.files.map(f => f.name).join(', ')}`;
2979
+ }
2980
+ }
2981
+ const setAllFiles = (inputFiles) => {
2982
+ if (input.current && inputFiles === undefined) {
2983
+ input.current.value = '';
2984
+ }
2985
+ setFiles(inputFiles);
2986
+ };
2987
+ const onFilesChange = (rawFiles) => {
2988
+ setAllFiles(rawFiles ? createFileList(rawFiles) : undefined);
2989
+ setCanUpload(!!(rawFiles === null || rawFiles === void 0 ? void 0 : rawFiles.length));
3079
2990
  setMessage(undefined);
2991
+ setFullFailureMessage(undefined);
2992
+ };
2993
+ const createFileList = (rawFiles) => {
2994
+ return new FileListPlus(rawFiles, {
2995
+ accept: p.accept,
2996
+ multiple: p.multiple,
2997
+ maxBytes: p.maxBytes
2998
+ });
3080
2999
  };
3081
- return (react.jsx(Form, { onSubmit: () => {
3000
+ return (react.jsx(Form, { ref: form, css: {
3001
+ border: theme.controls.border,
3002
+ borderStyle: 'dashed',
3003
+ alignItems: 'center',
3004
+ justifyContent: 'center',
3005
+ position: 'relative',
3006
+ padding: '1rem',
3007
+ overflow: 'hidden',
3008
+ backgroundColor: theme.colors.lightBg,
3009
+ }, onDragOver: e => {
3010
+ var _a, _b;
3011
+ e.preventDefault();
3012
+ // we're using onDragOver instead of onDragEnter because *Enter and *Leave apparently can fire multiple times even though you're over the target.
3013
+ // *Over is continuous.
3014
+ if (!((_a = form.current) === null || _a === void 0 ? void 0 : _a.classList.contains(hoverClass))) {
3015
+ (_b = form.current) === null || _b === void 0 ? void 0 : _b.classList.add(hoverClass);
3016
+ }
3017
+ }, onDragLeave: e => {
3018
+ var _a;
3019
+ (_a = form.current) === null || _a === void 0 ? void 0 : _a.classList.remove(hoverClass);
3020
+ }, onDrop: e => {
3021
+ var _a;
3022
+ e.preventDefault();
3023
+ (_a = form.current) === null || _a === void 0 ? void 0 : _a.classList.remove(hoverClass);
3024
+ onFilesChange(e.dataTransfer.files);
3025
+ }, onSubmit: e => {
3082
3026
  if (!files) {
3083
3027
  return;
3084
3028
  }
3085
3029
  setUploading(true);
3086
- p.onUpload(files).then(() => {
3030
+ p.onUpload(files.raw).then(() => {
3087
3031
  setMessage('success');
3088
- if (p.clearOnUpload) {
3089
- clearFilePickerHandler.current();
3090
- }
3032
+ setAllFiles(undefined);
3091
3033
  }).catch(err => {
3034
+ var _a;
3092
3035
  setMessage('failure');
3093
- setFullFailureMessage(`${failureMessage !== null && failureMessage !== void 0 ? failureMessage : DEFAULT_FAILURE_MESSAGE} Error: ${err instanceof Error ? err.message : err}`);
3036
+ setFullFailureMessage(`${(_a = p.failureMessage) !== null && _a !== void 0 ? _a : DEFAULT_FAILURE_MESSAGE} Error: ${err instanceof Error ? err.message : err}`);
3094
3037
  }).finally(() => {
3095
3038
  setUploading(false);
3096
3039
  setCanUpload(false);
3097
3040
  });
3098
3041
  } },
3099
- react.jsx(FilePicker, Object.assign({}, filePickerProps, { onChange: filesFromPicker => {
3100
- setFiles(filesFromPicker);
3101
- setCanUpload(!!(filesFromPicker === null || filesFromPicker === void 0 ? void 0 : filesFromPicker.length));
3102
- setMessage(undefined);
3103
- setFullFailureMessage(undefined);
3104
- if (onChange) {
3105
- return onChange(filesFromPicker);
3042
+ react.jsx("input", { ref: input, css: {
3043
+ position: 'absolute',
3044
+ top: -50,
3045
+ left: 0,
3046
+ bottom: 0,
3047
+ right: 0,
3048
+ width: '100%',
3049
+ cursor: 'pointer',
3050
+ opacity: 0
3051
+ }, type: "file", multiple: p.multiple, accept: p.accept, onChange: e => {
3052
+ try {
3053
+ if (!e.target.files) {
3054
+ onFilesChange(undefined);
3055
+ return;
3056
+ }
3057
+ onFilesChange(e.target.files);
3106
3058
  }
3107
- return false;
3108
- }, __passClearFilesHandle: func => clearFilePickerHandler.current = func })),
3109
- react.jsx("span", null,
3110
- react.jsx(Button, { css: { width: (_a = p.buttonWidth) !== null && _a !== void 0 ? _a : '10rem' }, disabled: !canUpload, waiting: uploading, type: "submit", variant: buttonVariant !== null && buttonVariant !== void 0 ? buttonVariant : 'primary', rightIcon: react.jsx(Icon, { id: "save" }) }, buttonText !== null && buttonText !== void 0 ? buttonText : 'Upload')),
3111
- message === 'success' && (react.jsx(UploadInfoPanel, { variant: "positive", message: successMessage !== null && successMessage !== void 0 ? successMessage : 'Upload successful.', onClear: onClear })),
3112
- message === 'failure' && (react.jsx(UploadInfoPanel, { variant: "error", message: fullFailureMessage, onClear: onClear }))));
3059
+ catch (err) {
3060
+ // tslint:disable-next-line
3061
+ console.error(err);
3062
+ onFilesChange(undefined);
3063
+ }
3064
+ } }),
3065
+ !message && (react.jsx("span", { css: {
3066
+ display: 'flex',
3067
+ flexDirection: 'column',
3068
+ gap: '1rem',
3069
+ alignItems: 'center',
3070
+ zIndex: !!(files === null || files === void 0 ? void 0 : files.length) ? 1 : undefined
3071
+ } },
3072
+ !(files === null || files === void 0 ? void 0 : files.length) && react.jsx(Icon, { style: { fontSize: '2rem' }, id: "upload" }),
3073
+ react.jsx(Text, { align: "center", noPad: true, spacedOut: true },
3074
+ filesDisplay,
3075
+ !!(files === null || files === void 0 ? void 0 : files.length) && (react.jsx(Button, { onClick: e => {
3076
+ e.stopPropagation();
3077
+ onFilesChange(undefined);
3078
+ }, css: { marginLeft: '1rem', color: theme.colors.negative }, rightIcon: react.jsx(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear"))),
3079
+ !!(files === null || files === void 0 ? void 0 : files.invalidFiles.length) && (react.jsx(InfoPanel, { variant: "error" },
3080
+ "Invalid files: ",
3081
+ files.invalidFiles.map(f => f.name).join(', '),
3082
+ ".")),
3083
+ (files === null || files === void 0 ? void 0 : files.overMaxBytes) && (react.jsx(InfoPanel, { variant: "error" },
3084
+ "Max file size exceeded (",
3085
+ getSizeString((_b = p.maxBytes) !== null && _b !== void 0 ? _b : 0),
3086
+ ").")))),
3087
+ canUpload && !(files === null || files === void 0 ? void 0 : files.hasErrors) && (react.jsx(Button, { css: {
3088
+ width: (_c = p.buttonWidth) !== null && _c !== void 0 ? _c : '10rem',
3089
+ zIndex: 1,
3090
+ }, waiting: uploading, type: "submit", variant: (_d = p.buttonVariant) !== null && _d !== void 0 ? _d : 'primary' }, (_e = p.buttonText) !== null && _e !== void 0 ? _e : 'Upload')),
3091
+ message === 'success' && (react.jsx(UploadInfoPanel, { variant: "positive", message: (_f = p.successMessage) !== null && _f !== void 0 ? _f : 'Upload successful.', onClear: () => setMessage(undefined) })),
3092
+ message === 'failure' && (react.jsx(UploadInfoPanel, { variant: "error", message: fullFailureMessage, onClear: () => setMessage(undefined) }))));
3113
3093
  };
3114
3094
  const UploadInfoPanel = (p) => {
3115
- return (react.jsx(InfoPanel, { variant: p.variant },
3095
+ return (react.jsx(InfoPanel, { variant: p.variant, css: { zIndex: 1 } },
3116
3096
  p.message,
3117
- react.jsx(Button, { block: true, className: css.css({
3097
+ react.jsx(Button, { block: true, css: {
3118
3098
  color: 'inherit',
3119
3099
  marginTop: '1rem'
3120
- }), onClick: p.onClear, rightIcon: react.jsx(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear")));
3100
+ }, onClick: e => {
3101
+ e.stopPropagation();
3102
+ p.onClear();
3103
+ }, rightIcon: react.jsx(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear")));
3104
+ };
3105
+ const bytesInMb = 1048576;
3106
+ const bytesInKb = 1024;
3107
+ const getSizeString = (size) => {
3108
+ if (size < bytesInKb) {
3109
+ return size.toLocaleString() + ' B';
3110
+ }
3111
+ else if (size < bytesInMb) {
3112
+ return (size / bytesInKb).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' KB';
3113
+ }
3114
+ else {
3115
+ return (size / bytesInMb).toLocaleString(undefined, { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' MB';
3116
+ }
3121
3117
  };
3122
3118
 
3123
3119
  const CopyButton = (props) => {
@@ -3389,7 +3385,6 @@ exports.CopyButton = CopyButton;
3389
3385
  exports.DatePicker = DatePicker;
3390
3386
  exports.Divider = Divider;
3391
3387
  exports.ErrorModal = ErrorModal;
3392
- exports.FilePicker = FilePicker;
3393
3388
  exports.FileUploader = FileUploader;
3394
3389
  exports.Form = Form;
3395
3390
  exports.FormColumnRow = FormColumnRow;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mackin.com/styleguide",
3
- "version": "6.2.0",
3
+ "version": "7.0.0",
4
4
  "description": "",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",