@mackin.com/styleguide 10.0.3 → 10.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.esm.js +145 -74
  2. package/index.js +144 -73
  3. package/package.json +1 -1
package/index.esm.js CHANGED
@@ -6,7 +6,7 @@ import { faPlus, faTrashAlt, faSave, faCrow, faTimes, faSync, faCheckSquare } fr
6
6
  import { faWifi, faWifiSlash, faBars, faSearch, faQuestionCircle, faNarwhal, faChevronRight, faChevronLeft, faCloudDownload, faCloudUpload, faChevronDoubleLeft, faChevronDoubleRight, faCalendarAlt, faCopy, faPaste, faEyeSlash, faEye } from '@fortawesome/pro-light-svg-icons';
7
7
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8
8
  import { uniqueId, sumBy, orderBy, get } from 'lodash';
9
- import { getDaysInMonth, getDay, isSameMonth, isBefore, isAfter, isSameDay, format, endOfYear, addYears, endOfMonth, addMonths, startOfMonth, startOfYear, isExists } from 'date-fns';
9
+ import { format, getDaysInMonth, getDay, isSameMonth, isBefore, isAfter, isSameDay, endOfYear, addYears, endOfMonth, addMonths, startOfMonth, startOfYear, isExists } from 'date-fns';
10
10
  import { createPortal } from 'react-dom';
11
11
  import { Popover as Popover$1, ArrowContainer } from 'react-tiny-popover';
12
12
  import { Link as Link$1 } from 'react-router-dom';
@@ -731,25 +731,49 @@ const tryClampDecimals = (value, step = 0) => {
731
731
  return parseFloat(value.toFixed(decimals));
732
732
  };
733
733
 
734
+ var StyleGuideLanguage;
735
+ (function (StyleGuideLanguage) {
736
+ StyleGuideLanguage[StyleGuideLanguage["English"] = 0] = "English";
737
+ StyleGuideLanguage[StyleGuideLanguage["Spanish"] = 1] = "Spanish";
738
+ StyleGuideLanguage[StyleGuideLanguage["French"] = 2] = "French";
739
+ })(StyleGuideLanguage || (StyleGuideLanguage = {}));
740
+
741
+ const LocalizationContext = createContext({
742
+ language: StyleGuideLanguage.English,
743
+ getText: text => text
744
+ });
745
+
746
+ const useLocalization = () => {
747
+ const localization = useContext(LocalizationContext);
748
+ if (!localization) {
749
+ return {
750
+ language: StyleGuideLanguage.English,
751
+ getText: (text) => text
752
+ };
753
+ }
754
+ return localization;
755
+ };
756
+
734
757
  /** Common state handling for displaying validation messages with inputs. */
735
758
  const useInputValidationMessage = (ref, props) => {
736
759
  const [validationError, setValidationError] = React__default.useState('');
760
+ const { getText, language } = useLocalization();
737
761
  const updateErrorMessage = (customErrorOverride) => {
738
762
  var _a;
739
763
  const customError = customErrorOverride || props.customError || '';
740
764
  // set it OR clear it. either way, update it.
741
765
  (_a = ref.current) === null || _a === void 0 ? void 0 : _a.setCustomValidity(customError);
742
- setValidationError(customError || getValidationMessage(ref.current, props.patternErrorMessage));
766
+ setValidationError(customError || getValidationMessage(ref.current, getText, props.patternErrorMessage));
743
767
  };
744
768
  useEffect(() => {
745
769
  updateErrorMessage();
746
- }, [props.customError]);
770
+ }, [props.customError, language]);
747
771
  React__default.useEffect(() => {
748
772
  updateErrorMessage();
749
- }, []);
773
+ }, [language]);
750
774
  return [validationError, updateErrorMessage];
751
775
  };
752
- const getValidationMessage = (element, patternErrorMessage) => {
776
+ const getValidationMessage = (element, getText, patternErrorMessage) => {
753
777
  var _a;
754
778
  if (!element) {
755
779
  return '';
@@ -764,25 +788,25 @@ const getValidationMessage = (element, patternErrorMessage) => {
764
788
  if (validity.typeMismatch) {
765
789
  switch (element.type) {
766
790
  case 'url':
767
- return `Invalid URL.`;
791
+ return getText(`Invalid URL.`);
768
792
  case 'email':
769
- return `Invalid email.`;
793
+ return getText(`Invalid email.`);
770
794
  default:
771
795
  return element.validationMessage;
772
796
  }
773
797
  }
774
798
  if (element instanceof HTMLInputElement) {
775
799
  if (validity.rangeOverflow) {
776
- return `Must be less than or equal to ${element.max}.`;
800
+ return `${getText('Must be less than or equal to')} ${element.max}.`;
777
801
  }
778
802
  if (validity.rangeUnderflow) {
779
- return `Must be greater than or equal to ${element.min}.`;
803
+ return `${getText('Must be greater than or equal to')} ${element.min}.`;
780
804
  }
781
805
  if (validity.stepMismatch) {
782
806
  const decimalPlaces = getStepDecimalPlaces(element.step);
783
807
  if (decimalPlaces > 0) {
784
- const place = decimalPlaces === 1 ? 'place' : 'places';
785
- return `Limited to ${getStepDecimalPlaces(element.step)} decimal ${place}.`;
808
+ const place = decimalPlaces === 1 ? getText('place') : getText('places');
809
+ return `${getText('Limited to')} ${getStepDecimalPlaces(element.step)} ${getText('decimal')} ${place}.`;
786
810
  }
787
811
  else {
788
812
  /*
@@ -794,15 +818,15 @@ const getValidationMessage = (element, patternErrorMessage) => {
794
818
  to make things worse, if you enter an invalid number like 59 with step=5 and then clear the input, Chrome will tell
795
819
  you the number 5 is now invalid and should be 4 or 9.
796
820
  */
797
- return `Must be an integer.`;
821
+ return getText(`Must be an integer.`);
798
822
  }
799
823
  }
800
824
  }
801
825
  if (validity.tooShort) {
802
- return `Must be at least ${((_a = element.minLength) !== null && _a !== void 0 ? _a : 0).toLocaleString()} characters in length.`;
826
+ return `${getText('Must be at least')} ${((_a = element.minLength) !== null && _a !== void 0 ? _a : 0).toLocaleString()} ${getText('characters in length.')} `;
803
827
  }
804
828
  if (validity.valueMissing) {
805
- return 'Required.';
829
+ return getText('Required.');
806
830
  }
807
831
  if (validity.patternMismatch && patternErrorMessage) {
808
832
  return patternErrorMessage;
@@ -959,6 +983,10 @@ const Autocomplete = (p) => {
959
983
  }
960
984
  return p.options.slice();
961
985
  }, [p.options]);
986
+ const { getText, language } = useLocalization();
987
+ const resultsText = React.useMemo(() => {
988
+ return `${getText("Showing")} ${displayOptions.length.toLocaleString()} ${getText("of")} ${p.options.length.toLocaleString()} ${getText("results")}.`;
989
+ }, [language]);
962
990
  const getNextTabElement = (fromIndex, direction) => {
963
991
  var _a, _b, _c;
964
992
  if (fromIndex === -1) {
@@ -1091,12 +1119,7 @@ const Autocomplete = (p) => {
1091
1119
  React.createElement(Text, { tag: "div", ellipsis: true, align: "left" }, v))));
1092
1120
  }),
1093
1121
  !p.allowScroll && displayOptions.length < p.options.length && (React.createElement(ListItem, { className: p.listItemClassName },
1094
- React.createElement(Text, { tag: "div", italics: true, align: "center" },
1095
- "Showing ",
1096
- displayOptions.length.toLocaleString(),
1097
- " of ",
1098
- p.options.length.toLocaleString(),
1099
- " results."))))))));
1122
+ React.createElement(Text, { tag: "div", italics: true, align: "center" }, resultsText))))))));
1100
1123
  };
1101
1124
 
1102
1125
  /** Returns a UID. Use this instead of a direct call to a library. */
@@ -1335,6 +1358,7 @@ const Backdrop = (p) => {
1335
1358
  const Calendar = (p) => {
1336
1359
  var _a, _b, _c, _d;
1337
1360
  const theme = useThemeSafely();
1361
+ const { getText, language } = useLocalization();
1338
1362
  const cellSize = (_a = p.cellSize) !== null && _a !== void 0 ? _a : '3rem';
1339
1363
  const showTitle = (_b = p.title) !== null && _b !== void 0 ? _b : true;
1340
1364
  const calendarStyles = css({
@@ -1381,6 +1405,11 @@ const Calendar = (p) => {
1381
1405
  }
1382
1406
  });
1383
1407
  const monthStart = new Date(p.year, p.month - 1, 1);
1408
+ const { monthTitle, weekdays } = React.useMemo(() => {
1409
+ const monthTitle = getText(format(monthStart, 'LLLL'));
1410
+ const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(d => getText(d));
1411
+ return { monthTitle, weekdays };
1412
+ }, [language]);
1384
1413
  const days = getDaysInMonth(monthStart);
1385
1414
  const startDayOfWeek = getDay(monthStart);
1386
1415
  const dayEndIndex = startDayOfWeek + days - 1;
@@ -1415,10 +1444,10 @@ const Calendar = (p) => {
1415
1444
  }
1416
1445
  return (React.createElement("div", { id: p.id, className: "calendar" },
1417
1446
  showTitle && ((_d = p.customTitle) !== null && _d !== void 0 ? _d : React.createElement(Text, { larger: true, align: "center" },
1418
- format(monthStart, 'LLLL'),
1447
+ monthTitle,
1419
1448
  p.yearInTitle && ` ${p.year}`)),
1420
1449
  React.createElement("div", { className: calendarStyles },
1421
- ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => {
1450
+ weekdays.map(day => {
1422
1451
  return (React.createElement(Text, { style: { paddingBottom: '0.25rem' }, key: day, smaller: p.smallHeader, noPad: true, align: "center" }, day));
1423
1452
  }),
1424
1453
  cells)));
@@ -1732,18 +1761,30 @@ const ConfirmModal = (props) => {
1732
1761
  }
1733
1762
  `}
1734
1763
  `;
1764
+ const { getText, language } = useLocalization();
1765
+ const { confirmText, cancelText } = React.useMemo(() => {
1766
+ const confirmText = props.confirmText || getText('OK');
1767
+ const cancelText = props.cancelText || getText('Cancel');
1768
+ return { confirmText, cancelText };
1769
+ }, [language]);
1735
1770
  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 },
1736
1771
  React.createElement("div", { className: css({ padding: '1rem' }) },
1737
1772
  React.createElement(Text, { align: "center" }, props.text),
1738
1773
  React.createElement("div", { className: css({ textAlign: 'center' }) },
1739
- React.createElement(Button, { className: css({ margin: '0 0.5rem' }), enforceMinWidth: true, variant: props.variant === 'omg' ? "omg" : 'primary2', onClick: props.onConfirm }, props.confirmText || 'OK'),
1740
- React.createElement(Button, { className: css({ margin: '0 0.5rem' }), enforceMinWidth: true, onClick: props.onCancel }, props.cancelText || 'Cancel')))));
1774
+ React.createElement(Button, { className: css({ margin: '0 0.5rem' }), enforceMinWidth: true, variant: props.variant === 'omg' ? "omg" : 'primary2', onClick: props.onConfirm }, confirmText),
1775
+ React.createElement(Button, { className: css({ margin: '0 0.5rem' }), enforceMinWidth: true, onClick: props.onCancel }, cancelText)))));
1741
1776
  };
1742
1777
 
1743
1778
  const CopyButton = React.forwardRef((props, ref) => {
1744
1779
  const { selector } = props, buttonProps = __rest(props, ["selector"]);
1745
1780
  const [copied, setCopied] = React.useState(false);
1746
- return (React.createElement(Button, Object.assign({}, buttonProps, { ref: ref, title: copied ? 'Copied!' : (props.title || 'Copy to clipboard'), variant: "icon", onBlur: e => {
1781
+ const { getText, language } = useLocalization();
1782
+ const { copiedText, titleText } = React.useMemo(() => {
1783
+ const copiedText = getText('Copied!');
1784
+ const titleText = props.title || getText('Copy to clipboard');
1785
+ return { copiedText, titleText };
1786
+ }, [language]);
1787
+ return (React.createElement(Button, Object.assign({}, buttonProps, { ref: ref, title: copied ? copiedText : titleText, variant: "icon", onBlur: e => {
1747
1788
  var _a;
1748
1789
  setCopied(false);
1749
1790
  (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
@@ -1779,6 +1820,8 @@ const Divider = (p) => {
1779
1820
  const ErrorModal = (props) => {
1780
1821
  const { message } = props;
1781
1822
  const theme = useThemeSafely();
1823
+ const { getText, language } = useLocalization();
1824
+ const heading = React.useMemo(() => getText("Error"), [language]);
1782
1825
  const modalStyles = css `
1783
1826
  .modalHeader {
1784
1827
  background-color: ${theme.colors.omg};
@@ -1788,7 +1831,7 @@ const ErrorModal = (props) => {
1788
1831
  color: ${theme.colors.omgFont};
1789
1832
  }
1790
1833
  `;
1791
- return (React.createElement(Modal, { id: props.id, __debug: props.__debug, className: cx('errorModal', modalStyles), heading: "Error", closeButton: true, show: props.show, onClick: props.close },
1834
+ return (React.createElement(Modal, { id: props.id, __debug: props.__debug, className: cx('errorModal', modalStyles), heading: heading, closeButton: true, show: props.show, onClick: props.close },
1792
1835
  React.createElement("div", { className: css({ padding: '1rem' }) },
1793
1836
  React.createElement(Text, { align: "center" }, message))));
1794
1837
  };
@@ -1924,7 +1967,7 @@ const hoverClass = css({
1924
1967
  backgroundColor: 'rgba(0,0,0,0.25) !important'
1925
1968
  });
1926
1969
  const FileUploader = (p) => {
1927
- var _a, _b, _c, _d, _e, _f, _g, _h;
1970
+ var _a, _b, _c, _d, _e, _f, _g;
1928
1971
  const [message, setMessage] = useState(undefined);
1929
1972
  const [canUpload, setCanUpload] = useState(false);
1930
1973
  const [uploading, setUploading] = useState(false);
@@ -1934,15 +1977,19 @@ const FileUploader = (p) => {
1934
1977
  const input = useRef(null);
1935
1978
  const totalFileSize = (_a = files === null || files === void 0 ? void 0 : files.totalBytes) !== null && _a !== void 0 ? _a : 0;
1936
1979
  const theme = useThemeSafely();
1937
- let filesDisplay = '';
1938
- if (!message) {
1939
- if (!(files === null || files === void 0 ? void 0 : files.length)) {
1940
- filesDisplay = (_b = p.instructionMessage) !== null && _b !== void 0 ? _b : `Click or drag and drop files.`;
1941
- }
1942
- else {
1943
- filesDisplay = `${files.length.toLocaleString()} file${files.length > 1 ? 's' : ''} selected (${getFileSizeDisplay(totalFileSize)}): ${files.files.map(f => f.name).join(', ')}`;
1980
+ const { getText, language } = useLocalization();
1981
+ const filesDisplay = React__default.useMemo(() => {
1982
+ var _a;
1983
+ if (!message) {
1984
+ if (!(files === null || files === void 0 ? void 0 : files.length)) {
1985
+ return (_a = p.instructionMessage) !== null && _a !== void 0 ? _a : getText(`Click or drag and drop files.`);
1986
+ }
1987
+ else {
1988
+ return `${files.length.toLocaleString()} ${getText("file")}${files.length > 1 ? 's' : ''} ${getText("selected")} (${getFileSizeDisplay(totalFileSize)}): ${files.files.map(f => f.name).join(', ')}`;
1989
+ }
1944
1990
  }
1945
- }
1991
+ return "";
1992
+ }, [language]);
1946
1993
  const setAllFiles = (inputFiles) => {
1947
1994
  if (input.current && inputFiles === undefined) {
1948
1995
  input.current.value = '';
@@ -1962,7 +2009,7 @@ const FileUploader = (p) => {
1962
2009
  maxBytes: p.maxBytes
1963
2010
  });
1964
2011
  };
1965
- const showInfoOnPick = (_c = p.showInfoOnPick) !== null && _c !== void 0 ? _c : true;
2012
+ const showInfoOnPick = (_b = p.showInfoOnPick) !== null && _b !== void 0 ? _b : true;
1966
2013
  let infoMessage;
1967
2014
  if (p.infoMessage && (!files || showInfoOnPick)) {
1968
2015
  if (typeof p.infoMessage === 'string') {
@@ -1972,6 +2019,7 @@ const FileUploader = (p) => {
1972
2019
  infoMessage = p.infoMessage;
1973
2020
  }
1974
2021
  }
2022
+ const clearText = getText("Clear");
1975
2023
  return (React__default.createElement(Form, { ref: form, className: css({
1976
2024
  border: theme.controls.border,
1977
2025
  borderStyle: 'dashed',
@@ -2015,7 +2063,7 @@ const FileUploader = (p) => {
2015
2063
  }).catch(err => {
2016
2064
  var _a;
2017
2065
  setMessage('failure');
2018
- setFullFailureMessage(`${(_a = p.failureMessage) !== null && _a !== void 0 ? _a : DEFAULT_FAILURE_MESSAGE} Error: ${err instanceof Error ? err.message : err}`);
2066
+ setFullFailureMessage(`${(_a = p.failureMessage) !== null && _a !== void 0 ? _a : getText(DEFAULT_FAILURE_MESSAGE)} ${getText("Error")}: ${err instanceof Error ? err.message : err}`);
2019
2067
  }).finally(() => {
2020
2068
  setUploading(false);
2021
2069
  setCanUpload(false);
@@ -2061,22 +2109,24 @@ const FileUploader = (p) => {
2061
2109
  !!(files === null || files === void 0 ? void 0 : files.length) && (React__default.createElement(Button, { onClick: e => {
2062
2110
  e.stopPropagation();
2063
2111
  onFilesChange(undefined);
2064
- }, className: css({ marginLeft: '1rem', color: theme.colors.negative }), rightIcon: React__default.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear"))),
2112
+ }, className: css({ marginLeft: '1rem', color: theme.colors.negative }), rightIcon: React__default.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, clearText))),
2065
2113
  infoMessage,
2066
2114
  !!(files === null || files === void 0 ? void 0 : files.invalidFiles.length) && (React__default.createElement(InfoPanel, { variant: "error", className: css({ width: '100%' }) },
2067
- "Invalid files: ",
2115
+ getText("Invalid files"),
2116
+ ": ",
2068
2117
  files.invalidFiles.map(f => f.name).join(', '),
2069
2118
  ".")),
2070
2119
  (files === null || files === void 0 ? void 0 : files.overMaxBytes) && (React__default.createElement(InfoPanel, { variant: "error", className: css({ width: '100%' }) },
2071
- "Max file size exceeded (",
2072
- getFileSizeDisplay((_d = p.maxBytes) !== null && _d !== void 0 ? _d : 0),
2120
+ getText("Max file size exceeded"),
2121
+ " (",
2122
+ getFileSizeDisplay((_c = p.maxBytes) !== null && _c !== void 0 ? _c : 0),
2073
2123
  ").")))),
2074
2124
  canUpload && !(files === null || files === void 0 ? void 0 : files.hasErrors) && (React__default.createElement(Button, { className: css({
2075
- width: (_e = p.buttonWidth) !== null && _e !== void 0 ? _e : '10rem',
2125
+ width: (_d = p.buttonWidth) !== null && _d !== void 0 ? _d : '10rem',
2076
2126
  zIndex: 1,
2077
- }), waiting: uploading, type: "submit", variant: (_f = p.buttonVariant) !== null && _f !== void 0 ? _f : 'primary' }, (_g = p.buttonText) !== null && _g !== void 0 ? _g : 'Upload')),
2078
- message === 'success' && (React__default.createElement(UploadInfoPanel, { variant: "positive", message: (_h = p.successMessage) !== null && _h !== void 0 ? _h : 'Upload successful.', onClear: () => setMessage(undefined) })),
2079
- message === 'failure' && (React__default.createElement(UploadInfoPanel, { variant: "error", message: fullFailureMessage, onClear: () => setMessage(undefined) }))));
2127
+ }), waiting: uploading, type: "submit", variant: (_e = p.buttonVariant) !== null && _e !== void 0 ? _e : 'primary' }, (_f = p.buttonText) !== null && _f !== void 0 ? _f : getText('Upload'))),
2128
+ message === 'success' && (React__default.createElement(UploadInfoPanel, { variant: "positive", clearText: clearText, message: (_g = p.successMessage) !== null && _g !== void 0 ? _g : getText('Upload successful.'), onClear: () => setMessage(undefined) })),
2129
+ message === 'failure' && (React__default.createElement(UploadInfoPanel, { variant: "error", clearText: clearText, message: fullFailureMessage, onClear: () => setMessage(undefined) }))));
2080
2130
  };
2081
2131
  const UploadInfoPanel = (p) => {
2082
2132
  return (React__default.createElement(InfoPanel, { variant: p.variant, className: css({ zIndex: 1 }) },
@@ -2088,7 +2138,7 @@ const UploadInfoPanel = (p) => {
2088
2138
  }), onClick: e => {
2089
2139
  e.stopPropagation();
2090
2140
  p.onClear();
2091
- }, rightIcon: React__default.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear")));
2141
+ }, rightIcon: React__default.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, p.clearText)));
2092
2142
  };
2093
2143
  // OMG this is dumb. We're sticking with decimals for ease of use.
2094
2144
  // https://stackoverflow.com/questions/40949135/gigabyte-or-gibibyte-1000-or-1024
@@ -2348,7 +2398,8 @@ const DateInput = React.forwardRef((props, ref) => {
2348
2398
  setTextValue(newTextValue);
2349
2399
  }, []);
2350
2400
  const inputRef = (ref !== null && ref !== void 0 ? ref : React.useRef(null));
2351
- const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, { customError: props.customError, patternErrorMessage: invalidDateMessage });
2401
+ const { getText, language } = useLocalization();
2402
+ const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, props);
2352
2403
  const updateDateErrorMessages = React.useCallback(() => {
2353
2404
  let dateError = '';
2354
2405
  if (dateValue === undefined) {
@@ -2360,19 +2411,20 @@ const DateInput = React.forwardRef((props, ref) => {
2360
2411
  else if (isOutOfRange(dateValue, props.min, props.max)) {
2361
2412
  // out of range
2362
2413
  if (props.min !== undefined && props.max !== undefined) {
2363
- dateError = `Must be be between ${parseDateString(props.min)} and ${parseDateString(props.max)}.`;
2414
+ dateError = `${getText("Must be be between")} ${parseDateString(props.min)} ${getText("and")} ${parseDateString(props.max)}.`;
2364
2415
  }
2365
2416
  else if (props.min !== undefined) {
2366
- dateError = `Must be greater than or equal to ${parseDateString(props.min)}.`;
2417
+ dateError = `${getText("Must be greater than or equal to")} ${parseDateString(props.min)}.`;
2367
2418
  }
2368
2419
  else {
2369
- dateError = `Must be less than or equal to ${parseDateString(props.max)}.`;
2420
+ dateError = `${getText("Must be less than or equal to")} ${parseDateString(props.max)}.`;
2370
2421
  }
2371
2422
  }
2372
2423
  updateErrorMessage(dateError);
2373
- }, [dateValue, textValue]);
2424
+ }, [dateValue, textValue, language]);
2374
2425
  const [showCalendar, setShowCalendar] = React.useState(false);
2375
2426
  const [calendarDate, setCalendarDate] = React.useState(getCalendarDate(props.value, props.min, props.max));
2427
+ const monthTitle = React.useMemo(() => getText(format(calendarDate, 'LLLL')), [language]);
2376
2428
  // controls the one-time focus set on show
2377
2429
  const needsFocus = React.useRef(false);
2378
2430
  const toggleCalendar = React.useCallback((show) => {
@@ -2473,7 +2525,7 @@ const DateInput = React.forwardRef((props, ref) => {
2473
2525
  React.createElement(Button, { disabled: !!props.min && isBefore(endOfMonth(addMonths(calendarDate, -1)), props.min), small: true, variant: "icon", onClick: () => setCalendarDate(addMonths(calendarDate, -1)) },
2474
2526
  React.createElement(Icon, { id: "pagerLeft" })),
2475
2527
  React.createElement(Text, { align: "center" },
2476
- format(calendarDate, 'LLLL'),
2528
+ monthTitle,
2477
2529
  " ",
2478
2530
  calendarDate.getFullYear()),
2479
2531
  React.createElement(Button, { disabled: !!props.max && isAfter(startOfMonth(addMonths(calendarDate, 1)), props.max), small: true, variant: "icon", onClick: () => setCalendarDate(addMonths(calendarDate, 1)) },
@@ -3153,27 +3205,32 @@ const Picker = (props) => {
3153
3205
  right: `calc(${theme.controls.padding} + ${round ? roundPxPadding : '0px'})`,
3154
3206
  height: '100%',
3155
3207
  pointerEvents: 'none',
3156
- color: theme.colors.font
3208
+ color: theme.colors.font,
3209
+ width: 14
3157
3210
  }), iconClassName) }))),
3158
3211
  (error || controlAlign) && React.createElement(InputErrorDisplay, { error: error })));
3159
3212
  };
3160
3213
 
3161
3214
  const Pager = (props) => {
3162
- var _a;
3163
3215
  const canGoNext = props.canGoNext && props.totalItems > 0;
3164
3216
  const canGoPrevious = props.canGoPrevious && props.totalItems > 0;
3165
- const dividerText = props.itemDividerText || 'of';
3166
- let itemText = '';
3167
- if (props.totalItems > 0) {
3168
- itemText = `${props.minItem.toLocaleString()}-${props.maxItem.toLocaleString()} ${dividerText} ${props.totalItems.toLocaleString()}`;
3169
- }
3170
- else {
3171
- itemText = props.noResultsText || 'No Results';
3172
- }
3173
- let pageText;
3174
- if (props.pageIndex !== undefined && props.totalPages) {
3175
- pageText = `${(_a = props.pageText) !== null && _a !== void 0 ? _a : 'Page'} ${(props.pageIndex + 1).toLocaleString()} ${dividerText} ${props.totalPages.toLocaleString()}`;
3176
- }
3217
+ const { getText, language } = useLocalization();
3218
+ const { itemText, pageText } = React.useMemo(() => {
3219
+ var _a;
3220
+ const dividerText = props.itemDividerText || getText('of');
3221
+ let itemText = '';
3222
+ if (props.totalItems > 0) {
3223
+ itemText = `${props.minItem.toLocaleString()}-${props.maxItem.toLocaleString()} ${dividerText} ${props.totalItems.toLocaleString()}`;
3224
+ }
3225
+ else {
3226
+ itemText = props.noResultsText || getText('No Results');
3227
+ }
3228
+ let pageText;
3229
+ if (props.pageIndex !== undefined && props.totalPages) {
3230
+ pageText = `${(_a = props.pageText) !== null && _a !== void 0 ? _a : getText('Page')} ${(props.pageIndex + 1).toLocaleString()} ${dividerText} ${props.totalPages.toLocaleString()}`;
3231
+ }
3232
+ return { itemText, pageText };
3233
+ }, [language]);
3177
3234
  const theme = useThemeSafely();
3178
3235
  const pagerStyles = css `
3179
3236
  display: grid;
@@ -3217,23 +3274,37 @@ const Pager = (props) => {
3217
3274
  };
3218
3275
 
3219
3276
  const BoundMemoryPager = (p) => {
3220
- var _a, _b, _c;
3277
+ var _a;
3221
3278
  const { pager, showPageText } = p, rest = __rest(p, ["pager", "showPageText"]);
3222
- return (React.createElement(Pager, Object.assign({}, rest, { pageIndex: showPageText ? pager.page : undefined, totalPages: 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" },
3223
- 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" },
3224
- 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 => {
3279
+ const { getText, language } = useLocalization();
3280
+ const { limitText, sortText } = React.useMemo(() => {
3281
+ var _a, _b;
3282
+ const limitText = (_a = p.limitText) !== null && _a !== void 0 ? _a : getText("Limit");
3283
+ const sortText = (_b = p.sortText) !== null && _b !== void 0 ? _b : getText("Sort");
3284
+ return { limitText, sortText };
3285
+ }, [language]);
3286
+ return (React.createElement(Pager, Object.assign({}, rest, { pageIndex: showPageText ? pager.page : undefined, totalPages: 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: limitText, orientation: "horizontal" },
3287
+ 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: sortText, orientation: "horizontalReverse" },
3288
+ React.createElement(Picker, { value: (_a = pager.sort) !== null && _a !== void 0 ? _a : '', options: pager.sortOptions, onValueChange: v => { var _a; return (_a = p.onSort) === null || _a === void 0 ? void 0 : _a.call(p, v); } }))) : undefined, page: d => {
3225
3289
  p.onPage(d);
3226
3290
  } })));
3227
3291
  };
3228
3292
 
3229
3293
  const BoundStaticPager = (p) => {
3230
- var _a, _b, _c, _d, _e, _f;
3294
+ var _a, _b, _c, _d;
3231
3295
  const { result, showPageText } = p, rest = __rest(p, ["result", "showPageText"]);
3296
+ const { getText, language } = useLocalization();
3297
+ const { limitText, sortText } = React.useMemo(() => {
3298
+ var _a, _b;
3299
+ const limitText = (_a = p.limitText) !== null && _a !== void 0 ? _a : getText("Limit");
3300
+ const sortText = (_b = p.sortText) !== null && _b !== void 0 ? _b : getText("Sort");
3301
+ return { limitText, sortText };
3302
+ }, [language]);
3232
3303
  const showLimit = !!(result.limit && p.limitOptions && p.limitOptions.length > 1 && p.onLimit);
3233
3304
  const showSort = !!(p.sort !== undefined && p.sortOptions && p.sortOptions.length > 1 && p.onSort);
3234
- return (React.createElement(Pager, Object.assign({}, rest, { pageIndex: showPageText ? result.page : undefined, totalPages: 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" },
3235
- 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" },
3236
- React.createElement(Picker, { value: (_e = p.sort) !== null && _e !== void 0 ? _e : '', options: (_f = p.sortOptions) !== null && _f !== void 0 ? _f : [], onValueChange: v => {
3305
+ return (React.createElement(Pager, Object.assign({}, rest, { pageIndex: showPageText ? result.page : undefined, totalPages: 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: limitText, orientation: "horizontal" },
3306
+ React.createElement(Picker, { value: (_a = result.limit) !== null && _a !== void 0 ? _a : 1, options: (_b = p.limitOptions) !== null && _b !== void 0 ? _b : [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: sortText, orientation: "horizontalReverse" },
3307
+ React.createElement(Picker, { value: (_c = p.sort) !== null && _c !== void 0 ? _c : '', options: (_d = p.sortOptions) !== null && _d !== void 0 ? _d : [], onValueChange: v => {
3237
3308
  var _a;
3238
3309
  (_a = p.onSort) === null || _a === void 0 ? void 0 : _a.call(p, v);
3239
3310
  } }))) : undefined, page: d => {
package/index.js CHANGED
@@ -749,25 +749,49 @@ const tryClampDecimals = (value, step = 0) => {
749
749
  return parseFloat(value.toFixed(decimals));
750
750
  };
751
751
 
752
+ var StyleGuideLanguage;
753
+ (function (StyleGuideLanguage) {
754
+ StyleGuideLanguage[StyleGuideLanguage["English"] = 0] = "English";
755
+ StyleGuideLanguage[StyleGuideLanguage["Spanish"] = 1] = "Spanish";
756
+ StyleGuideLanguage[StyleGuideLanguage["French"] = 2] = "French";
757
+ })(StyleGuideLanguage || (StyleGuideLanguage = {}));
758
+
759
+ const LocalizationContext = React.createContext({
760
+ language: StyleGuideLanguage.English,
761
+ getText: text => text
762
+ });
763
+
764
+ const useLocalization = () => {
765
+ const localization = React.useContext(LocalizationContext);
766
+ if (!localization) {
767
+ return {
768
+ language: StyleGuideLanguage.English,
769
+ getText: (text) => text
770
+ };
771
+ }
772
+ return localization;
773
+ };
774
+
752
775
  /** Common state handling for displaying validation messages with inputs. */
753
776
  const useInputValidationMessage = (ref, props) => {
754
777
  const [validationError, setValidationError] = React.useState('');
778
+ const { getText, language } = useLocalization();
755
779
  const updateErrorMessage = (customErrorOverride) => {
756
780
  var _a;
757
781
  const customError = customErrorOverride || props.customError || '';
758
782
  // set it OR clear it. either way, update it.
759
783
  (_a = ref.current) === null || _a === void 0 ? void 0 : _a.setCustomValidity(customError);
760
- setValidationError(customError || getValidationMessage(ref.current, props.patternErrorMessage));
784
+ setValidationError(customError || getValidationMessage(ref.current, getText, props.patternErrorMessage));
761
785
  };
762
786
  React.useEffect(() => {
763
787
  updateErrorMessage();
764
- }, [props.customError]);
788
+ }, [props.customError, language]);
765
789
  React.useEffect(() => {
766
790
  updateErrorMessage();
767
- }, []);
791
+ }, [language]);
768
792
  return [validationError, updateErrorMessage];
769
793
  };
770
- const getValidationMessage = (element, patternErrorMessage) => {
794
+ const getValidationMessage = (element, getText, patternErrorMessage) => {
771
795
  var _a;
772
796
  if (!element) {
773
797
  return '';
@@ -782,25 +806,25 @@ const getValidationMessage = (element, patternErrorMessage) => {
782
806
  if (validity.typeMismatch) {
783
807
  switch (element.type) {
784
808
  case 'url':
785
- return `Invalid URL.`;
809
+ return getText(`Invalid URL.`);
786
810
  case 'email':
787
- return `Invalid email.`;
811
+ return getText(`Invalid email.`);
788
812
  default:
789
813
  return element.validationMessage;
790
814
  }
791
815
  }
792
816
  if (element instanceof HTMLInputElement) {
793
817
  if (validity.rangeOverflow) {
794
- return `Must be less than or equal to ${element.max}.`;
818
+ return `${getText('Must be less than or equal to')} ${element.max}.`;
795
819
  }
796
820
  if (validity.rangeUnderflow) {
797
- return `Must be greater than or equal to ${element.min}.`;
821
+ return `${getText('Must be greater than or equal to')} ${element.min}.`;
798
822
  }
799
823
  if (validity.stepMismatch) {
800
824
  const decimalPlaces = getStepDecimalPlaces(element.step);
801
825
  if (decimalPlaces > 0) {
802
- const place = decimalPlaces === 1 ? 'place' : 'places';
803
- return `Limited to ${getStepDecimalPlaces(element.step)} decimal ${place}.`;
826
+ const place = decimalPlaces === 1 ? getText('place') : getText('places');
827
+ return `${getText('Limited to')} ${getStepDecimalPlaces(element.step)} ${getText('decimal')} ${place}.`;
804
828
  }
805
829
  else {
806
830
  /*
@@ -812,15 +836,15 @@ const getValidationMessage = (element, patternErrorMessage) => {
812
836
  to make things worse, if you enter an invalid number like 59 with step=5 and then clear the input, Chrome will tell
813
837
  you the number 5 is now invalid and should be 4 or 9.
814
838
  */
815
- return `Must be an integer.`;
839
+ return getText(`Must be an integer.`);
816
840
  }
817
841
  }
818
842
  }
819
843
  if (validity.tooShort) {
820
- return `Must be at least ${((_a = element.minLength) !== null && _a !== void 0 ? _a : 0).toLocaleString()} characters in length.`;
844
+ return `${getText('Must be at least')} ${((_a = element.minLength) !== null && _a !== void 0 ? _a : 0).toLocaleString()} ${getText('characters in length.')} `;
821
845
  }
822
846
  if (validity.valueMissing) {
823
- return 'Required.';
847
+ return getText('Required.');
824
848
  }
825
849
  if (validity.patternMismatch && patternErrorMessage) {
826
850
  return patternErrorMessage;
@@ -977,6 +1001,10 @@ const Autocomplete = (p) => {
977
1001
  }
978
1002
  return p.options.slice();
979
1003
  }, [p.options]);
1004
+ const { getText, language } = useLocalization();
1005
+ const resultsText = React__namespace.useMemo(() => {
1006
+ return `${getText("Showing")} ${displayOptions.length.toLocaleString()} ${getText("of")} ${p.options.length.toLocaleString()} ${getText("results")}.`;
1007
+ }, [language]);
980
1008
  const getNextTabElement = (fromIndex, direction) => {
981
1009
  var _a, _b, _c;
982
1010
  if (fromIndex === -1) {
@@ -1109,12 +1137,7 @@ const Autocomplete = (p) => {
1109
1137
  React__namespace.createElement(Text, { tag: "div", ellipsis: true, align: "left" }, v))));
1110
1138
  }),
1111
1139
  !p.allowScroll && displayOptions.length < p.options.length && (React__namespace.createElement(ListItem, { className: p.listItemClassName },
1112
- React__namespace.createElement(Text, { tag: "div", italics: true, align: "center" },
1113
- "Showing ",
1114
- displayOptions.length.toLocaleString(),
1115
- " of ",
1116
- p.options.length.toLocaleString(),
1117
- " results."))))))));
1140
+ React__namespace.createElement(Text, { tag: "div", italics: true, align: "center" }, resultsText))))))));
1118
1141
  };
1119
1142
 
1120
1143
  /** Returns a UID. Use this instead of a direct call to a library. */
@@ -1353,6 +1376,7 @@ const Backdrop = (p) => {
1353
1376
  const Calendar = (p) => {
1354
1377
  var _a, _b, _c, _d;
1355
1378
  const theme = useThemeSafely();
1379
+ const { getText, language } = useLocalization();
1356
1380
  const cellSize = (_a = p.cellSize) !== null && _a !== void 0 ? _a : '3rem';
1357
1381
  const showTitle = (_b = p.title) !== null && _b !== void 0 ? _b : true;
1358
1382
  const calendarStyles = css.css({
@@ -1399,6 +1423,11 @@ const Calendar = (p) => {
1399
1423
  }
1400
1424
  });
1401
1425
  const monthStart = new Date(p.year, p.month - 1, 1);
1426
+ const { monthTitle, weekdays } = React__namespace.useMemo(() => {
1427
+ const monthTitle = getText(dateFns.format(monthStart, 'LLLL'));
1428
+ const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(d => getText(d));
1429
+ return { monthTitle, weekdays };
1430
+ }, [language]);
1402
1431
  const days = dateFns.getDaysInMonth(monthStart);
1403
1432
  const startDayOfWeek = dateFns.getDay(monthStart);
1404
1433
  const dayEndIndex = startDayOfWeek + days - 1;
@@ -1433,10 +1462,10 @@ const Calendar = (p) => {
1433
1462
  }
1434
1463
  return (React__namespace.createElement("div", { id: p.id, className: "calendar" },
1435
1464
  showTitle && ((_d = p.customTitle) !== null && _d !== void 0 ? _d : React__namespace.createElement(Text, { larger: true, align: "center" },
1436
- dateFns.format(monthStart, 'LLLL'),
1465
+ monthTitle,
1437
1466
  p.yearInTitle && ` ${p.year}`)),
1438
1467
  React__namespace.createElement("div", { className: calendarStyles },
1439
- ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => {
1468
+ weekdays.map(day => {
1440
1469
  return (React__namespace.createElement(Text, { style: { paddingBottom: '0.25rem' }, key: day, smaller: p.smallHeader, noPad: true, align: "center" }, day));
1441
1470
  }),
1442
1471
  cells)));
@@ -1750,18 +1779,30 @@ const ConfirmModal = (props) => {
1750
1779
  }
1751
1780
  `}
1752
1781
  `;
1782
+ const { getText, language } = useLocalization();
1783
+ const { confirmText, cancelText } = React__namespace.useMemo(() => {
1784
+ const confirmText = props.confirmText || getText('OK');
1785
+ const cancelText = props.cancelText || getText('Cancel');
1786
+ return { confirmText, cancelText };
1787
+ }, [language]);
1753
1788
  return (React__namespace.createElement(Modal, { id: props.id, __debug: props.__debug, className: css.cx('confirmModal', modalStyle, props.className), heading: props.header, closeButton: true, show: props.show, onClick: props.onCancel },
1754
1789
  React__namespace.createElement("div", { className: css.css({ padding: '1rem' }) },
1755
1790
  React__namespace.createElement(Text, { align: "center" }, props.text),
1756
1791
  React__namespace.createElement("div", { className: css.css({ textAlign: 'center' }) },
1757
- React__namespace.createElement(Button, { className: css.css({ margin: '0 0.5rem' }), enforceMinWidth: true, variant: props.variant === 'omg' ? "omg" : 'primary2', onClick: props.onConfirm }, props.confirmText || 'OK'),
1758
- React__namespace.createElement(Button, { className: css.css({ margin: '0 0.5rem' }), enforceMinWidth: true, onClick: props.onCancel }, props.cancelText || 'Cancel')))));
1792
+ React__namespace.createElement(Button, { className: css.css({ margin: '0 0.5rem' }), enforceMinWidth: true, variant: props.variant === 'omg' ? "omg" : 'primary2', onClick: props.onConfirm }, confirmText),
1793
+ React__namespace.createElement(Button, { className: css.css({ margin: '0 0.5rem' }), enforceMinWidth: true, onClick: props.onCancel }, cancelText)))));
1759
1794
  };
1760
1795
 
1761
1796
  const CopyButton = React__namespace.forwardRef((props, ref) => {
1762
1797
  const { selector } = props, buttonProps = __rest(props, ["selector"]);
1763
1798
  const [copied, setCopied] = React__namespace.useState(false);
1764
- return (React__namespace.createElement(Button, Object.assign({}, buttonProps, { ref: ref, title: copied ? 'Copied!' : (props.title || 'Copy to clipboard'), variant: "icon", onBlur: e => {
1799
+ const { getText, language } = useLocalization();
1800
+ const { copiedText, titleText } = React__namespace.useMemo(() => {
1801
+ const copiedText = getText('Copied!');
1802
+ const titleText = props.title || getText('Copy to clipboard');
1803
+ return { copiedText, titleText };
1804
+ }, [language]);
1805
+ return (React__namespace.createElement(Button, Object.assign({}, buttonProps, { ref: ref, title: copied ? copiedText : titleText, variant: "icon", onBlur: e => {
1765
1806
  var _a;
1766
1807
  setCopied(false);
1767
1808
  (_a = props.onBlur) === null || _a === void 0 ? void 0 : _a.call(props, e);
@@ -1797,6 +1838,8 @@ const Divider = (p) => {
1797
1838
  const ErrorModal = (props) => {
1798
1839
  const { message } = props;
1799
1840
  const theme = useThemeSafely();
1841
+ const { getText, language } = useLocalization();
1842
+ const heading = React__namespace.useMemo(() => getText("Error"), [language]);
1800
1843
  const modalStyles = css.css `
1801
1844
  .modalHeader {
1802
1845
  background-color: ${theme.colors.omg};
@@ -1806,7 +1849,7 @@ const ErrorModal = (props) => {
1806
1849
  color: ${theme.colors.omgFont};
1807
1850
  }
1808
1851
  `;
1809
- return (React__namespace.createElement(Modal, { id: props.id, __debug: props.__debug, className: css.cx('errorModal', modalStyles), heading: "Error", closeButton: true, show: props.show, onClick: props.close },
1852
+ return (React__namespace.createElement(Modal, { id: props.id, __debug: props.__debug, className: css.cx('errorModal', modalStyles), heading: heading, closeButton: true, show: props.show, onClick: props.close },
1810
1853
  React__namespace.createElement("div", { className: css.css({ padding: '1rem' }) },
1811
1854
  React__namespace.createElement(Text, { align: "center" }, message))));
1812
1855
  };
@@ -1942,7 +1985,7 @@ const hoverClass = css.css({
1942
1985
  backgroundColor: 'rgba(0,0,0,0.25) !important'
1943
1986
  });
1944
1987
  const FileUploader = (p) => {
1945
- var _a, _b, _c, _d, _e, _f, _g, _h;
1988
+ var _a, _b, _c, _d, _e, _f, _g;
1946
1989
  const [message, setMessage] = React.useState(undefined);
1947
1990
  const [canUpload, setCanUpload] = React.useState(false);
1948
1991
  const [uploading, setUploading] = React.useState(false);
@@ -1952,15 +1995,19 @@ const FileUploader = (p) => {
1952
1995
  const input = React.useRef(null);
1953
1996
  const totalFileSize = (_a = files === null || files === void 0 ? void 0 : files.totalBytes) !== null && _a !== void 0 ? _a : 0;
1954
1997
  const theme = useThemeSafely();
1955
- let filesDisplay = '';
1956
- if (!message) {
1957
- if (!(files === null || files === void 0 ? void 0 : files.length)) {
1958
- filesDisplay = (_b = p.instructionMessage) !== null && _b !== void 0 ? _b : `Click or drag and drop files.`;
1959
- }
1960
- else {
1961
- filesDisplay = `${files.length.toLocaleString()} file${files.length > 1 ? 's' : ''} selected (${getFileSizeDisplay(totalFileSize)}): ${files.files.map(f => f.name).join(', ')}`;
1998
+ const { getText, language } = useLocalization();
1999
+ const filesDisplay = React.useMemo(() => {
2000
+ var _a;
2001
+ if (!message) {
2002
+ if (!(files === null || files === void 0 ? void 0 : files.length)) {
2003
+ return (_a = p.instructionMessage) !== null && _a !== void 0 ? _a : getText(`Click or drag and drop files.`);
2004
+ }
2005
+ else {
2006
+ return `${files.length.toLocaleString()} ${getText("file")}${files.length > 1 ? 's' : ''} ${getText("selected")} (${getFileSizeDisplay(totalFileSize)}): ${files.files.map(f => f.name).join(', ')}`;
2007
+ }
1962
2008
  }
1963
- }
2009
+ return "";
2010
+ }, [language]);
1964
2011
  const setAllFiles = (inputFiles) => {
1965
2012
  if (input.current && inputFiles === undefined) {
1966
2013
  input.current.value = '';
@@ -1980,7 +2027,7 @@ const FileUploader = (p) => {
1980
2027
  maxBytes: p.maxBytes
1981
2028
  });
1982
2029
  };
1983
- const showInfoOnPick = (_c = p.showInfoOnPick) !== null && _c !== void 0 ? _c : true;
2030
+ const showInfoOnPick = (_b = p.showInfoOnPick) !== null && _b !== void 0 ? _b : true;
1984
2031
  let infoMessage;
1985
2032
  if (p.infoMessage && (!files || showInfoOnPick)) {
1986
2033
  if (typeof p.infoMessage === 'string') {
@@ -1990,6 +2037,7 @@ const FileUploader = (p) => {
1990
2037
  infoMessage = p.infoMessage;
1991
2038
  }
1992
2039
  }
2040
+ const clearText = getText("Clear");
1993
2041
  return (React.createElement(Form, { ref: form, className: css.css({
1994
2042
  border: theme.controls.border,
1995
2043
  borderStyle: 'dashed',
@@ -2033,7 +2081,7 @@ const FileUploader = (p) => {
2033
2081
  }).catch(err => {
2034
2082
  var _a;
2035
2083
  setMessage('failure');
2036
- setFullFailureMessage(`${(_a = p.failureMessage) !== null && _a !== void 0 ? _a : DEFAULT_FAILURE_MESSAGE} Error: ${err instanceof Error ? err.message : err}`);
2084
+ setFullFailureMessage(`${(_a = p.failureMessage) !== null && _a !== void 0 ? _a : getText(DEFAULT_FAILURE_MESSAGE)} ${getText("Error")}: ${err instanceof Error ? err.message : err}`);
2037
2085
  }).finally(() => {
2038
2086
  setUploading(false);
2039
2087
  setCanUpload(false);
@@ -2079,22 +2127,24 @@ const FileUploader = (p) => {
2079
2127
  !!(files === null || files === void 0 ? void 0 : files.length) && (React.createElement(Button, { onClick: e => {
2080
2128
  e.stopPropagation();
2081
2129
  onFilesChange(undefined);
2082
- }, className: css.css({ marginLeft: '1rem', color: theme.colors.negative }), rightIcon: React.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear"))),
2130
+ }, className: css.css({ marginLeft: '1rem', color: theme.colors.negative }), rightIcon: React.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, clearText))),
2083
2131
  infoMessage,
2084
2132
  !!(files === null || files === void 0 ? void 0 : files.invalidFiles.length) && (React.createElement(InfoPanel, { variant: "error", className: css.css({ width: '100%' }) },
2085
- "Invalid files: ",
2133
+ getText("Invalid files"),
2134
+ ": ",
2086
2135
  files.invalidFiles.map(f => f.name).join(', '),
2087
2136
  ".")),
2088
2137
  (files === null || files === void 0 ? void 0 : files.overMaxBytes) && (React.createElement(InfoPanel, { variant: "error", className: css.css({ width: '100%' }) },
2089
- "Max file size exceeded (",
2090
- getFileSizeDisplay((_d = p.maxBytes) !== null && _d !== void 0 ? _d : 0),
2138
+ getText("Max file size exceeded"),
2139
+ " (",
2140
+ getFileSizeDisplay((_c = p.maxBytes) !== null && _c !== void 0 ? _c : 0),
2091
2141
  ").")))),
2092
2142
  canUpload && !(files === null || files === void 0 ? void 0 : files.hasErrors) && (React.createElement(Button, { className: css.css({
2093
- width: (_e = p.buttonWidth) !== null && _e !== void 0 ? _e : '10rem',
2143
+ width: (_d = p.buttonWidth) !== null && _d !== void 0 ? _d : '10rem',
2094
2144
  zIndex: 1,
2095
- }), waiting: uploading, type: "submit", variant: (_f = p.buttonVariant) !== null && _f !== void 0 ? _f : 'primary' }, (_g = p.buttonText) !== null && _g !== void 0 ? _g : 'Upload')),
2096
- message === 'success' && (React.createElement(UploadInfoPanel, { variant: "positive", message: (_h = p.successMessage) !== null && _h !== void 0 ? _h : 'Upload successful.', onClear: () => setMessage(undefined) })),
2097
- message === 'failure' && (React.createElement(UploadInfoPanel, { variant: "error", message: fullFailureMessage, onClear: () => setMessage(undefined) }))));
2145
+ }), waiting: uploading, type: "submit", variant: (_e = p.buttonVariant) !== null && _e !== void 0 ? _e : 'primary' }, (_f = p.buttonText) !== null && _f !== void 0 ? _f : getText('Upload'))),
2146
+ message === 'success' && (React.createElement(UploadInfoPanel, { variant: "positive", clearText: clearText, message: (_g = p.successMessage) !== null && _g !== void 0 ? _g : getText('Upload successful.'), onClear: () => setMessage(undefined) })),
2147
+ message === 'failure' && (React.createElement(UploadInfoPanel, { variant: "error", clearText: clearText, message: fullFailureMessage, onClear: () => setMessage(undefined) }))));
2098
2148
  };
2099
2149
  const UploadInfoPanel = (p) => {
2100
2150
  return (React.createElement(InfoPanel, { variant: p.variant, className: css.css({ zIndex: 1 }) },
@@ -2106,7 +2156,7 @@ const UploadInfoPanel = (p) => {
2106
2156
  }), onClick: e => {
2107
2157
  e.stopPropagation();
2108
2158
  p.onClear();
2109
- }, rightIcon: React.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, "Clear")));
2159
+ }, rightIcon: React.createElement(Icon, { id: "clear" }), variant: "inlineLink" }, p.clearText)));
2110
2160
  };
2111
2161
  // OMG this is dumb. We're sticking with decimals for ease of use.
2112
2162
  // https://stackoverflow.com/questions/40949135/gigabyte-or-gibibyte-1000-or-1024
@@ -2366,7 +2416,8 @@ const DateInput = React__namespace.forwardRef((props, ref) => {
2366
2416
  setTextValue(newTextValue);
2367
2417
  }, []);
2368
2418
  const inputRef = (ref !== null && ref !== void 0 ? ref : React__namespace.useRef(null));
2369
- const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, { customError: props.customError, patternErrorMessage: invalidDateMessage });
2419
+ const { getText, language } = useLocalization();
2420
+ const [validationError, updateErrorMessage] = useInputValidationMessage(inputRef, props);
2370
2421
  const updateDateErrorMessages = React__namespace.useCallback(() => {
2371
2422
  let dateError = '';
2372
2423
  if (dateValue === undefined) {
@@ -2378,19 +2429,20 @@ const DateInput = React__namespace.forwardRef((props, ref) => {
2378
2429
  else if (isOutOfRange(dateValue, props.min, props.max)) {
2379
2430
  // out of range
2380
2431
  if (props.min !== undefined && props.max !== undefined) {
2381
- dateError = `Must be be between ${parseDateString(props.min)} and ${parseDateString(props.max)}.`;
2432
+ dateError = `${getText("Must be be between")} ${parseDateString(props.min)} ${getText("and")} ${parseDateString(props.max)}.`;
2382
2433
  }
2383
2434
  else if (props.min !== undefined) {
2384
- dateError = `Must be greater than or equal to ${parseDateString(props.min)}.`;
2435
+ dateError = `${getText("Must be greater than or equal to")} ${parseDateString(props.min)}.`;
2385
2436
  }
2386
2437
  else {
2387
- dateError = `Must be less than or equal to ${parseDateString(props.max)}.`;
2438
+ dateError = `${getText("Must be less than or equal to")} ${parseDateString(props.max)}.`;
2388
2439
  }
2389
2440
  }
2390
2441
  updateErrorMessage(dateError);
2391
- }, [dateValue, textValue]);
2442
+ }, [dateValue, textValue, language]);
2392
2443
  const [showCalendar, setShowCalendar] = React__namespace.useState(false);
2393
2444
  const [calendarDate, setCalendarDate] = React__namespace.useState(getCalendarDate(props.value, props.min, props.max));
2445
+ const monthTitle = React__namespace.useMemo(() => getText(dateFns.format(calendarDate, 'LLLL')), [language]);
2394
2446
  // controls the one-time focus set on show
2395
2447
  const needsFocus = React__namespace.useRef(false);
2396
2448
  const toggleCalendar = React__namespace.useCallback((show) => {
@@ -2491,7 +2543,7 @@ const DateInput = React__namespace.forwardRef((props, ref) => {
2491
2543
  React__namespace.createElement(Button, { disabled: !!props.min && dateFns.isBefore(dateFns.endOfMonth(dateFns.addMonths(calendarDate, -1)), props.min), small: true, variant: "icon", onClick: () => setCalendarDate(dateFns.addMonths(calendarDate, -1)) },
2492
2544
  React__namespace.createElement(Icon, { id: "pagerLeft" })),
2493
2545
  React__namespace.createElement(Text, { align: "center" },
2494
- dateFns.format(calendarDate, 'LLLL'),
2546
+ monthTitle,
2495
2547
  " ",
2496
2548
  calendarDate.getFullYear()),
2497
2549
  React__namespace.createElement(Button, { disabled: !!props.max && dateFns.isAfter(dateFns.startOfMonth(dateFns.addMonths(calendarDate, 1)), props.max), small: true, variant: "icon", onClick: () => setCalendarDate(dateFns.addMonths(calendarDate, 1)) },
@@ -3171,27 +3223,32 @@ const Picker = (props) => {
3171
3223
  right: `calc(${theme.controls.padding} + ${round ? roundPxPadding : '0px'})`,
3172
3224
  height: '100%',
3173
3225
  pointerEvents: 'none',
3174
- color: theme.colors.font
3226
+ color: theme.colors.font,
3227
+ width: 14
3175
3228
  }), iconClassName) }))),
3176
3229
  (error || controlAlign) && React__namespace.createElement(InputErrorDisplay, { error: error })));
3177
3230
  };
3178
3231
 
3179
3232
  const Pager = (props) => {
3180
- var _a;
3181
3233
  const canGoNext = props.canGoNext && props.totalItems > 0;
3182
3234
  const canGoPrevious = props.canGoPrevious && props.totalItems > 0;
3183
- const dividerText = props.itemDividerText || 'of';
3184
- let itemText = '';
3185
- if (props.totalItems > 0) {
3186
- itemText = `${props.minItem.toLocaleString()}-${props.maxItem.toLocaleString()} ${dividerText} ${props.totalItems.toLocaleString()}`;
3187
- }
3188
- else {
3189
- itemText = props.noResultsText || 'No Results';
3190
- }
3191
- let pageText;
3192
- if (props.pageIndex !== undefined && props.totalPages) {
3193
- pageText = `${(_a = props.pageText) !== null && _a !== void 0 ? _a : 'Page'} ${(props.pageIndex + 1).toLocaleString()} ${dividerText} ${props.totalPages.toLocaleString()}`;
3194
- }
3235
+ const { getText, language } = useLocalization();
3236
+ const { itemText, pageText } = React__namespace.useMemo(() => {
3237
+ var _a;
3238
+ const dividerText = props.itemDividerText || getText('of');
3239
+ let itemText = '';
3240
+ if (props.totalItems > 0) {
3241
+ itemText = `${props.minItem.toLocaleString()}-${props.maxItem.toLocaleString()} ${dividerText} ${props.totalItems.toLocaleString()}`;
3242
+ }
3243
+ else {
3244
+ itemText = props.noResultsText || getText('No Results');
3245
+ }
3246
+ let pageText;
3247
+ if (props.pageIndex !== undefined && props.totalPages) {
3248
+ pageText = `${(_a = props.pageText) !== null && _a !== void 0 ? _a : getText('Page')} ${(props.pageIndex + 1).toLocaleString()} ${dividerText} ${props.totalPages.toLocaleString()}`;
3249
+ }
3250
+ return { itemText, pageText };
3251
+ }, [language]);
3195
3252
  const theme = useThemeSafely();
3196
3253
  const pagerStyles = css.css `
3197
3254
  display: grid;
@@ -3235,23 +3292,37 @@ const Pager = (props) => {
3235
3292
  };
3236
3293
 
3237
3294
  const BoundMemoryPager = (p) => {
3238
- var _a, _b, _c;
3295
+ var _a;
3239
3296
  const { pager, showPageText } = p, rest = __rest(p, ["pager", "showPageText"]);
3240
- return (React__namespace.createElement(Pager, Object.assign({}, rest, { pageIndex: showPageText ? pager.page : undefined, totalPages: 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__namespace.createElement(Label, { text: (_a = p.limitText) !== null && _a !== void 0 ? _a : 'Limit', orientation: "horizontal" },
3241
- React__namespace.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__namespace.createElement(Label, { text: (_b = p.sortText) !== null && _b !== void 0 ? _b : 'Sort', orientation: "horizontalReverse" },
3242
- React__namespace.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 => {
3297
+ const { getText, language } = useLocalization();
3298
+ const { limitText, sortText } = React__namespace.useMemo(() => {
3299
+ var _a, _b;
3300
+ const limitText = (_a = p.limitText) !== null && _a !== void 0 ? _a : getText("Limit");
3301
+ const sortText = (_b = p.sortText) !== null && _b !== void 0 ? _b : getText("Sort");
3302
+ return { limitText, sortText };
3303
+ }, [language]);
3304
+ return (React__namespace.createElement(Pager, Object.assign({}, rest, { pageIndex: showPageText ? pager.page : undefined, totalPages: 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__namespace.createElement(Label, { text: limitText, orientation: "horizontal" },
3305
+ React__namespace.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__namespace.createElement(Label, { text: sortText, orientation: "horizontalReverse" },
3306
+ React__namespace.createElement(Picker, { value: (_a = pager.sort) !== null && _a !== void 0 ? _a : '', options: pager.sortOptions, onValueChange: v => { var _a; return (_a = p.onSort) === null || _a === void 0 ? void 0 : _a.call(p, v); } }))) : undefined, page: d => {
3243
3307
  p.onPage(d);
3244
3308
  } })));
3245
3309
  };
3246
3310
 
3247
3311
  const BoundStaticPager = (p) => {
3248
- var _a, _b, _c, _d, _e, _f;
3312
+ var _a, _b, _c, _d;
3249
3313
  const { result, showPageText } = p, rest = __rest(p, ["result", "showPageText"]);
3314
+ const { getText, language } = useLocalization();
3315
+ const { limitText, sortText } = React__namespace.useMemo(() => {
3316
+ var _a, _b;
3317
+ const limitText = (_a = p.limitText) !== null && _a !== void 0 ? _a : getText("Limit");
3318
+ const sortText = (_b = p.sortText) !== null && _b !== void 0 ? _b : getText("Sort");
3319
+ return { limitText, sortText };
3320
+ }, [language]);
3250
3321
  const showLimit = !!(result.limit && p.limitOptions && p.limitOptions.length > 1 && p.onLimit);
3251
3322
  const showSort = !!(p.sort !== undefined && p.sortOptions && p.sortOptions.length > 1 && p.onSort);
3252
- return (React__namespace.createElement(Pager, Object.assign({}, rest, { pageIndex: showPageText ? result.page : undefined, totalPages: showPageText ? result.totalPages : undefined, canGoNext: result.hasNext, canGoPrevious: result.hasPrevious, minItem: result.minPageItemIndex + 1, maxItem: result.maxPageItemIndex + 1, totalItems: result.total, leftControls: showLimit ? (React__namespace.createElement(Label, { text: (_a = p.limitText) !== null && _a !== void 0 ? _a : 'Limit', orientation: "horizontal" },
3253
- React__namespace.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__namespace.createElement(Label, { text: (_d = p.sortText) !== null && _d !== void 0 ? _d : 'Sort', orientation: "horizontalReverse" },
3254
- React__namespace.createElement(Picker, { value: (_e = p.sort) !== null && _e !== void 0 ? _e : '', options: (_f = p.sortOptions) !== null && _f !== void 0 ? _f : [], onValueChange: v => {
3323
+ return (React__namespace.createElement(Pager, Object.assign({}, rest, { pageIndex: showPageText ? result.page : undefined, totalPages: showPageText ? result.totalPages : undefined, canGoNext: result.hasNext, canGoPrevious: result.hasPrevious, minItem: result.minPageItemIndex + 1, maxItem: result.maxPageItemIndex + 1, totalItems: result.total, leftControls: showLimit ? (React__namespace.createElement(Label, { text: limitText, orientation: "horizontal" },
3324
+ React__namespace.createElement(Picker, { value: (_a = result.limit) !== null && _a !== void 0 ? _a : 1, options: (_b = p.limitOptions) !== null && _b !== void 0 ? _b : [1], onValueChange: v => { var _a; return (_a = p.onLimit) === null || _a === void 0 ? void 0 : _a.call(p, v); } }))) : undefined, rightControls: showSort ? (React__namespace.createElement(Label, { text: sortText, orientation: "horizontalReverse" },
3325
+ React__namespace.createElement(Picker, { value: (_c = p.sort) !== null && _c !== void 0 ? _c : '', options: (_d = p.sortOptions) !== null && _d !== void 0 ? _d : [], onValueChange: v => {
3255
3326
  var _a;
3256
3327
  (_a = p.onSort) === null || _a === void 0 ? void 0 : _a.call(p, v);
3257
3328
  } }))) : undefined, page: d => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mackin.com/styleguide",
3
- "version": "10.0.3",
3
+ "version": "10.1.0",
4
4
  "description": "",
5
5
  "main": "./index.js",
6
6
  "module": "./index.esm.js",