@trackunit/react-form-components 0.0.297-alpha-3fd0172a5e.0 → 0.0.301

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.
package/index.cjs.js CHANGED
@@ -12,6 +12,7 @@ var ReactSelect = require('react-select');
12
12
  var ReactAsyncCreatableSelect = require('react-select/async-creatable');
13
13
  var ReactCreatableSelect = require('react-select/creatable');
14
14
  var ReactAsyncSelect = require('react-select/async');
15
+ var reactUse = require('react-use');
15
16
  var dateFns = require('date-fns');
16
17
  var parsePhoneNumberFromString = require('libphonenumber-js');
17
18
 
@@ -44,6 +45,7 @@ var ReactAsyncSelect__default = /*#__PURE__*/_interopDefaultLegacy(ReactAsyncSel
44
45
  var parsePhoneNumberFromString__default = /*#__PURE__*/_interopDefaultLegacy(parsePhoneNumberFromString);
45
46
 
46
47
  var defaultTranslations = {
48
+ "clearIndicator.icon.tooltip.clearAll": "Clear all",
47
49
  "phoneField.error.INVALID_COUNTRY": "The country code is not valid",
48
50
  "phoneField.error.INVALID_LENGTH": "The phone number is not valid",
49
51
  "phoneField.error.INVALID_NUMBER": "The phone number is not valid",
@@ -616,6 +618,7 @@ const TagsContainer = ({ items, width = "100%", itemsGap = 5, postFix, disabled
616
618
  * @returns {Partial<SelectComponents<Option, boolean, GroupBase<Option>>> | undefined} components object to override react-select default components
617
619
  */
618
620
  const useCustomComponents = (componentsProps, disabled, menuIsOpen, refMenuIsEnabled, dataTestId, maxSelectedDisplayCount, dropdownIcon) => {
621
+ const [t] = useTranslation();
619
622
  // perhaps it should not be wrap in memo (causing some issues with opening and closing on mobiles)
620
623
  const customComponents = React__namespace.useMemo(() => {
621
624
  return Object.assign({ ValueContainer: props => {
@@ -660,7 +663,7 @@ const useCustomComponents = (componentsProps, disabled, menuIsOpen, refMenuIsEna
660
663
  if (disabled) {
661
664
  return null;
662
665
  }
663
- return (jsxRuntime.jsx(ReactSelect.components.ClearIndicator, Object.assign({}, props, { children: jsxRuntime.jsx("div", { className: cvaSelectXIcon(), onClick: props.clearValue, "data-testid": dataTestId && `${dataTestId}-XMarkIcon`, children: jsxRuntime.jsx(reactComponents.Icon, { name: "XCircle", size: "medium" }) }) })));
666
+ return (jsxRuntime.jsx(ReactSelect.components.ClearIndicator, Object.assign({}, props, { children: jsxRuntime.jsx("div", { className: cvaSelectXIcon(), onClick: props.clearValue, "data-testid": dataTestId && `${dataTestId}-XMarkIcon`, children: jsxRuntime.jsx(reactComponents.Icon, { title: t("clearIndicator.icon.tooltip.clearAll"), name: "XCircle", size: "medium" }) }) })));
664
667
  }, Control: props => {
665
668
  return jsxRuntime.jsx(ReactSelect.components.Control, Object.assign({}, props, { className: props.isDisabled ? "bg-slate-100" : "" }));
666
669
  }, SingleValue: props => {
@@ -697,13 +700,13 @@ const useCustomComponents = (componentsProps, disabled, menuIsOpen, refMenuIsEna
697
700
  * @param {StylesConfig<Option, IsMulti, Group> | undefined} styles a optional object to override styles of react-select
698
701
  * @returns {StylesConfig<Option, boolean>} styles to override in select
699
702
  */
700
- const useCustomStyles = (refContainer, refPrefix, maxSelectedDisplayCount, styles) => {
703
+ const useCustomStyles = (refContainer, refPrefix, maxSelectedDisplayCount, styles, disabled) => {
701
704
  const customStyles = React__namespace.useMemo(() => {
702
705
  return Object.assign({ control: base => {
703
706
  return Object.assign(Object.assign({}, base), { border: "0", boxShadow: "0", "&:hover": {
704
707
  border: "0",
705
708
  }, marginRight: "2px", backgroundColor: "" });
706
- }, singleValue: base => (Object.assign({}, base)), multiValue: base => (Object.assign({}, base)), multiValueLabel: base => (Object.assign({}, base)), indicatorsContainer: base => (Object.assign({}, base)), indicatorSeparator: () => ({
709
+ }, singleValue: base => (Object.assign({}, base)), multiValue: base => (Object.assign({}, base)), multiValueLabel: base => (Object.assign({}, base)), indicatorsContainer: base => (Object.assign(Object.assign({}, base), (disabled && { display: "none" }))), indicatorSeparator: () => ({
707
710
  width: "0px",
708
711
  }), menu: base => {
709
712
  return Object.assign(Object.assign({}, base), { width: "100%", marginTop: "4px", marginBottom: "18px", transition: "all 1s ease-in-out" });
@@ -730,7 +733,7 @@ const useSelect = (_a) => {
730
733
  var { id, className, dataTestId = "select", prefix, async, dropdownIcon, maxMenuHeight = 200, label, hasError, disabled, isMulti, components, value, options, onChange, isLoading, classNamePrefix = "", onMenuOpen, onMenuClose, maxSelectedDisplayCount = undefined, isClearable = false, isSearchable = true, onMenuScrollToBottom, styles, filterOption, onInputChange } = _a, props = __rest(_a, ["id", "className", "dataTestId", "prefix", "async", "dropdownIcon", "maxMenuHeight", "label", "hasError", "disabled", "isMulti", "components", "value", "options", "onChange", "isLoading", "classNamePrefix", "onMenuOpen", "onMenuClose", "maxSelectedDisplayCount", "isClearable", "isSearchable", "onMenuScrollToBottom", "styles", "filterOption", "onInputChange"]);
731
734
  const refContainer = React__default["default"].useRef(null);
732
735
  const refPrefix = React__default["default"].useRef(null);
733
- const { customStyles } = useCustomStyles(refContainer, refPrefix, maxSelectedDisplayCount, styles);
736
+ const { customStyles } = useCustomStyles(refContainer, refPrefix, maxSelectedDisplayCount, styles, disabled);
734
737
  const [menuIsOpen, setMenuIsOpen] = React__default["default"].useState((_b = props.menuIsOpen) !== null && _b !== void 0 ? _b : false);
735
738
  const refMenuIsEnabled = React__default["default"].useRef(true);
736
739
  const customComponents = useCustomComponents(components, disabled || false, menuIsOpen, refMenuIsEnabled, dataTestId, maxSelectedDisplayCount, dropdownIcon);
@@ -824,7 +827,7 @@ const CreatableSelect = (props) => {
824
827
  */
825
828
  const Select = (props) => {
826
829
  const { className } = props, propsNoClassName = __rest(props, ["className"]);
827
- const { id, dataTestId = "select", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, value, options, onChange, isLoading, classNamePrefix = dataTestId ? dataTestId : "select", onMenuScrollToBottom, filterOption, onInputChange, isSearchable, isClearable = false, readOnly, } = props;
830
+ const { id, dataTestId = "select", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, value, options, onChange, isLoading, classNamePrefix = dataTestId ? dataTestId : "select", onMenuScrollToBottom, filterOption, onInputChange, isSearchable, isClearable = false, readOnly, openMenuOnClick = !disabled, openMenuOnFocus = !disabled, } = props;
828
831
  const { refContainer, refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
829
832
  const selectProps = {
830
833
  value,
@@ -841,9 +844,9 @@ const Select = (props) => {
841
844
  isSearchable: disabled || readOnly ? false : isSearchable,
842
845
  menuShouldBlockScroll: true,
843
846
  menuShouldScrollIntoView: true,
844
- openMenuOnFocus: true,
847
+ openMenuOnFocus,
845
848
  menuIsOpen: !readOnly ? menuIsOpen : false,
846
- openMenuOnClick: true,
849
+ openMenuOnClick,
847
850
  closeMenuOnSelect: false,
848
851
  isMulti,
849
852
  classNamePrefix,
@@ -928,7 +931,9 @@ const cvaActionContainer = cssClassVarianceUtilities.cvaMerge(["flex", "items-ce
928
931
  * @param {ActionButtonProps} props - The props for the ActionButton component
929
932
  * @returns {JSX.Element} ActionButton component
930
933
  */
931
- const ActionButton = ({ type, value, dataTestId, iconSize, disabled, className, }) => {
934
+ const ActionButton = ({ type, value, dataTestId, iconSize, disabled, key, className }) => {
935
+ const [, copyToClipboard] = reactUse.useCopyToClipboard();
936
+ const elementKey = React.useMemo(() => key !== null && key !== void 0 ? key : `ActionButton-${uuid.v4()}`, [key]);
932
937
  const getIconName = () => {
933
938
  switch (type) {
934
939
  case "PHONE_NUMBER":
@@ -936,11 +941,14 @@ const ActionButton = ({ type, value, dataTestId, iconSize, disabled, className,
936
941
  case "WEB_ADDRESS":
937
942
  return "ArrowTopRightOnSquare";
938
943
  case "EMAIL":
939
- default:
940
944
  return "Envelope";
945
+ case "COPY":
946
+ default:
947
+ return "ClipboardDocument";
941
948
  }
942
949
  };
943
950
  const ButtonAction = () => {
951
+ var _a, _b;
944
952
  switch (type) {
945
953
  case "EMAIL":
946
954
  return window.open(`mailto:${value}`);
@@ -950,11 +958,17 @@ const ActionButton = ({ type, value, dataTestId, iconSize, disabled, className,
950
958
  }
951
959
  return null;
952
960
  case "PHONE_NUMBER":
953
- default:
954
961
  return window.open(`tel:${value}`);
962
+ case "COPY":
963
+ return copyToClipboard((_b = (_a = value === null || value === void 0 ? void 0 : value.current) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "");
964
+ default:
965
+ return null;
955
966
  }
956
967
  };
957
- return (jsxRuntime.jsx("div", { className: cvaActionContainer({ className }), children: jsxRuntime.jsx(reactComponents.IconButton, { disabled: disabled, dataTestId: dataTestId || "testIconButtonId", size: "small", variant: "secondary", icon: jsxRuntime.jsx(reactComponents.Icon, { size: "small", name: getIconName() }), onClick: ButtonAction, className: cvaActionButton({ size: iconSize }) }) }));
968
+ if (ButtonAction === null) {
969
+ return null;
970
+ }
971
+ return (jsxRuntime.jsx("div", { className: cvaActionContainer({ className }), children: jsxRuntime.jsx(reactComponents.IconButton, { disabled: disabled, dataTestId: dataTestId || "testIconButtonId", size: "small", variant: "secondary", icon: jsxRuntime.jsx(reactComponents.Icon, { size: "small", name: getIconName() }), onClick: ButtonAction, className: cvaActionButton({ size: iconSize }) }) }, elementKey));
958
972
  };
959
973
 
960
974
  const cvaInputBase = cssClassVarianceUtilities.cvaMerge([
@@ -1055,9 +1069,11 @@ const cvaInputSuffix = cssClassVarianceUtilities.cvaMerge(["flex", "justify-cent
1055
1069
  * This is a base used by our other input components such as TextInput, NumberInput, PasswordInput, etc.
1056
1070
  */
1057
1071
  const BaseInput = React__namespace.forwardRef((_a, ref) => {
1058
- var { className, isInvalid, dataTestId, prefix, suffix, addonBefore, addonAfter, actions, fieldSize = "medium", nonInteractive = false, type, inputClassName, addonBeforeClassName } = _a, rest = __rest(_a, ["className", "isInvalid", "dataTestId", "prefix", "suffix", "addonBefore", "addonAfter", "actions", "fieldSize", "nonInteractive", "type", "inputClassName", "addonBeforeClassName"]);
1072
+ var { className, isInvalid, dataTestId, prefix, suffix, addonBefore, addonAfter, actions, fieldSize = "medium", nonInteractive = false, showDefaultActions = false, type, inputClassName, addonBeforeClassName } = _a, rest = __rest(_a, ["className", "isInvalid", "dataTestId", "prefix", "suffix", "addonBefore", "addonAfter", "actions", "fieldSize", "nonInteractive", "showDefaultActions", "type", "inputClassName", "addonBeforeClassName"]);
1059
1073
  const isReadOnlyWithPlaceholder = React.useMemo(() => !!rest.readOnly && !!rest.placeholder && !rest.value, [rest.readOnly, rest.placeholder, rest.value]);
1060
1074
  const renderAsDisabled = rest.disabled || rest.readOnly;
1075
+ const innerRef = React__namespace.useRef(null);
1076
+ React__namespace.useImperativeHandle(ref, () => innerRef.current, []);
1061
1077
  return (jsxRuntime.jsxs("div", { className: cvaInput({
1062
1078
  disabled: renderAsDisabled,
1063
1079
  invalid: isInvalid,
@@ -1065,9 +1081,9 @@ const BaseInput = React__namespace.forwardRef((_a, ref) => {
1065
1081
  className,
1066
1082
  }), "data-testid": dataTestId && `${dataTestId}-container`, children: [addonBefore && addonBefore !== undefined ? (jsxRuntime.jsx("div", { className: cvaInputAddonBefore({ className: addonBeforeClassName }), "data-testid": dataTestId && `${dataTestId}-addonBefore`, children: addonBefore })) : null, prefix && addonBefore !== prefix ? (jsxRuntime.jsx("div", { className: cvaInputPrefix({
1067
1083
  disabled: renderAsDisabled,
1068
- }), "data-testid": dataTestId && `${dataTestId}-prefix`, children: prefix })) : null, jsxRuntime.jsx("input", Object.assign({ ref: ref, "data-testid": dataTestId, className: cvaInputField({ size: fieldSize, disabled: renderAsDisabled, className: inputClassName }), type: isReadOnlyWithPlaceholder ? "text" : type }, rest, { readOnly: rest.readOnly || nonInteractive })), suffix && addonBefore !== suffix ? (jsxRuntime.jsx("div", { className: cvaInputSuffix({
1084
+ }), "data-testid": dataTestId && `${dataTestId}-prefix`, children: prefix })) : null, jsxRuntime.jsx("input", Object.assign({ ref: innerRef, "data-testid": dataTestId, className: cvaInputField({ size: fieldSize, disabled: renderAsDisabled, className: inputClassName }), type: isReadOnlyWithPlaceholder ? "text" : type }, rest, { readOnly: rest.readOnly || nonInteractive })), suffix && addonBefore !== suffix ? (jsxRuntime.jsx("div", { className: cvaInputSuffix({
1069
1085
  disabled: renderAsDisabled,
1070
- }), "data-testid": dataTestId && `${dataTestId}-suffix`, children: suffix })) : null, actions && addonBefore !== actions ? actions : null, addonAfter && addonAfter !== undefined && addonBefore !== addonAfter ? (jsxRuntime.jsx("div", { className: cvaInputAddonAfter(), "data-testid": dataTestId && `${dataTestId}-addonAfter`, children: addonAfter })) : null] }));
1086
+ }), "data-testid": dataTestId && `${dataTestId}-suffix`, children: suffix })) : null, rest.readOnly === true && showDefaultActions ? (jsxRuntime.jsx(ActionButton, { type: "COPY", value: innerRef }, "default-copy-action")) : null, actions && addonBefore !== actions ? actions : null, addonAfter && addonAfter !== undefined && addonBefore !== addonAfter ? (jsxRuntime.jsx("div", { className: cvaInputAddonAfter(), "data-testid": dataTestId && `${dataTestId}-addonAfter`, children: addonAfter })) : null] }));
1071
1087
  });
1072
1088
 
1073
1089
  /**
@@ -1303,7 +1319,7 @@ const OptionCard = React.forwardRef((_a, ref) => {
1303
1319
  *
1304
1320
  * NOTE: If shown with a label, please use the `PasswordField` component instead.
1305
1321
  */
1306
- const PasswordInput = React.forwardRef((props, ref) => (jsxRuntime.jsx(BaseInput, Object.assign({ ref: ref, type: props.obfuscate ? "text" : "password" }, props))));
1322
+ const PasswordInput = React.forwardRef((props, ref) => (jsxRuntime.jsx(BaseInput, Object.assign({ ref: ref }, props))));
1307
1323
 
1308
1324
  /**
1309
1325
  * Password fields enter a password or other confidential information. Characters are masked as they are typed.
@@ -1320,7 +1336,7 @@ const PasswordField = React.forwardRef((_a, ref) => {
1320
1336
  const handleChange = React.useCallback((event) => {
1321
1337
  onChange === null || onChange === void 0 ? void 0 : onChange(event);
1322
1338
  }, [onChange]);
1323
- return (jsxRuntime.jsx(FormGroup, { htmlFor: htmlFor, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, dataTestId: dataTestId && `${dataTestId}-FormGroup`, children: jsxRuntime.jsx(PasswordInput, Object.assign({}, rest, { obfuscate: showPassword, disabled: rest.readOnly, id: htmlFor, "aria-labelledby": htmlFor + "-label", ref: ref, maxLength: maxLength, value: value, isInvalid: renderAsInvalid, className: className, onChange: handleChange, dataTestId: dataTestId, actions: jsxRuntime.jsx(reactComponents.Icon, { className: "absolute top-0 bottom-0 right-2 h-full content-center", name: showPassword ? "EyeSlash" : "Eye", size: "small", color: "neutral", onClick: () => setShowPassword(prevState => !prevState) }) })) }));
1339
+ return (jsxRuntime.jsx(FormGroup, { htmlFor: htmlFor, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, dataTestId: dataTestId && `${dataTestId}-FormGroup`, children: jsxRuntime.jsx(PasswordInput, Object.assign({}, rest, { type: showPassword ? "text" : "password", disabled: rest.readOnly, id: htmlFor, "aria-labelledby": htmlFor + "-label", ref: ref, maxLength: maxLength, value: value, isInvalid: renderAsInvalid, className: className, onChange: handleChange, dataTestId: dataTestId, actions: jsxRuntime.jsx(reactComponents.Icon, { className: "absolute top-0 bottom-0 right-2 h-full content-center", name: showPassword ? "EyeSlash" : "Eye", size: "small", color: "neutral", onClick: () => setShowPassword(prevState => !prevState) }) })) }));
1324
1340
  });
1325
1341
 
1326
1342
  /**
@@ -2114,7 +2130,7 @@ const TimeRangeField = (_a) => {
2114
2130
  var { className, dataTestId, onChange, isInvalid, errorMessage, label, tip, disabled, children, helpText, id } = _a, rest = __rest(_a, ["className", "dataTestId", "onChange", "isInvalid", "errorMessage", "label", "tip", "disabled", "children", "helpText", "id"]);
2115
2131
  const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
2116
2132
  const htmlFor = id ? id : "timeRangeField-" + uuid.v4();
2117
- return (jsxRuntime.jsx(FormGroup, { htmlFor: htmlFor, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, dataTestId: dataTestId && `${dataTestId}-FormGroup`, disabled: disabled, children: jsxRuntime.jsx(TimeRange, Object.assign({ className: className, onChange: onChange, disabled: disabled, dataTestId: dataTestId, isInvalid: renderAsInvalid, children: children }, rest)) }));
2133
+ return (jsxRuntime.jsx(FormGroup, { htmlFor: htmlFor, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, dataTestId: dataTestId && `${dataTestId}-FormGroup`, disabled: disabled, children: jsxRuntime.jsx(TimeRange, Object.assign({ className: className, onChange: onChange, disabled: disabled, dataTestId: dataTestId, isInvalid: renderAsInvalid }, rest, { children: children })) }));
2118
2134
  };
2119
2135
 
2120
2136
  const cvaToggleWrapper = cssClassVarianceUtilities.cvaMerge(["flex", "gap-2", "items-center"]);
package/index.esm.js CHANGED
@@ -10,10 +10,12 @@ export { default as ValueType } from 'react-select';
10
10
  import ReactAsyncCreatableSelect from 'react-select/async-creatable';
11
11
  import ReactCreatableSelect from 'react-select/creatable';
12
12
  import ReactAsyncSelect from 'react-select/async';
13
+ import { useCopyToClipboard } from 'react-use';
13
14
  import { format } from 'date-fns';
14
15
  import parsePhoneNumberFromString, { isSupportedCountry, getCountries, getCountryCallingCode, isValidPhoneNumber, parsePhoneNumberFromString as parsePhoneNumberFromString$1 } from 'libphonenumber-js';
15
16
 
16
17
  var defaultTranslations = {
18
+ "clearIndicator.icon.tooltip.clearAll": "Clear all",
17
19
  "phoneField.error.INVALID_COUNTRY": "The country code is not valid",
18
20
  "phoneField.error.INVALID_LENGTH": "The phone number is not valid",
19
21
  "phoneField.error.INVALID_NUMBER": "The phone number is not valid",
@@ -586,6 +588,7 @@ const TagsContainer = ({ items, width = "100%", itemsGap = 5, postFix, disabled
586
588
  * @returns {Partial<SelectComponents<Option, boolean, GroupBase<Option>>> | undefined} components object to override react-select default components
587
589
  */
588
590
  const useCustomComponents = (componentsProps, disabled, menuIsOpen, refMenuIsEnabled, dataTestId, maxSelectedDisplayCount, dropdownIcon) => {
591
+ const [t] = useTranslation();
589
592
  // perhaps it should not be wrap in memo (causing some issues with opening and closing on mobiles)
590
593
  const customComponents = React.useMemo(() => {
591
594
  return Object.assign({ ValueContainer: props => {
@@ -630,7 +633,7 @@ const useCustomComponents = (componentsProps, disabled, menuIsOpen, refMenuIsEna
630
633
  if (disabled) {
631
634
  return null;
632
635
  }
633
- return (jsx(components.ClearIndicator, Object.assign({}, props, { children: jsx("div", { className: cvaSelectXIcon(), onClick: props.clearValue, "data-testid": dataTestId && `${dataTestId}-XMarkIcon`, children: jsx(Icon, { name: "XCircle", size: "medium" }) }) })));
636
+ return (jsx(components.ClearIndicator, Object.assign({}, props, { children: jsx("div", { className: cvaSelectXIcon(), onClick: props.clearValue, "data-testid": dataTestId && `${dataTestId}-XMarkIcon`, children: jsx(Icon, { title: t("clearIndicator.icon.tooltip.clearAll"), name: "XCircle", size: "medium" }) }) })));
634
637
  }, Control: props => {
635
638
  return jsx(components.Control, Object.assign({}, props, { className: props.isDisabled ? "bg-slate-100" : "" }));
636
639
  }, SingleValue: props => {
@@ -667,13 +670,13 @@ const useCustomComponents = (componentsProps, disabled, menuIsOpen, refMenuIsEna
667
670
  * @param {StylesConfig<Option, IsMulti, Group> | undefined} styles a optional object to override styles of react-select
668
671
  * @returns {StylesConfig<Option, boolean>} styles to override in select
669
672
  */
670
- const useCustomStyles = (refContainer, refPrefix, maxSelectedDisplayCount, styles) => {
673
+ const useCustomStyles = (refContainer, refPrefix, maxSelectedDisplayCount, styles, disabled) => {
671
674
  const customStyles = React.useMemo(() => {
672
675
  return Object.assign({ control: base => {
673
676
  return Object.assign(Object.assign({}, base), { border: "0", boxShadow: "0", "&:hover": {
674
677
  border: "0",
675
678
  }, marginRight: "2px", backgroundColor: "" });
676
- }, singleValue: base => (Object.assign({}, base)), multiValue: base => (Object.assign({}, base)), multiValueLabel: base => (Object.assign({}, base)), indicatorsContainer: base => (Object.assign({}, base)), indicatorSeparator: () => ({
679
+ }, singleValue: base => (Object.assign({}, base)), multiValue: base => (Object.assign({}, base)), multiValueLabel: base => (Object.assign({}, base)), indicatorsContainer: base => (Object.assign(Object.assign({}, base), (disabled && { display: "none" }))), indicatorSeparator: () => ({
677
680
  width: "0px",
678
681
  }), menu: base => {
679
682
  return Object.assign(Object.assign({}, base), { width: "100%", marginTop: "4px", marginBottom: "18px", transition: "all 1s ease-in-out" });
@@ -700,7 +703,7 @@ const useSelect = (_a) => {
700
703
  var { id, className, dataTestId = "select", prefix, async, dropdownIcon, maxMenuHeight = 200, label, hasError, disabled, isMulti, components, value, options, onChange, isLoading, classNamePrefix = "", onMenuOpen, onMenuClose, maxSelectedDisplayCount = undefined, isClearable = false, isSearchable = true, onMenuScrollToBottom, styles, filterOption, onInputChange } = _a, props = __rest(_a, ["id", "className", "dataTestId", "prefix", "async", "dropdownIcon", "maxMenuHeight", "label", "hasError", "disabled", "isMulti", "components", "value", "options", "onChange", "isLoading", "classNamePrefix", "onMenuOpen", "onMenuClose", "maxSelectedDisplayCount", "isClearable", "isSearchable", "onMenuScrollToBottom", "styles", "filterOption", "onInputChange"]);
701
704
  const refContainer = React__default.useRef(null);
702
705
  const refPrefix = React__default.useRef(null);
703
- const { customStyles } = useCustomStyles(refContainer, refPrefix, maxSelectedDisplayCount, styles);
706
+ const { customStyles } = useCustomStyles(refContainer, refPrefix, maxSelectedDisplayCount, styles, disabled);
704
707
  const [menuIsOpen, setMenuIsOpen] = React__default.useState((_b = props.menuIsOpen) !== null && _b !== void 0 ? _b : false);
705
708
  const refMenuIsEnabled = React__default.useRef(true);
706
709
  const customComponents = useCustomComponents(components, disabled || false, menuIsOpen, refMenuIsEnabled, dataTestId, maxSelectedDisplayCount, dropdownIcon);
@@ -794,7 +797,7 @@ const CreatableSelect = (props) => {
794
797
  */
795
798
  const Select = (props) => {
796
799
  const { className } = props, propsNoClassName = __rest(props, ["className"]);
797
- const { id, dataTestId = "select", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, value, options, onChange, isLoading, classNamePrefix = dataTestId ? dataTestId : "select", onMenuScrollToBottom, filterOption, onInputChange, isSearchable, isClearable = false, readOnly, } = props;
800
+ const { id, dataTestId = "select", prefix, async, maxMenuHeight = 200, label, hasError, disabled, isMulti, value, options, onChange, isLoading, classNamePrefix = dataTestId ? dataTestId : "select", onMenuScrollToBottom, filterOption, onInputChange, isSearchable, isClearable = false, readOnly, openMenuOnClick = !disabled, openMenuOnFocus = !disabled, } = props;
798
801
  const { refContainer, refPrefix, customStyles, menuIsOpen, customComponents, menuPlacement, openMenuHandler, closeMenuHandler, orderedOptions, } = useSelect(props);
799
802
  const selectProps = {
800
803
  value,
@@ -811,9 +814,9 @@ const Select = (props) => {
811
814
  isSearchable: disabled || readOnly ? false : isSearchable,
812
815
  menuShouldBlockScroll: true,
813
816
  menuShouldScrollIntoView: true,
814
- openMenuOnFocus: true,
817
+ openMenuOnFocus,
815
818
  menuIsOpen: !readOnly ? menuIsOpen : false,
816
- openMenuOnClick: true,
819
+ openMenuOnClick,
817
820
  closeMenuOnSelect: false,
818
821
  isMulti,
819
822
  classNamePrefix,
@@ -898,7 +901,9 @@ const cvaActionContainer = cvaMerge(["flex", "items-center", "m-1"]);
898
901
  * @param {ActionButtonProps} props - The props for the ActionButton component
899
902
  * @returns {JSX.Element} ActionButton component
900
903
  */
901
- const ActionButton = ({ type, value, dataTestId, iconSize, disabled, className, }) => {
904
+ const ActionButton = ({ type, value, dataTestId, iconSize, disabled, key, className }) => {
905
+ const [, copyToClipboard] = useCopyToClipboard();
906
+ const elementKey = useMemo(() => key !== null && key !== void 0 ? key : `ActionButton-${v4()}`, [key]);
902
907
  const getIconName = () => {
903
908
  switch (type) {
904
909
  case "PHONE_NUMBER":
@@ -906,11 +911,14 @@ const ActionButton = ({ type, value, dataTestId, iconSize, disabled, className,
906
911
  case "WEB_ADDRESS":
907
912
  return "ArrowTopRightOnSquare";
908
913
  case "EMAIL":
909
- default:
910
914
  return "Envelope";
915
+ case "COPY":
916
+ default:
917
+ return "ClipboardDocument";
911
918
  }
912
919
  };
913
920
  const ButtonAction = () => {
921
+ var _a, _b;
914
922
  switch (type) {
915
923
  case "EMAIL":
916
924
  return window.open(`mailto:${value}`);
@@ -920,11 +928,17 @@ const ActionButton = ({ type, value, dataTestId, iconSize, disabled, className,
920
928
  }
921
929
  return null;
922
930
  case "PHONE_NUMBER":
923
- default:
924
931
  return window.open(`tel:${value}`);
932
+ case "COPY":
933
+ return copyToClipboard((_b = (_a = value === null || value === void 0 ? void 0 : value.current) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : "");
934
+ default:
935
+ return null;
925
936
  }
926
937
  };
927
- return (jsx("div", { className: cvaActionContainer({ className }), children: jsx(IconButton, { disabled: disabled, dataTestId: dataTestId || "testIconButtonId", size: "small", variant: "secondary", icon: jsx(Icon, { size: "small", name: getIconName() }), onClick: ButtonAction, className: cvaActionButton({ size: iconSize }) }) }));
938
+ if (ButtonAction === null) {
939
+ return null;
940
+ }
941
+ return (jsx("div", { className: cvaActionContainer({ className }), children: jsx(IconButton, { disabled: disabled, dataTestId: dataTestId || "testIconButtonId", size: "small", variant: "secondary", icon: jsx(Icon, { size: "small", name: getIconName() }), onClick: ButtonAction, className: cvaActionButton({ size: iconSize }) }) }, elementKey));
928
942
  };
929
943
 
930
944
  const cvaInputBase = cvaMerge([
@@ -1025,9 +1039,11 @@ const cvaInputSuffix = cvaMerge(["flex", "justify-center", "items-center", "text
1025
1039
  * This is a base used by our other input components such as TextInput, NumberInput, PasswordInput, etc.
1026
1040
  */
1027
1041
  const BaseInput = React.forwardRef((_a, ref) => {
1028
- var { className, isInvalid, dataTestId, prefix, suffix, addonBefore, addonAfter, actions, fieldSize = "medium", nonInteractive = false, type, inputClassName, addonBeforeClassName } = _a, rest = __rest(_a, ["className", "isInvalid", "dataTestId", "prefix", "suffix", "addonBefore", "addonAfter", "actions", "fieldSize", "nonInteractive", "type", "inputClassName", "addonBeforeClassName"]);
1042
+ var { className, isInvalid, dataTestId, prefix, suffix, addonBefore, addonAfter, actions, fieldSize = "medium", nonInteractive = false, showDefaultActions = false, type, inputClassName, addonBeforeClassName } = _a, rest = __rest(_a, ["className", "isInvalid", "dataTestId", "prefix", "suffix", "addonBefore", "addonAfter", "actions", "fieldSize", "nonInteractive", "showDefaultActions", "type", "inputClassName", "addonBeforeClassName"]);
1029
1043
  const isReadOnlyWithPlaceholder = useMemo(() => !!rest.readOnly && !!rest.placeholder && !rest.value, [rest.readOnly, rest.placeholder, rest.value]);
1030
1044
  const renderAsDisabled = rest.disabled || rest.readOnly;
1045
+ const innerRef = React.useRef(null);
1046
+ React.useImperativeHandle(ref, () => innerRef.current, []);
1031
1047
  return (jsxs("div", { className: cvaInput({
1032
1048
  disabled: renderAsDisabled,
1033
1049
  invalid: isInvalid,
@@ -1035,9 +1051,9 @@ const BaseInput = React.forwardRef((_a, ref) => {
1035
1051
  className,
1036
1052
  }), "data-testid": dataTestId && `${dataTestId}-container`, children: [addonBefore && addonBefore !== undefined ? (jsx("div", { className: cvaInputAddonBefore({ className: addonBeforeClassName }), "data-testid": dataTestId && `${dataTestId}-addonBefore`, children: addonBefore })) : null, prefix && addonBefore !== prefix ? (jsx("div", { className: cvaInputPrefix({
1037
1053
  disabled: renderAsDisabled,
1038
- }), "data-testid": dataTestId && `${dataTestId}-prefix`, children: prefix })) : null, jsx("input", Object.assign({ ref: ref, "data-testid": dataTestId, className: cvaInputField({ size: fieldSize, disabled: renderAsDisabled, className: inputClassName }), type: isReadOnlyWithPlaceholder ? "text" : type }, rest, { readOnly: rest.readOnly || nonInteractive })), suffix && addonBefore !== suffix ? (jsx("div", { className: cvaInputSuffix({
1054
+ }), "data-testid": dataTestId && `${dataTestId}-prefix`, children: prefix })) : null, jsx("input", Object.assign({ ref: innerRef, "data-testid": dataTestId, className: cvaInputField({ size: fieldSize, disabled: renderAsDisabled, className: inputClassName }), type: isReadOnlyWithPlaceholder ? "text" : type }, rest, { readOnly: rest.readOnly || nonInteractive })), suffix && addonBefore !== suffix ? (jsx("div", { className: cvaInputSuffix({
1039
1055
  disabled: renderAsDisabled,
1040
- }), "data-testid": dataTestId && `${dataTestId}-suffix`, children: suffix })) : null, actions && addonBefore !== actions ? actions : null, addonAfter && addonAfter !== undefined && addonBefore !== addonAfter ? (jsx("div", { className: cvaInputAddonAfter(), "data-testid": dataTestId && `${dataTestId}-addonAfter`, children: addonAfter })) : null] }));
1056
+ }), "data-testid": dataTestId && `${dataTestId}-suffix`, children: suffix })) : null, rest.readOnly === true && showDefaultActions ? (jsx(ActionButton, { type: "COPY", value: innerRef }, "default-copy-action")) : null, actions && addonBefore !== actions ? actions : null, addonAfter && addonAfter !== undefined && addonBefore !== addonAfter ? (jsx("div", { className: cvaInputAddonAfter(), "data-testid": dataTestId && `${dataTestId}-addonAfter`, children: addonAfter })) : null] }));
1041
1057
  });
1042
1058
 
1043
1059
  /**
@@ -1273,7 +1289,7 @@ const OptionCard = forwardRef((_a, ref) => {
1273
1289
  *
1274
1290
  * NOTE: If shown with a label, please use the `PasswordField` component instead.
1275
1291
  */
1276
- const PasswordInput = forwardRef((props, ref) => (jsx(BaseInput, Object.assign({ ref: ref, type: props.obfuscate ? "text" : "password" }, props))));
1292
+ const PasswordInput = forwardRef((props, ref) => (jsx(BaseInput, Object.assign({ ref: ref }, props))));
1277
1293
 
1278
1294
  /**
1279
1295
  * Password fields enter a password or other confidential information. Characters are masked as they are typed.
@@ -1290,7 +1306,7 @@ const PasswordField = forwardRef((_a, ref) => {
1290
1306
  const handleChange = useCallback((event) => {
1291
1307
  onChange === null || onChange === void 0 ? void 0 : onChange(event);
1292
1308
  }, [onChange]);
1293
- return (jsx(FormGroup, { htmlFor: htmlFor, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, dataTestId: dataTestId && `${dataTestId}-FormGroup`, children: jsx(PasswordInput, Object.assign({}, rest, { obfuscate: showPassword, disabled: rest.readOnly, id: htmlFor, "aria-labelledby": htmlFor + "-label", ref: ref, maxLength: maxLength, value: value, isInvalid: renderAsInvalid, className: className, onChange: handleChange, dataTestId: dataTestId, actions: jsx(Icon, { className: "absolute top-0 bottom-0 right-2 h-full content-center", name: showPassword ? "EyeSlash" : "Eye", size: "small", color: "neutral", onClick: () => setShowPassword(prevState => !prevState) }) })) }));
1309
+ return (jsx(FormGroup, { htmlFor: htmlFor, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, helpAddon: helpAddon, dataTestId: dataTestId && `${dataTestId}-FormGroup`, children: jsx(PasswordInput, Object.assign({}, rest, { type: showPassword ? "text" : "password", disabled: rest.readOnly, id: htmlFor, "aria-labelledby": htmlFor + "-label", ref: ref, maxLength: maxLength, value: value, isInvalid: renderAsInvalid, className: className, onChange: handleChange, dataTestId: dataTestId, actions: jsx(Icon, { className: "absolute top-0 bottom-0 right-2 h-full content-center", name: showPassword ? "EyeSlash" : "Eye", size: "small", color: "neutral", onClick: () => setShowPassword(prevState => !prevState) }) })) }));
1294
1310
  });
1295
1311
 
1296
1312
  /**
@@ -2084,7 +2100,7 @@ const TimeRangeField = (_a) => {
2084
2100
  var { className, dataTestId, onChange, isInvalid, errorMessage, label, tip, disabled, children, helpText, id } = _a, rest = __rest(_a, ["className", "dataTestId", "onChange", "isInvalid", "errorMessage", "label", "tip", "disabled", "children", "helpText", "id"]);
2085
2101
  const renderAsInvalid = isInvalid === undefined ? Boolean(errorMessage) : isInvalid;
2086
2102
  const htmlFor = id ? id : "timeRangeField-" + v4();
2087
- return (jsx(FormGroup, { htmlFor: htmlFor, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, dataTestId: dataTestId && `${dataTestId}-FormGroup`, disabled: disabled, children: jsx(TimeRange, Object.assign({ className: className, onChange: onChange, disabled: disabled, dataTestId: dataTestId, isInvalid: renderAsInvalid, children: children }, rest)) }));
2103
+ return (jsx(FormGroup, { htmlFor: htmlFor, label: label, tip: tip, isInvalid: renderAsInvalid, helpText: (renderAsInvalid && errorMessage) || helpText, dataTestId: dataTestId && `${dataTestId}-FormGroup`, disabled: disabled, children: jsx(TimeRange, Object.assign({ className: className, onChange: onChange, disabled: disabled, dataTestId: dataTestId, isInvalid: renderAsInvalid }, rest, { children: children })) }));
2088
2104
  };
2089
2105
 
2090
2106
  const cvaToggleWrapper = cvaMerge(["flex", "gap-2", "items-center"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trackunit/react-form-components",
3
- "version": "0.0.297-alpha-3fd0172a5e.0",
3
+ "version": "0.0.301",
4
4
  "repository": "https://github.com/Trackunit/manager",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "engines": {
@@ -11,6 +11,7 @@
11
11
  "@trackunit/ui-icons": "*",
12
12
  "react": "^18.2.0",
13
13
  "uuid": "^9.0.1",
14
+ "react-use": "^17.4.0",
14
15
  "date-fns": "^2.30.0",
15
16
  "react-select": "^5.8.0",
16
17
  "libphonenumber-js": "1.10.36",
@@ -1,26 +1,51 @@
1
1
  /// <reference types="react" />
2
2
  import { ButtonCommonProps, CommonProps } from "@trackunit/react-components";
3
- type ActionType = "PHONE_NUMBER" | "WEB_ADDRESS" | "EMAIL";
3
+ type ActionType = "PHONE_NUMBER" | "WEB_ADDRESS" | "EMAIL" | "COPY";
4
4
  type FieldSize = "small" | "medium";
5
- interface ActionButtonProps extends ButtonCommonProps, CommonProps {
5
+ interface AbstractActionButtonProps extends ButtonCommonProps, CommonProps {
6
6
  /**
7
7
  * The type of action performed when clicking the button.
8
8
  */
9
9
  type: ActionType;
10
+ /**
11
+ * A key to be passed into the button.
12
+ */
13
+ key?: string;
10
14
  /**
11
15
  * The value to be passed into the button.
12
16
  */
13
- value?: string | null;
17
+ value?: string | React.RefObject<HTMLInputElement> | null;
14
18
  /**
15
19
  * The size of the icon.
16
20
  */
17
21
  iconSize?: FieldSize;
18
22
  }
23
+ interface GenericActionButtonProps extends AbstractActionButtonProps {
24
+ /**
25
+ * The type of action performed when clicking the button.
26
+ */
27
+ type: "PHONE_NUMBER" | "WEB_ADDRESS" | "EMAIL";
28
+ /**
29
+ * The value to be passed into the button.
30
+ */
31
+ value?: string | null;
32
+ }
33
+ interface CopyActionButtonProps extends AbstractActionButtonProps {
34
+ /**
35
+ * The type of action performed when clicking the button.
36
+ */
37
+ type: "COPY";
38
+ /**
39
+ * The value to be passed into the button.
40
+ */
41
+ value?: React.RefObject<HTMLInputElement>;
42
+ }
43
+ type ActionButtonProps = CopyActionButtonProps | GenericActionButtonProps;
19
44
  /**
20
45
  * The ActionButton component is a wrapper over IconButton to perform an action when the onClick event is triggered.
21
46
  *
22
47
  * @param {ActionButtonProps} props - The props for the ActionButton component
23
48
  * @returns {JSX.Element} ActionButton component
24
49
  */
25
- export declare const ActionButton: ({ type, value, dataTestId, iconSize, disabled, className, }: ActionButtonProps) => JSX.Element;
50
+ export declare const ActionButton: ({ type, value, dataTestId, iconSize, disabled, key, className }: ActionButtonProps) => JSX.Element | null;
26
51
  export {};
@@ -57,6 +57,14 @@ export interface BaseInputProps extends FilteredInputProps, CommonProps {
57
57
  * Custom css classes for the addon before element.
58
58
  */
59
59
  addonBeforeClassName?: string;
60
+ /**
61
+ * If true the default actions are shown.
62
+ */
63
+ showDefaultActions?: boolean;
64
+ /**
65
+ * If true the input is readonly.
66
+ */
67
+ readonly?: boolean;
60
68
  }
61
69
  /**
62
70
  * A base input component that can be used for text inputs, password inputs, etc.
@@ -2,10 +2,7 @@
2
2
  import { BaseInputProps } from "../BaseInput/BaseInput";
3
3
  type BaseInputExposedProps = Omit<BaseInputProps, "type">;
4
4
  export interface PasswordInputProps extends BaseInputExposedProps {
5
- /**
6
- * If false the field will show password as plain text.
7
- */
8
- obfuscate?: boolean;
5
+ type?: "password" | "text";
9
6
  }
10
7
  /**
11
8
  * A thin wrapper around the `BaseInput` component for password input fields.
@@ -9,6 +9,6 @@ import { GroupBase, StylesConfig } from "react-select";
9
9
  * @param {StylesConfig<Option, IsMulti, Group> | undefined} styles a optional object to override styles of react-select
10
10
  * @returns {StylesConfig<Option, boolean>} styles to override in select
11
11
  */
12
- export declare const useCustomStyles: <Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>>(refContainer: React.RefObject<HTMLDivElement>, refPrefix: React.RefObject<HTMLDivElement>, maxSelectedDisplayCount: number | undefined, styles: StylesConfig<Option, IsMulti, Group> | undefined) => {
12
+ export declare const useCustomStyles: <Option, IsMulti extends boolean = false, Group extends GroupBase<Option> = GroupBase<Option>>(refContainer: React.RefObject<HTMLDivElement>, refPrefix: React.RefObject<HTMLDivElement>, maxSelectedDisplayCount: number | undefined, styles: StylesConfig<Option, IsMulti, Group> | undefined, disabled: boolean | undefined) => {
13
13
  customStyles: StylesConfig<Option, IsMulti, Group>;
14
14
  };
@@ -15,8 +15,8 @@ export declare const translations: TranslationResource<TranslationKeys>;
15
15
  /**
16
16
  * Local useTranslation for this specific library
17
17
  */
18
- export declare const useTranslation: () => [TransForLibs<"phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined">, import("i18next").i18n, boolean] & {
19
- t: TransForLibs<"phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined">;
18
+ export declare const useTranslation: () => [TransForLibs<"clearIndicator.icon.tooltip.clearAll" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined">, import("i18next").i18n, boolean] & {
19
+ t: TransForLibs<"clearIndicator.icon.tooltip.clearAll" | "phoneField.error.INVALID_COUNTRY" | "phoneField.error.INVALID_LENGTH" | "phoneField.error.INVALID_NUMBER" | "phoneField.error.NOT_A_NUMBER" | "phoneField.error.REQUIRED" | "phoneField.error.REQUIRED_COUNTRY" | "phoneField.error.TOO_LONG" | "phoneField.error.TOO_SHORT" | "phoneField.error.undefined">;
20
20
  i18n: import("i18next").i18n;
21
21
  ready: boolean;
22
22
  };