@capgeminiuk/dcx-react-library
Version:
[](https://circleci.com/gh/Capgemini/dcx-react-library)
1,559 lines (1,528 loc) • 112 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('@cesium133/forgjs'), require('imask')) :
typeof define === 'function' && define.amd ? define(['exports', 'react', '@cesium133/forgjs', 'imask'], factory) :
(global = global || self, factory(global.dcxReactLibrary = {}, global.react, global.forgjs, global.imask));
})(this, (function (exports, React, forgjs, IMask) {
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
var IMask__default = /*#__PURE__*/_interopDefaultLegacy(IMask);
/**
* Check if is valid or not
* @param validation
* @param value
*/
const isValid = (validation, value) => {
const floatRule = new forgjs.Rule(validation.rule, validation.message);
let rule = value;
if (validation.rule.type === 'float' || validation.rule.type === 'int') {
rule = parseFloat(value);
}
return {
valid: floatRule.test(rule),
floatRule
};
};
/**
* This custom hooks accept in input a validation rule and provide in
* output the validity or not of the element.
* For a list of Validation rules have a look on: https://github.com/oussamahamdaoui/forgJs
* @Example
* const {validity, onValueChange} = useValidationOnChange({
* rule: {
* type: 'float',
* min: 100,
* },
* message: 'the value have to be float and more then 100',
* })
* return (<input onChange={onValueChange}/>
* @param validation
*/
const useValidationOnChange = (validation, value = '') => {
let checkValidation = {
valid: true,
floatRule: {
error: ''
}
};
if (validation) checkValidation = isValid(validation, value);
const [validity, setValid] = React__default["default"].useState({
valid: checkValidation.valid,
message: checkValidation.floatRule.error
});
if (validation === null) return {
validity: null,
onValueChange: null
};
const onValueChange = evt => {
const {
valid,
floatRule
} = isValid(validation, evt.currentTarget.value);
setValid({
valid,
message: floatRule.error
});
};
return {
validity,
onValueChange
};
};
/**
* It will check a valid date given day.month and year
* @param day
* @param month
* @param year
* @returns
*/
function validateDateString(day, month, year) {
day = Number(day);
month = Number(month) - 1; //bloody 0-indexed month
year = Number(year);
let d = new Date(year, month, day);
let yearMatches = d.getUTCFullYear() === year;
let monthMatches = d.getUTCMonth() === month;
let dayMatches = d.getDate() === day;
return yearMatches && monthMatches && dayMatches;
}
const Roles = {
alert: 'alert',
button: 'button',
combobox: 'combobox',
error: 'error',
formInput: 'form-input',
formRadio: 'radio',
formCheckbox: 'checkbox',
formGroup: 'form-group',
listbox: 'listbox',
listItem: 'listitem',
list: 'list',
prefix: 'prefix',
presentation: 'presentation',
progress: 'progress',
region: 'region',
slider: 'slider',
suffix: 'suffix',
tab: 'tab',
tablist: 'tablist',
tabpanel: 'tabpanel',
textbox: 'textbox',
toggle: 'form-toggle'
};
function subscribe() {
return () => {};
}
/**
* Return a boolean indicating if the JS has been hydrated already.
* When doing Server-Side Rendering, the result will always be false.
* When doing Client-Side Rendering, the result will always be false on the
* first render and true from then on. Even if a new component renders it will
* always start with true.
*
* Example: Disable a button that needs JS to work.
* ```tsx
* let hydrated = useHydrated();
* return (
* <button type="button" disabled={!hydrated} onClick={doSomethingCustom}>
* Click me
* </button>
* );
* ```
*/
function useHydrated() {
return React.useSyncExternalStore(subscribe, () => true, () => false);
}
const classNames = classes => {
let result = '';
classes.forEach(c => {
if (c !== null && typeof c === 'object') {
for (const v in c) {
if (c[v] === true) {
result = result.concat(classNames(v.split(' '))).concat(' ');
}
}
} else {
if (c !== 'undefined' && c !== undefined && c !== 'null' && c !== null && typeof c === 'string') result = result.concat(c).concat(' ');
}
});
return result.slice(0, -1).trim();
};
const debounce = (func, time) => {
let timer;
const context = undefined;
return function () {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
func.apply(context, [].slice.call(arguments));
}, time);
};
};
const isEmpty = value => value === (null) || typeof value === 'object' && Object.keys(value).length === 0 || typeof value === 'string' && value.trim().length === 0;
const upperFirst = string => string.charAt(0).toUpperCase() + string.slice(1);
const omit = (originalObj, keysToOmit) => Object.fromEntries(Object.entries(originalObj).filter(([key]) => !keysToOmit.includes(key)));
const ErrorMessage = ({
text,
className,
id,
visuallyHiddenText
}) => {
const getErrorElement = () => {
let errorElement = /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null);
if (text && typeof text === 'string') {
errorElement = /*#__PURE__*/React__default["default"].createElement("span", {
id: id,
className: classNames(['dcx-error-message', className])
}, visuallyHiddenText && ( /*#__PURE__*/React__default["default"].createElement("span", {
className: visuallyHiddenText.className
}, visuallyHiddenText.text)), text);
}
return errorElement;
};
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, getErrorElement());
};
const Hint = ({
text,
className,
id,
useLabel
}) => useLabel ? ( /*#__PURE__*/React__default["default"].createElement("label", {
id: id,
className: classNames(['dcx-hint', className])
}, text)) : ( /*#__PURE__*/React__default["default"].createElement("div", {
id: id,
className: classNames(['dcx-hint', className])
}, text));
const Conditional = ({
name,
label,
type,
className,
groupClassName,
id,
inputClassName,
inputId,
labelClassName,
value,
onChange
}) => {
const onChangeHandler = event => {
if (onChange) onChange(event);
};
const containerClasses = classNames([className, 'dcx-conditional-input']);
return /*#__PURE__*/React__default["default"].createElement("div", {
className: containerClasses,
id: id
}, /*#__PURE__*/React__default["default"].createElement("div", {
className: groupClassName
}, /*#__PURE__*/React__default["default"].createElement("label", {
className: labelClassName,
htmlFor: inputId
}, label), /*#__PURE__*/React__default["default"].createElement("input", {
className: inputClassName,
id: inputId,
name: name,
type: type,
value: value,
onChange: onChangeHandler
})));
};
const LOWEST_HEADING = 6;
const HIGHEST_HEADING = 1;
const Legend = ({
text,
className,
heading
}) => ( /*#__PURE__*/React__default["default"].createElement("legend", {
className: className
}, heading ? /*#__PURE__*/React__default["default"].createElement(`h${heading && heading.priority >= HIGHEST_HEADING && heading.priority <= LOWEST_HEADING ? heading.priority : 1}`, {
className: heading.className
}, text) : text));
const Option = ({
label,
value,
className,
disabled,
id,
ariaLabel
}) => ( /*#__PURE__*/React__default["default"].createElement("option", {
value: value,
id: id,
className: className,
disabled: disabled,
"aria-label": ariaLabel || label
}, label));
const OptionGroup = ({
label,
displayCount,
options
}) => ( /*#__PURE__*/React__default["default"].createElement("optgroup", {
label: displayCount ? `${label} (${options.length})` : label
}, options.map((item, index) => ( /*#__PURE__*/React__default["default"].createElement(Option, {
key: index,
...item
})))));
const CheckboxRadioBase = ({
label,
value,
type,
role,
id,
ariaLabel,
ariaDataControls,
ariaDescribedBy,
ariaLabelledBy,
disabled,
conditional,
hint,
inputProps,
itemProps,
labelProps,
name,
nested,
selected,
onChange,
inputClassName,
labelClassName,
itemClassName
}) => {
let hydrated = useHydrated();
const conditionalReveal = () => !isEmpty(conditional) && selected === true;
const [conditionalValue, setConditionalValue] = React.useState(conditional ? conditional.value : '');
const onChangeHandler = event => {
if (onChange) onChange(event);
};
const input = /*#__PURE__*/React__default["default"].createElement("input", {
id: id,
type: type,
role: role,
value: value,
name: name,
"aria-label": ariaLabel,
"data-aria-controls": ariaDataControls,
"aria-describedby": ariaDescribedBy,
"aria-labelledby": ariaLabelledBy,
disabled: disabled,
checked: selected,
...inputProps,
className: inputClassName,
onChange: onChangeHandler
});
const el = nested ? ( /*#__PURE__*/React__default["default"].createElement("label", {
...labelProps,
className: labelClassName
}, input, label)) : ( /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, input, /*#__PURE__*/React__default["default"].createElement("label", {
...labelProps,
htmlFor: id,
className: labelClassName
}, label)));
const getConditionalElement = () => {
let hydratedElm = /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null);
if (!hydrated && conditional !== undefined) {
hydratedElm = Conditional({
...conditional,
value: conditionalValue
});
} else if (conditional !== undefined && conditionalReveal()) {
hydratedElm = Conditional({
...conditional,
value: conditionalValue,
onChange: event => {
setConditionalValue(event.currentTarget.value);
if (onChange) onChange(event, event.currentTarget.value);
}
});
}
return hydratedElm;
};
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("div", {
...itemProps,
className: itemClassName
}, hint && hint.position === 'above' && /*#__PURE__*/React__default["default"].createElement(Hint, {
...hint
}), el, hint && hint.position !== 'above' && /*#__PURE__*/React__default["default"].createElement(Hint, {
...hint
}), conditional && !conditional.position && getConditionalElement()), conditional && conditional.position === 'sibling' && getConditionalElement());
};
const Label$1 = ({
label,
labelProperties,
htmlFor,
className
}) => label ? ( /*#__PURE__*/React__default["default"].createElement("label", {
...labelProperties,
htmlFor: htmlFor,
className: className
}, label)) : null;
const FormCheckbox = ({
id,
name,
value,
label,
inputProps,
itemProps,
labelProps,
ariaLabel,
ariaDataControls = '',
ariaDescribedBy = '',
ariaLabelledBy,
onChange,
conditional,
disabled,
selected,
hint,
nested,
inputClassName,
labelClassName,
itemClassName,
isError
}) => {
const containerClasses = classNames([itemClassName, 'dcx-checkbox', {
'dcx-checkbox--error': isError
}]);
return /*#__PURE__*/React__default["default"].createElement(CheckboxRadioBase, {
type: "checkbox",
id: id,
role: Roles.formCheckbox,
name: name,
value: value,
label: label,
inputProps: inputProps,
itemProps: itemProps,
labelProps: labelProps,
ariaLabel: ariaLabel,
ariaDataControls: ariaDataControls,
ariaDescribedBy: ariaDescribedBy,
ariaLabelledBy: ariaLabelledBy || labelProps?.id,
onChange: onChange,
conditional: conditional,
disabled: disabled,
selected: selected,
hint: hint,
nested: nested,
itemClassName: containerClasses,
inputClassName: inputClassName,
labelClassName: labelClassName
});
};
const isDivider = item => typeof item !== 'string' && item.label === undefined;
const isString = item => typeof item === 'string';
const FormRadioCheckboxBase = ({
name,
type,
items,
ariaDescribedBy,
groupClasses,
error,
fieldsetClasses,
hint,
id,
inputProps,
itemProps,
itemsClasses,
inputClassName,
itemClassName,
labelClassName,
labelProps,
legend,
onChange
}) => {
const findSelection = items => {
let newSelection = {};
items.forEach(item => {
if (item.selected) {
newSelection = {
...newSelection,
[item.id]: true
};
} else {
newSelection = {
...newSelection,
[item.id]: false
};
}
});
return newSelection;
};
const [selection, setSelection] = React.useState(findSelection(items));
const handleChange = (item, e) => {
if (type === 'radio') {
let newSelection = {};
items.forEach(item => {
newSelection[item.id] = item.id === e.currentTarget.id ? e.currentTarget.checked : false;
});
setSelection(newSelection);
} else {
setSelection({
...selection,
[isString(item) ? item : item.id]: e.currentTarget.checked
});
}
if (onChange) {
onChange(e);
}
};
const isSelected = item => Object.keys(selection).some(key => key === item.id && selection[key] === true);
const formGroupItems = items.map((item, index) => {
if (isDivider(item)) {
return /*#__PURE__*/React__default["default"].createElement("div", {
key: `${id}_${index.toString()}`,
id: item.id,
className: item.className
}, item.text);
}
if (isString(item)) {
return /*#__PURE__*/React__default["default"].createElement(CheckboxRadioBase, {
id: item,
type: type,
key: `${id}_${index.toString()}`,
name: name,
inputProps: {
...inputProps
},
itemProps: {
...itemProps
},
labelProps: {
...labelProps
},
inputClassName: inputClassName,
labelClassName: labelClassName,
itemClassName: itemClassName,
label: item,
value: item,
onChange: event => {
handleChange(item, event);
}
});
}
if (type === 'radio') {
return /*#__PURE__*/React__default["default"].createElement(CheckboxRadioBase, {
type: type,
key: `${id}_${index.toString()}`,
...item,
name: name,
inputProps: {
...inputProps,
...item.inputProps
},
itemProps: {
...itemProps,
...item.itemProps
},
labelProps: {
...labelProps,
...item.labelProps
},
inputClassName: inputClassName,
labelClassName: labelClassName,
itemClassName: itemClassName,
selected: isSelected(item),
onChange: (event, conditionalInput) => {
if (conditionalInput && onChange) {
onChange(event, conditionalInput);
return;
}
handleChange(item, event);
}
});
}
if (type === 'checkbox') {
return /*#__PURE__*/React__default["default"].createElement(FormCheckbox, {
key: `${id}_${index.toString()}`,
...item,
name: name,
inputProps: {
...inputProps,
...item.inputProps
},
itemProps: {
...itemProps,
...item.itemProps
},
labelProps: {
...labelProps,
...item.labelProps
},
inputClassName: inputClassName,
labelClassName: labelClassName,
itemClassName: itemClassName,
selected: isSelected(item),
onChange: (event, conditionalInput) => {
if (conditionalInput && onChange) {
onChange(event, conditionalInput);
return;
}
handleChange(item, event);
}
});
}
return null;
});
return items && items.length > 1 ? ( /*#__PURE__*/React__default["default"].createElement("div", {
id: id,
className: groupClasses
}, /*#__PURE__*/React__default["default"].createElement("fieldset", {
className: fieldsetClasses,
"aria-describedby": ariaDescribedBy || ''
}, legend && /*#__PURE__*/React__default["default"].createElement(Legend, {
...legend
}), hint && /*#__PURE__*/React__default["default"].createElement(Hint, {
...hint
}), error && /*#__PURE__*/React__default["default"].createElement(ErrorMessage, {
...error
}), /*#__PURE__*/React__default["default"].createElement("div", {
className: itemsClasses
}, formGroupItems)))) : ( /*#__PURE__*/React__default["default"].createElement("div", null, "Can not render a ", type, " group with less than 2 items"));
};
const CheckboxGroup = ({
name,
items,
ariaDescribedBy,
groupClasses,
error,
fieldsetClasses,
hint,
id,
inputProps,
itemProps,
itemsClasses,
inputClassName,
itemClassName,
labelClassName,
labelProps,
legend,
onChange
}) => {
const containerClasses = classNames([groupClasses, {
'dcx-checkbox-group': true
}, {
'dcx-checkbox-group--error': !!error
}]);
const containerCheckboxesClasses = classNames([itemsClasses, 'dcx-checkbox-group-checkboxes']);
return /*#__PURE__*/React__default["default"].createElement(FormRadioCheckboxBase, {
type: 'checkbox',
name,
items,
ariaDescribedBy,
groupClasses: containerClasses,
error,
fieldsetClasses,
hint,
id,
inputProps,
itemProps,
itemsClasses: containerCheckboxesClasses,
inputClassName,
itemClassName,
labelClassName,
labelProps,
legend,
onChange
});
};
const RadioGroup = ({
name,
items,
ariaDescribedBy,
groupClasses,
error,
fieldsetClasses,
hint,
id,
inputProps,
itemProps,
itemsClasses,
inputClassName,
itemClassName,
labelClassName,
labelProps,
legend,
onChange
}) => {
const containerClasses = classNames([groupClasses, {
'dcx-radio-button-group': true
}, {
'dcx-radio-button-group--error': !!error
}]);
const containerRadioButtonsClasses = classNames([itemsClasses, 'dcx-radio-button-group-radios']);
return /*#__PURE__*/React__default["default"].createElement(FormRadioCheckboxBase, {
type: 'radio',
name,
items,
ariaDescribedBy,
groupClasses: containerClasses,
error,
fieldsetClasses,
hint: hint,
id,
inputProps,
itemProps,
itemsClasses: containerRadioButtonsClasses,
inputClassName,
itemClassName,
labelClassName,
labelProps,
legend,
onChange
});
};
const floatVariants = ['floating', 'floating-filled'];
exports.ErrorPosition = void 0;
(function (ErrorPosition) {
ErrorPosition["BEFORE_LABEL"] = "before-label";
ErrorPosition["BOTTOM"] = "bottom";
ErrorPosition["AFTER_LABEL"] = "after-label";
ErrorPosition["AFTER_HINT"] = "after-hint";
})(exports.ErrorPosition || (exports.ErrorPosition = {}));
const FormInput = ({
name,
type,
value,
label,
validation = null,
inputProps,
labelProps,
errorProps,
prefix,
suffix,
onChange,
onFocus,
onBlur,
isValid,
staticErrorMessage,
errorPosition,
ariaLabel,
ariaRequired,
displayError = false,
inputClassName,
containerClassName,
containerClassNameError,
labelClassName,
required,
hint,
variant = 'normal',
inputDivProps = {
style: {
display: 'flex'
}
},
tabIndex,
hiddenErrorText = '',
hiddenErrorTextProps
}) => {
const {
validity,
onValueChange
} = useValidationOnChange(validation, value);
const [showError, setShowError] = React__default["default"].useState(displayError);
React__default["default"].useEffect(() => {
if (isValid && validity) isValid(validity.valid, showError && !validity.valid);
// eslint-disable-next-line
}, [validity?.valid, showError]);
React__default["default"].useEffect(() => {
setShowError(displayError);
}, [displayError]);
const handleChange = event => {
setShowError(true);
if (onValueChange) onValueChange(event);
if (onChange) onChange(event);
};
const handleFocus = event => {
if (onFocus) onFocus(event);
};
const handleBlur = event => {
if (onBlur) onBlur(event);
};
const isStaticMessageValid = () => typeof staticErrorMessage === 'string' && !isEmpty(staticErrorMessage);
const ErrorMessage = () => {
if (isStaticMessageValid()) {
return /*#__PURE__*/React__default["default"].createElement("p", {
...errorProps,
className: classNames(['dcx-error-message', errorProps?.className]),
role: Roles.alert
}, !isEmpty(hiddenErrorText) && ( /*#__PURE__*/React__default["default"].createElement("span", {
...hiddenErrorTextProps
}, hiddenErrorText + ' ')), staticErrorMessage);
}
if (validity && !validity.valid && showError) {
return /*#__PURE__*/React__default["default"].createElement("p", {
...errorProps,
className: classNames(['dcx-error-message', errorProps?.className]),
role: Roles.alert
}, !isEmpty(hiddenErrorText) && ( /*#__PURE__*/React__default["default"].createElement("span", {
...hiddenErrorTextProps
}, hiddenErrorText + ' ')), validity.message);
}
return null;
};
const isStaticOrDynamicError = () => isStaticMessageValid() || validity && !validity.valid || false;
const inputEl = /*#__PURE__*/React__default["default"].createElement("input", {
name: name,
type: type,
value: value,
onChange: handleChange,
onFocus: handleFocus,
onBlur: handleBlur,
required: required,
className: inputClassName,
"aria-label": ariaLabel,
"aria-required": ariaRequired,
tabIndex: tabIndex,
...inputProps
});
const labelEl = /*#__PURE__*/React__default["default"].createElement(Label$1, {
label: label,
labelProperties: labelProps,
htmlFor: inputProps?.id,
className: labelClassName
});
const containerClasses = classNames(['dcx-form-input', containerClassName, {
'dcx-form-input--filled': !!value,
'dcx-form-input--placeholder': !!inputProps?.placeholder,
'dcx-error-bottom': errorPosition === exports.ErrorPosition.BOTTOM,
'dcx-hint-bottom': hint && hint.position !== 'above',
'dcx-floating-label': floatVariants.includes(variant),
'dcx-floating-label-filled': variant === 'floating-filled',
[`dcx-form-input--error ${containerClassNameError}`]: isStaticOrDynamicError()
}]);
return /*#__PURE__*/React__default["default"].createElement("div", {
className: containerClasses
}, errorPosition && errorPosition === exports.ErrorPosition.BEFORE_LABEL && ( /*#__PURE__*/React__default["default"].createElement(ErrorMessage, null)), !floatVariants.includes(variant) && labelEl, errorPosition && errorPosition === exports.ErrorPosition.AFTER_LABEL && ( /*#__PURE__*/React__default["default"].createElement(ErrorMessage, null)), hint && hint.position === 'above' && /*#__PURE__*/React__default["default"].createElement(Hint, {
...hint
}), errorPosition && errorPosition === exports.ErrorPosition.AFTER_HINT && ( /*#__PURE__*/React__default["default"].createElement(ErrorMessage, null)), prefix || suffix ? ( /*#__PURE__*/React__default["default"].createElement("div", {
...inputDivProps
}, prefix && !isEmpty(prefix) && ( /*#__PURE__*/React__default["default"].createElement("div", {
...prefix.properties,
className: classNames(['dcx-form-input__prefix', prefix.properties.className])
}, prefix.content)), floatVariants.includes(variant) ? ( /*#__PURE__*/React__default["default"].createElement("div", {
className: "dcx-wrapper-label"
}, labelEl, inputEl)) : inputEl, suffix && !isEmpty(suffix) && ( /*#__PURE__*/React__default["default"].createElement("div", {
...suffix.properties,
className: classNames(['dcx-form-input__suffix', suffix.properties.className])
}, suffix.content)))) : floatVariants.includes(variant) ? ( /*#__PURE__*/React__default["default"].createElement("div", {
className: "dcx-wrapper-label"
}, labelEl, inputEl)) : inputEl, hint && hint.position !== 'above' && /*#__PURE__*/React__default["default"].createElement(Hint, {
...hint
}), errorPosition && errorPosition === exports.ErrorPosition.BOTTOM && ( /*#__PURE__*/React__default["default"].createElement(ErrorMessage, null)));
};
const FormInputMasked = ({
options,
onChange,
value,
name,
type,
ariaLabel,
...props
}) => {
const inputRef = React.useRef(null);
const [mask, setMask] = React.useState(null);
React.useEffect(() => {
if (mask && value) {
mask.value = value;
}
}, [mask, value]);
React.useEffect(() => {
if (inputRef.current && !mask) {
setMask(IMask__default["default"](inputRef.current, {
...options
}));
}
}, [mask, options]);
React.useEffect(() => {
if (inputRef.current && mask && onChange) {
const {
current
} = inputRef;
mask.on('accept', () => {
onChange({
value: current.value,
unmaskedValue: mask.unmaskedValue
});
});
}
}, [mask, onChange]);
return /*#__PURE__*/React__default["default"].createElement("input", {
ref: inputRef,
...props,
name: name,
type: type,
"aria-label": ariaLabel
});
};
/**
* @deprecated since release 0.6
*/
const FormRadio = ({
label,
value,
id,
ariaLabel,
ariaDataControls,
ariaDescribedBy,
ariaLabelledBy,
disabled,
conditional,
hint,
inputProps,
itemProps,
labelProps,
name,
nested,
selected,
onChange,
inputClassName,
labelClassName,
itemClassName
}) => ( /*#__PURE__*/React__default["default"].createElement(CheckboxRadioBase, {
type: "radio",
id: id,
role: Roles.formRadio,
name: name,
value: value,
label: label,
inputProps: inputProps,
itemProps: itemProps,
labelProps: labelProps,
ariaLabel: ariaLabel,
ariaDataControls: ariaDataControls,
ariaDescribedBy: ariaDescribedBy,
ariaLabelledBy: ariaLabelledBy || labelProps?.id,
onChange: onChange,
conditional: conditional,
disabled: disabled,
selected: selected,
hint: hint,
nested: nested,
inputClassName: inputClassName,
labelClassName: labelClassName,
itemClassName: itemClassName
}));
const FormSelect = ({
selectClassName,
labelClassName,
containerClassName,
containerErrorClassName,
containerFilledClassName,
name,
optionGroups,
options = [],
onChange,
value,
id,
ariaLabel,
label,
labelProps,
hint,
error,
errorMessage,
errorMessageClassName,
errorMessageVisuallyHidden,
errorMessageId,
style,
nullOption,
containerProps,
defaultValue,
tabIndex,
variant = 'normal',
disabled = false,
selectProps
}) => {
let initialValue = '';
if (defaultValue !== undefined) {
initialValue = defaultValue;
} else if (value !== undefined) {
initialValue = value;
} else if (nullOption !== undefined) {
initialValue = nullOption;
} else if (options.length > 0 && typeof options[0] === 'string') {
initialValue = options[0];
} else if (options.length > 0) {
initialValue = options[0].value;
}
const [selectValue, setSelectValue] = React.useState(initialValue);
const getOptions = options => options.map((item, index) => {
let convertedItem;
// in case item is a string we need to convert it to an option
if (typeof item === 'string') {
convertedItem = {
label: item,
value: item
};
} else {
convertedItem = {
...item
};
}
return /*#__PURE__*/React__default["default"].createElement(Option, {
key: index,
...convertedItem
});
});
const getOptionGroups = optionGroups => optionGroups.map((groupOption, index) => ( /*#__PURE__*/React__default["default"].createElement(OptionGroup, {
key: index,
...groupOption
})));
const handleChange = event => {
setSelectValue(event.currentTarget.value);
if (onChange) onChange(event);
};
const containerClasses = classNames(['dcx-formselect', containerClassName, {
[`dcx-formselect--error ${containerErrorClassName}`]: errorMessage !== undefined,
'dcx-floating-label': variant === 'floating',
[`dcx-formselect--filled ${containerFilledClassName}`]: selectValue && selectValue !== nullOption
}]);
return /*#__PURE__*/React__default["default"].createElement("div", {
className: containerClasses,
...containerProps
}, /*#__PURE__*/React__default["default"].createElement(Label$1, {
label: label,
labelProperties: labelProps,
htmlFor: id,
className: labelClassName
}), hint && /*#__PURE__*/React__default["default"].createElement(Hint, {
...hint
}), error && /*#__PURE__*/React__default["default"].createElement(ErrorMessage, {
...error
}), errorMessage && ( /*#__PURE__*/React__default["default"].createElement(ErrorMessage, {
text: errorMessage,
className: errorMessageClassName,
visuallyHiddenText: errorMessageVisuallyHidden,
id: errorMessageId
})), /*#__PURE__*/React__default["default"].createElement("select", {
value: selectValue,
name: name || 'formSelect',
id: id || 'formSelect',
className: selectClassName,
"aria-label": ariaLabel,
onChange: handleChange,
style: style,
tabIndex: tabIndex,
disabled: disabled,
...selectProps
}, nullOption && /*#__PURE__*/React__default["default"].createElement(Option, {
value: "",
label: nullOption
}), getOptions(options), optionGroups && getOptionGroups(optionGroups)));
};
const ResultList = ({
resultLiRef,
list,
listId,
userInput,
activeOption,
ulContainerId,
ulContainerStyle,
ulContainerClass,
liContainerClass,
liContainerStyle,
activeClass,
noOptionClass,
noElFoundText,
onClick,
ariaLabeledBy
}) => ( /*#__PURE__*/React__default["default"].createElement("ul", {
id: ulContainerId,
className: ulContainerClass,
style: ulContainerStyle,
"aria-labelledby": ariaLabeledBy,
role: "listbox"
}, userInput && list.length > 0 ? list.map((optionName, index) => ( /*#__PURE__*/React__default["default"].createElement("li", {
id: listId ? `${listId}--${index + 1}` : undefined,
className: classNames([liContainerClass, {
[`${activeClass}`]: index === activeOption,
[`${liContainerClass}--even`]: index !== activeOption && index % 2 === 0,
[`${liContainerClass}--odd`]: index !== activeOption && index % 2 !== 0
}]),
key: optionName,
onClick: onClick,
style: liContainerStyle,
ref: ref => {
resultLiRef.current = {
...resultLiRef.current,
[index]: ref
};
},
role: "option",
"aria-selected": index === activeOption,
"aria-setsize": list.length,
"aria-posinset": index + 1,
tabIndex: -1
}, optionName))) : noElFoundText && ( /*#__PURE__*/React__default["default"].createElement("li", {
className: classNames([liContainerClass, noOptionClass])
}, noElFoundText))));
const SelectedItem = ({
label,
ariaLabel,
className,
role,
style,
tabIndex,
onClick,
onFocus,
onKeyDown
}) => ( /*#__PURE__*/React__default["default"].createElement("span", {
"aria-label": ariaLabel || label,
role: role || Roles.presentation,
className: className,
onClick: onClick,
onFocus: onFocus,
onKeyDown: onKeyDown,
tabIndex: tabIndex,
style: style
}, label));
const ENTER_KEY = 13;
const Selected = ({
select,
ariaLabel,
className,
labelClassName,
removeButtonClassName,
style,
onFocus,
onRemove
}) => {
const handleClick = () => onRemove && onRemove(select);
const handleKeyDown = event => {
if (parseInt(event.code) === ENTER_KEY) {
onRemove && onRemove(select);
}
};
return /*#__PURE__*/React__default["default"].createElement("div", {
id: select.id,
className: className,
role: Roles.listItem,
style: style
}, /*#__PURE__*/React__default["default"].createElement(SelectedItem, {
className: labelClassName,
label: select.label
}), /*#__PURE__*/React__default["default"].createElement(SelectedItem, {
className: removeButtonClassName,
label: "x",
role: "button",
ariaLabel: ariaLabel || `Remove ${select.label}`,
onClick: handleClick,
onFocus: onFocus,
onKeyDown: handleKeyDown,
style: {
marginLeft: '5px',
fontWeight: 'bold'
},
tabIndex: 0
}));
};
exports.AutoCompleteErrorPosition = void 0;
(function (AutoCompleteErrorPosition) {
AutoCompleteErrorPosition["BEFORE_LABEL"] = "before-label";
AutoCompleteErrorPosition["BOTTOM"] = "bottom";
AutoCompleteErrorPosition["AFTER_LABEL"] = "after-label";
AutoCompleteErrorPosition["AFTER_HINT"] = "after-hint";
})(exports.AutoCompleteErrorPosition || (exports.AutoCompleteErrorPosition = {}));
const Autocomplete = ({
options,
optionsId,
minCharsBeforeSearch = 1,
minCharsMessage = '',
promptCondition = () => false,
promptMessage = '',
promptId = 'input-prompt',
promptClassName,
debounceMs = 0,
inputProps,
defaultValue = '',
hintText,
hintClass,
hintId,
multiSelect = false,
notFoundText,
resultId,
resultActiveClass,
resultUlClass,
resultUlStyle,
resultlLiClass,
resultLiStyle,
resultNoOptionClass,
removeAllButtonClass,
searchContainerStyle,
selectedListItemStyle,
selected,
onSelected,
onChange,
onRemove,
onRemoveAll,
onFocus,
required = false,
containerClassName,
labelText,
labelClassName,
labelProps,
id,
errorPosition,
errorMessageText = '',
errorMessageClassName,
errorId,
errorVisuallyHiddenText,
name,
selectProps,
prefix,
suffix,
tabIndex,
search,
accessibilityStatus = '',
accessibilityHintText = '',
statusUpdate,
customNonJSComp = undefined
}) => {
const [activeOption, setActiveOption] = React.useState(0);
const [filterList, setFilterList] = React.useState([]);
const [showOptions, setShowOptions] = React.useState(false);
const [showPrompt, setShowPrompt] = React.useState(false);
const [userInput, setUserInput] = React.useState(defaultValue);
const [currentAutocompleteStatus, setCurrentAutocompleteStatus] = React.useState(true);
const resultRef = React.useRef(null);
const [accessibilityStatusA, setAccessibilityStatusA] = React.useState('');
const [accessibilityStatusB, setAccessibilityStatusB] = React.useState('');
let hydrated = useHydrated();
const displayComp = () => {
if (hydrated) {
return formInput;
} else {
if (customNonJSComp !== undefined) {
return customNonJSComp;
} else {
if (multiSelect) {
return /*#__PURE__*/React__default["default"].createElement(FormSelect, {
name: "multiSelect",
options: options,
...inputProps
});
} else {
return /*#__PURE__*/React__default["default"].createElement(FormSelect, {
name: name ?? 'select',
options: options,
id: id,
defaultValue: defaultValue,
...selectProps
});
}
}
}
};
const showPromptMessage = (inputValue = userInput) => inputValue.trim().length === 0 && promptCondition() && promptMessage.length > 0;
const showMinCharsMessage = (inputValue = userInput) => !showPromptMessage() && inputValue.trim().length < minCharsBeforeSearch && minCharsMessage.length > 0;
const displayResultList = (inputValue = userInput) => showOptions && inputValue.trim().length >= minCharsBeforeSearch;
const handlePrompt = (_evt, inputValue = userInput) => {
const canShowPrompt = !displayResultList(inputValue) && (showMinCharsMessage(inputValue) || showPromptMessage(inputValue));
if (!showPrompt && canShowPrompt) {
setShowPrompt(true);
} else if (showPrompt && !canShowPrompt) {
setShowPrompt(false);
}
};
const delayResult = React__default["default"].useMemo(() => debounce(value => {
const filtered = search ? search(value, options) : options.filter(optionsName => optionsName.toLowerCase().includes(value.toLowerCase()));
setActiveOption(0);
setFilterList(filtered);
setShowOptions(true);
statusUpdate && statusUpdate(filtered.length, filtered[0], 1);
}, debounceMs), [debounceMs, options, currentAutocompleteStatus]);
const delayedFilterResults = React__default["default"].useCallback(delayResult, [delayResult]);
const searchMemo = React__default["default"].useMemo(() => debounce(value =>
//@ts-ignore
onChange(value), debounceMs), [debounceMs, onChange]);
const debounceSearch = React__default["default"].useCallback(searchMemo, [searchMemo]);
const handleChange = evt => {
// prevent user input if promptCondition() is true
if (promptCondition()) {
return;
}
const {
value
} = evt.currentTarget;
setUserInput(value);
handlePrompt(evt, value);
// if the user input is blank, close the options list and set the accessibility status to blank
if (value === '') {
setShowOptions(false);
statusUpdate && statusUpdate(-1, '', 0);
} else if (onChange) {
debounceSearch(value);
} else {
delayedFilterResults(value);
}
};
React__default["default"].useEffect(() => {
if (onChange) {
setActiveOption(0);
setFilterList(options);
setShowOptions(true);
}
}, [options, onChange]);
React__default["default"].useEffect(() => {
setAccessibilityStatus(accessibilityStatus);
}, [accessibilityStatus]);
React__default["default"].useEffect(() => {
setUserInput(defaultValue);
}, [defaultValue]);
const handleClick = evt => {
const optionName = evt.currentTarget.innerHTML;
setActiveOption(0);
setFilterList([]);
setShowOptions(false);
setUserInput(multiSelect ? '' : optionName);
statusUpdate && statusUpdate(-1, '', 0);
if (onSelected) onSelected(optionName);
};
const onKeyDown = evt => {
if (!showOptions) {
return;
}
if (evt.code === 'Enter') {
evt.preventDefault();
setActiveOption(0);
setShowOptions(false);
if (filterList.length > 0) {
setUserInput(filterList[activeOption]);
statusUpdate && statusUpdate(-1, '', 0);
if (onSelected) onSelected(filterList[activeOption]);
}
} else if (evt.code === 'ArrowUp') {
if (activeOption === 0) {
return;
}
const newActiveOption = activeOption - 1;
setActiveOption(newActiveOption);
const prevItem = resultRef.current && resultRef.current[newActiveOption];
prevItem && prevItem.scrollIntoView({
block: 'nearest',
inline: 'start'
});
statusUpdate && statusUpdate(filterList.length, filterList[newActiveOption], newActiveOption + 1);
} else if (evt.code === 'ArrowDown') {
if (activeOption === filterList.length - 1) {
return;
}
const newActiveOption = activeOption + 1;
setActiveOption(newActiveOption);
const nextItem = resultRef.current && resultRef.current[newActiveOption];
nextItem && nextItem.scrollIntoView({
block: 'nearest',
inline: 'start'
});
statusUpdate && statusUpdate(filterList.length, filterList[newActiveOption], newActiveOption + 1);
} else if (evt.code === 'Escape') {
setShowOptions(false);
}
};
const onBlur = event => {
setShowPrompt(false);
let focusingOnInput = false;
let focusingOnOptions = false;
if (event.relatedTarget !== null) {
// checks to see if the element comming into focus is the specific input element
focusingOnInput = event.relatedTarget.id === id;
// checks to see if the element comming into focus is an option
focusingOnOptions = Object.keys(resultRef.current).map(value => resultRef.current[parseInt(value, 10)]).includes(event.relatedTarget);
}
if (!(focusingOnInput || focusingOnOptions)) {
setShowOptions(false);
}
};
const setAccessibilityStatus = newStatus => {
if (currentAutocompleteStatus) {
setAccessibilityStatusA('');
setAccessibilityStatusB(newStatus);
} else {
setAccessibilityStatusA(newStatus);
setAccessibilityStatusB('');
}
// Alternates between the two status elements to make sure the change is seen for screen readers
setCurrentAutocompleteStatus(!currentAutocompleteStatus);
};
const getActivedescendantId = () => {
if (resultRef.current === null && showOptions) {
return `${optionsId}--1`;
} else if (resultRef.current && resultRef.current[activeOption]) {
return resultRef.current[activeOption].id;
} else {
return null;
}
};
const formInput = /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(FormInput, {
name: name || 'autocompleteSearch',
type: "text",
value: userInput,
onChange: handleChange,
onFocus: handlePrompt,
onBlur: onBlur,
prefix: prefix,
suffix: suffix,
required: required,
inputProps: {
onKeyDown,
autoComplete: 'off',
id,
...inputProps,
...(showPrompt && {
'aria-describedby': promptId
}),
'aria-expanded': showOptions,
'aria-owns': resultId,
role: 'combobox',
'aria-activedescendant': getActivedescendantId()
},
tabIndex: tabIndex,
hiddenErrorText: ""
}), showPrompt && ( /*#__PURE__*/React__default["default"].createElement("div", {
className: promptClassName,
id: promptId
}, showPromptMessage() && promptMessage, showMinCharsMessage() && minCharsMessage)));
const searchEl = multiSelect ? ( /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("div", {
role: Roles.presentation,
style: {
display: 'inline-flex',
flexDirection: 'row',
width: '100%',
flexWrap: 'wrap',
position: 'relative'
}
}, selected && selected.map(({
id,
label,
value
}, index) => ( /*#__PURE__*/React__default["default"].createElement(Selected, {
key: index,
select: {
id,
label,
value
},
onRemove: onRemove,
onFocus: onFocus,
style: {
...selectedListItemStyle,
display: 'inline-flex'
}
}))), displayComp()), /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement(SelectedItem, {
className: removeAllButtonClass,
label: "x",
role: "button",
ariaLabel: "Remove all",
onClick: onRemoveAll,
style: {
marginLeft: '5px',
fontWeight: 'bold',
verticalAlign: '-webkit-baseline-middle'
},
tabIndex: 0
})))) : ( /*#__PURE__*/React__default["default"].createElement("div", {
style: {
position: 'relative'
}
}, errorPosition && errorPosition === exports.AutoCompleteErrorPosition.BEFORE_LABEL && ( /*#__PURE__*/React__default["default"].createElement(ErrorMessage, {
text: errorMessageText,
className: errorMessageClassName,
id: errorId,
visuallyHiddenText: errorVisuallyHiddenText
})), labelText && ( /*#__PURE__*/React__default["default"].createElement("label", {
htmlFor: id,
className: labelClassName,
...labelProps
}, labelText)), errorPosition && errorPosition === exports.AutoCompleteErrorPosition.AFTER_LABEL && ( /*#__PURE__*/React__default["default"].createElement(ErrorMessage, {
text: errorMessageText,
className: errorMessageClassName,
id: errorId,
visuallyHiddenText: errorVisuallyHiddenText
})), hintText && ( /*#__PURE__*/React__default["default"].createElement(Hint, {
text: hintText,
className: hintClass,
id: hintId,
useLabel: false
})), errorPosition && errorPosition === exports.AutoCompleteErrorPosition.AFTER_HINT && ( /*#__PURE__*/React__default["default"].createElement(ErrorMessage, {
text: errorMessageText,
className: errorMessageClassName,
id: errorId,
visuallyHiddenText: errorVisuallyHiddenText
})), /*#__PURE__*/React__default["default"].createElement("div", {
style: {
border: '0px',
clip: 'rect(0px, 0px, 0px, 0px)',
height: '1px',
marginBottom: '-1px',
marginRight: '-1px',
overflow: 'hidden',
padding: '0px',
position: 'absolute',
whiteSpace: 'nowrap',
width: '1px'
}
}, /*#__PURE__*/React__default["default"].createElement("div", {
id: `autocomplete-status-${id}-A`,
role: "status",
"aria-atomic": "true",
"aria-live": "polite"
}, accessibilityStatusA), /*#__PURE__*/React__default["default"].createElement("div", {
id: `autocomplete-status-${id}-B`,
role: "status",
"aria-atomic": "true",
"aria-live": "polite"
}, accessibilityStatusB)), displayComp()));
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, multiSelect && hintText && ( /*#__PURE__*/React__default["default"].createElement(Hint, {
text: hintText,
className: hintClass,
id: hintId,
useLabel: false
})), /*#__PURE__*/React__default["default"].createElement("div", {
className: containerClassName,
style: {
...searchContainerStyle
}
}, searchEl, displayResultList() && ( /*#__PURE__*/React__default["default"].createElement(ResultList, {
resultLiRef: resultRef,
list: filterList,
listId: optionsId,
userInput: userInput,
activeOption: activeOption,
noElFoundText: notFoundText,
onClick: handleClick,
activeClass: resultActiveClass,
ulContainerId: resultId,
ulContainerClass: resultUlClass,
ulContainerStyle: resultUlStyle,
liContainerClass: resultlLiClass,
liContainerStyle: resultLiStyle,
noOptionClass: resultNoOptionClass,
ariaLabeledBy: id
})), /*#__PURE__*/React__default["default"].createElement("span", {
id: `autocomplete-${id}-assistiveHint`,
style: {
display: 'none'
}
}, accessibilityHintText)));
};
exports.BUTTON_TYPE = void 0;
(function (BUTTON_TYPE) {
BUTTON_TYPE["BUTTON"] = "button";
BUTTON_TYPE["SUBMIT"] = "submit";
BUTTON_TYPE["RESET"] = "reset";
})(exports.BUTTON_TYPE || (exports.BUTTON_TYPE = {}));
const Button = ({
label,
onClick,
type = exports.BUTTON_TYPE.BUTTON,
disabled = false,
ariaLabel,
disableClickForMs,
customPrefixImg,
customPostfixImg,
isLoading,
loadingLabel,
customLoadingPreImage,
customLoadingPostImage,
formAction,
name,
value,
className,
variant,
children,
visuallyHiddenText,