@mackin.com/styleguide 7.1.1 → 7.3.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 +39 -2
  2. package/index.js +108 -9
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -268,9 +268,11 @@ declare const Header: (props: {
268
268
  toggleMenu?: () => void | undefined;
269
269
  /** If true, will hide menu button on larger screens */
270
270
  responsive?: boolean;
271
- rightElements?: JSX.Element;
272
271
  inline?: boolean;
273
272
  className?: string;
273
+ leftElements?: JSX.Element;
274
+ rightElements?: JSX.Element;
275
+ /** Elements will appear in the center of the header and slightly below. */
274
276
  centerOffsetElements?: JSX.Element;
275
277
  noMenu?: boolean;
276
278
  /** Will fill the title center aligned with wrapping. */
@@ -345,10 +347,14 @@ interface InputProps {
345
347
  debounceMs?: number;
346
348
  /** Defaults to 'off'. */
347
349
  autoComplete?: string;
350
+ autoFocus?: boolean;
348
351
  /** Called with debounce when the input changes. */
349
352
  onChange?: (value: InputValue, name?: string) => void;
350
353
  onFocus?: (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
351
354
  onBlur?: (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
355
+ onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
356
+ onKeyUp?: (event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
357
+ onKeyPress?: (event: React.KeyboardEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
352
358
  /** Defaults to 100. Ignored for type=number and type=date. */
353
359
  maxLength?: number;
354
360
  /** Ignored for type=textarea. */
@@ -685,6 +691,8 @@ interface MackinTheme {
685
691
  fonts: {
686
692
  family: string;
687
693
  size: string;
694
+ sizeSmall: string;
695
+ sizeLarge: string;
688
696
  headerFamily: string;
689
697
  };
690
698
  controls: {
@@ -901,4 +909,33 @@ declare const Backdrop: (p: {
901
909
 
902
910
  declare const useMediaQuery: (query: string) => boolean;
903
911
 
904
- 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, ThemeProvider, ToggleButton, ToggleButtonGroup, ToggleButtonGroupProps, ToggleButtonProps, TogglePasswordInput, Tr, WaitingIndicator, calcDynamicThemeProps, defaultTheme, getCurrencyDisplay, mergeClassNames, useMediaQuery, useThemeSafely };
912
+ interface AutoCompleteItem {
913
+ id: string | number;
914
+ name: string;
915
+ }
916
+ declare type AutocompleteValue = string | AutoCompleteItem;
917
+ interface AutocompleteProps {
918
+ value: AutocompleteValue | undefined;
919
+ round?: boolean;
920
+ rounded?: boolean;
921
+ rightControl?: JSX.Element;
922
+ placeholder?: string;
923
+ id?: string;
924
+ disabled?: boolean;
925
+ className?: string;
926
+ inputClassName?: string;
927
+ /** If not set, defaults to 100 */
928
+ maxLength?: number;
929
+ required?: boolean;
930
+ /** Limits what will be show in the autocomplete options. Default is 7. */
931
+ maxShownValues?: number;
932
+ /** The minimum characters before 'getOptions' is called. No default. */
933
+ minChars?: number;
934
+ onChange: (value: string) => void;
935
+ getOptions: (value: string) => Promise<AutocompleteValue[]>;
936
+ onPick: (value: AutocompleteValue) => void;
937
+ onKeyPress?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
938
+ }
939
+ declare const Autocomplete: (p: AutocompleteProps) => JSX.Element;
940
+
941
+ export { Alignment, Autocomplete, 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, ThemeProvider, ToggleButton, ToggleButtonGroup, ToggleButtonGroupProps, ToggleButtonProps, TogglePasswordInput, Tr, WaitingIndicator, calcDynamicThemeProps, defaultTheme, getCurrencyDisplay, mergeClassNames, useMediaQuery, useThemeSafely };
package/index.js CHANGED
@@ -108,6 +108,8 @@ const defaultTheme = {
108
108
  fonts: {
109
109
  family: 'Arial, Helvetica, sans-serif',
110
110
  size: '16px',
111
+ sizeSmall: '0.7rem',
112
+ sizeLarge: '1.3rem',
111
113
  headerFamily: 'Arial, Helvetica, sans-serif',
112
114
  },
113
115
  controls: {
@@ -567,7 +569,7 @@ const Text = (props) => {
567
569
  const styles = css.css({
568
570
  userSelect: 'text',
569
571
  label: 'Text'
570
- }, tagStyles[tagChoice], alignStyles[(_a = props.align) !== null && _a !== void 0 ? _a : 'left'], props.smaller && { fontSize: '0.7rem' }, props.larger && { fontSize: '1.3rem' }, props.italics && { fontStyle: 'italic' }, props.ellipsis && {
572
+ }, tagStyles[tagChoice], alignStyles[(_a = props.align) !== null && _a !== void 0 ? _a : 'left'], props.smaller && { fontSize: theme.fonts.sizeSmall }, props.larger && { fontSize: theme.fonts.sizeLarge }, props.italics && { fontStyle: 'italic' }, props.ellipsis && {
571
573
  overflow: 'hidden',
572
574
  whiteSpace: 'nowrap',
573
575
  textOverflow: 'ellipsis'
@@ -800,7 +802,6 @@ const Pager = (props) => {
800
802
  display: block;
801
803
  }
802
804
  `;
803
- //TB: check this after fixing buttons
804
805
  const buttonStyles = css.css({
805
806
  backgroundColor: 'transparent'
806
807
  });
@@ -1636,7 +1637,6 @@ const FormColumnRow = (props) => {
1636
1637
  }), props.className) }, props.children));
1637
1638
  };
1638
1639
 
1639
- //TB: have a sticky variant
1640
1640
  const Header = (props) => {
1641
1641
  const theme = useThemeSafely();
1642
1642
  const bodyStyles = css.css `
@@ -1696,6 +1696,12 @@ const Header = (props) => {
1696
1696
  grid-auto-flow: column;
1697
1697
  align-items: center;
1698
1698
  `;
1699
+ const leftElementStyles = props.leftElements && css.css({
1700
+ display: 'grid',
1701
+ gap: '0.5rem',
1702
+ gridAutoFlow: 'column',
1703
+ alignItems: 'center'
1704
+ });
1699
1705
  const centerElementsStyles = props.centerOffsetElements && css.css `
1700
1706
  position: absolute;
1701
1707
  display: flex;
@@ -1721,6 +1727,7 @@ const Header = (props) => {
1721
1727
  return (React__namespace.createElement("div", { className: css.cx('header', headerStyles, props.className) },
1722
1728
  !props.noMenu && (React__namespace.createElement(Button, { variant: "icon", className: headerButtonStyles, onClick: toggleMenu },
1723
1729
  React__namespace.createElement(Icon, { id: "menu" }))),
1730
+ props.leftElements && React__namespace.createElement("div", { className: leftElementStyles }, props.leftElements),
1724
1731
  title,
1725
1732
  props.rightElements && React__namespace.createElement("div", { className: rightElementStyles }, props.rightElements),
1726
1733
  props.centerOffsetElements && (React__namespace.createElement("div", { className: centerElementsStyles },
@@ -2033,24 +2040,24 @@ const Input = React__namespace.forwardRef((props, ref) => {
2033
2040
  };
2034
2041
  }
2035
2042
  if (props.type === 'number') {
2036
- inputElement = React__namespace.createElement("input", { ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly,
2043
+ inputElement = React__namespace.createElement("input", { autoFocus: props.autoFocus, ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly,
2037
2044
  // set fixed default to defeat pasting stupid numbers
2038
- maxLength: 50, min: props.min, max: props.max, required: props.required, disabled: props.disabled, id: props.id, className: css.cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: "number", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange });
2045
+ maxLength: 50, min: props.min, max: props.max, required: props.required, disabled: props.disabled, id: props.id, className: css.cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: "number", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress });
2039
2046
  }
2040
2047
  else if (props.type === 'date') {
2041
- inputElement = React__namespace.createElement("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, className: css.cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: "text", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange });
2048
+ inputElement = React__namespace.createElement("input", { autoFocus: props.autoFocus, ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: 10, required: props.required, disabled: props.disabled, id: props.id, className: css.cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: "text", value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress });
2042
2049
  }
2043
2050
  else if (props.type === 'textarea') {
2044
- inputElement = React__namespace.createElement("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, className: css.cx(css.css `
2051
+ inputElement = React__namespace.createElement("textarea", { autoFocus: props.autoFocus, ref: ref, name: props.name, style: props.style, rows: props.rows || 10, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, className: css.cx(css.css `
2045
2052
  ${inputStyles}
2046
2053
  max-width: 100%;
2047
2054
  min-height: ${theme.controls.height};
2048
2055
  padding-top: 0.75rem;
2049
- height:auto;`, props.inputClassName), placeholder: props.placeholder, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange });
2056
+ height:auto;`, props.inputClassName), placeholder: props.placeholder, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress });
2050
2057
  }
2051
2058
  else {
2052
2059
  // text, password, email, and url
2053
- inputElement = React__namespace.createElement("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, className: css.cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: props.type, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange });
2060
+ inputElement = React__namespace.createElement("input", { autoFocus: props.autoFocus, ref: ref, name: props.name, pattern: props.pattern, style: props.style, autoComplete: autoComplete, tabIndex: props.readOnly ? -1 : undefined, readOnly: props.readOnly, maxLength: props.maxLength || DEFAULT_MAX_LENGTH, required: props.required, disabled: props.disabled, id: props.id, className: css.cx(inputStyles, props.inputClassName), placeholder: props.placeholder, type: props.type, value: localValue, onFocus: onFocus, onBlur: onBlur, onChange: localOnChange, onKeyDown: props.onKeyDown, onKeyUp: props.onKeyUp, onKeyPress: props.onKeyPress });
2054
2061
  }
2055
2062
  const inputWrapperStyles = css.css `
2056
2063
  width:100%;
@@ -3358,6 +3365,98 @@ const Backdrop = (props) => {
3358
3365
  }, ref: backdrop, className: css.cx('backdrop', styles, props.className) }, props.children));
3359
3366
  };
3360
3367
 
3368
+ const DEFAULT_MAX_SHOWN_VALUES = 7;
3369
+ const getAutocompleteValueText = (v) => {
3370
+ if (!v) {
3371
+ return '';
3372
+ }
3373
+ if (typeof v === 'string') {
3374
+ return v;
3375
+ }
3376
+ return v.name;
3377
+ };
3378
+ const getAutocompleteValueId = (v) => {
3379
+ if (typeof v === 'string') {
3380
+ return v;
3381
+ }
3382
+ return v.id;
3383
+ };
3384
+ const Autocomplete = (p) => {
3385
+ const element = React__namespace.useRef(null);
3386
+ const input = React__namespace.useRef(null);
3387
+ const [values, setValues] = React__namespace.useState([]);
3388
+ const showValues = React__namespace.useMemo(() => values.length > 0, [values]);
3389
+ const shownValues = React__namespace.useMemo(() => {
3390
+ var _a;
3391
+ return values.slice(0, (_a = p.maxShownValues) !== null && _a !== void 0 ? _a : DEFAULT_MAX_SHOWN_VALUES);
3392
+ }, [values]);
3393
+ const theme = useThemeSafely();
3394
+ const baseClass = css.css `
3395
+ label: Autocomplete;
3396
+ position: relative;
3397
+ width: 100%;
3398
+ `;
3399
+ const listClass = css.css `
3400
+ position: absolute;
3401
+ width: 100%;
3402
+ border: ${theme.controls.border};
3403
+ box-shadow: ${theme.controls.boxShadow};
3404
+ background-color: ${theme.colors.bg};
3405
+ margin-top: -4px !important;
3406
+ z-index: ${theme.zIndexes.backdrop};
3407
+ ${p.round || p.rounded && `
3408
+ border-radius: ${theme.controls.roundedRadius};
3409
+ `}
3410
+ `;
3411
+ const inputClass = css.css `
3412
+ ${showValues && `
3413
+ z-index: ${theme.zIndexes.backdrop};
3414
+ position: relative;
3415
+ `}
3416
+ `;
3417
+ return (React__namespace.createElement("div", { ref: element, className: css.cx(baseClass, 'autocomplete') },
3418
+ React__namespace.createElement(Backdrop, { onClick: () => setValues([]), show: showValues, allowScroll: true, transparent: true }),
3419
+ React__namespace.createElement(TabLocker, { disabled: !showValues, style: { position: 'relative' } },
3420
+ React__namespace.createElement(Input, { ref: input, debounceMs: 0, type: "text", value: getAutocompleteValueText(p.value), round: p.round, rounded: p.rounded, rightControl: p.rightControl, placeholder: p.placeholder, id: p.id, disabled: p.disabled, className: p.className, inputClassName: css.cx(inputClass, p.inputClassName), maxLength: p.maxLength, required: p.required, onChange: v => {
3421
+ const value = v;
3422
+ p.onChange(value);
3423
+ if (!p.minChars || value.length >= p.minChars) {
3424
+ p.getOptions(value)
3425
+ .then(vals => {
3426
+ setValues(vals);
3427
+ }).catch(err => {
3428
+ // ignore it
3429
+ });
3430
+ }
3431
+ else {
3432
+ setValues([]);
3433
+ }
3434
+ }, onKeyPress: e => { var _a; return (_a = p.onKeyPress) === null || _a === void 0 ? void 0 : _a.call(p, e); } }),
3435
+ showValues && (React__namespace.createElement(List, { className: listClass },
3436
+ shownValues.map(value => {
3437
+ return (React__namespace.createElement(ListItem, { key: getAutocompleteValueId(value), variant: "full" },
3438
+ React__namespace.createElement(Button, { onClick: () => {
3439
+ p.onPick(value);
3440
+ setValues([]);
3441
+ setTimeout(() => {
3442
+ var _a;
3443
+ // we need to wait until the component is re-rendered.
3444
+ // outside changes to Inputs will be ignored if the component has focus.
3445
+ (_a = input.current) === null || _a === void 0 ? void 0 : _a.focus();
3446
+ }, 0);
3447
+ } },
3448
+ React__namespace.createElement(Text, { tag: "div", ellipsis: true, align: "left" }, getAutocompleteValueText(value)))));
3449
+ }),
3450
+ shownValues.length < values.length && (React__namespace.createElement(ListItem, null,
3451
+ React__namespace.createElement(Text, { tag: "div", italics: true, align: "center" },
3452
+ "Showing ",
3453
+ shownValues.length.toLocaleString(),
3454
+ " of ",
3455
+ values.length.toLocaleString(),
3456
+ " results."))))))));
3457
+ };
3458
+
3459
+ exports.Autocomplete = Autocomplete;
3361
3460
  exports.Backdrop = Backdrop;
3362
3461
  exports.Backdrop2 = Backdrop$1;
3363
3462
  exports.BoundMemoryPager = BoundMemoryPager;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mackin.com/styleguide",
3
- "version": "7.1.1",
3
+ "version": "7.3.0",
4
4
  "description": "",
5
5
  "main": "./index.js",
6
6
  "types": "./index.d.ts",