@capgeminiuk/dcx-react-library
Version:
[](https://circleci.com/gh/Capgemini/dcx-react-library)
1,643 lines (1,608 loc) • 103 kB
JavaScript
import React, { useSyncExternalStore, useState, useRef, useEffect, useReducer, forwardRef, useImperativeHandle, createContext, useContext } from 'react';
import { Rule } from '@cesium133/forgjs';
import IMask from 'imask';
function _extends() {
_extends = Object.assign ? Object.assign.bind() : function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
}
return target;
}
/**
* Check if is valid or not
* @param validation
* @param value
*/
const isValid = (validation, value) => {
const floatRule = new 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.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 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 (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
func.apply(context, args);
}, 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.createElement(React.Fragment, null);
if (text && typeof text === 'string') {
errorElement = /*#__PURE__*/React.createElement("span", {
id: id,
className: classNames(['dcx-error-message', className])
}, visuallyHiddenText && ( /*#__PURE__*/React.createElement("span", {
className: visuallyHiddenText.className
}, visuallyHiddenText.text)), text);
}
return errorElement;
};
return /*#__PURE__*/React.createElement(React.Fragment, null, getErrorElement());
};
const Hint = ({
text,
className,
id,
useLabel
}) => useLabel ? ( /*#__PURE__*/React.createElement("label", {
id: id,
className: classNames(['dcx-hint', className])
}, text)) : ( /*#__PURE__*/React.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.createElement("div", {
className: containerClasses,
id: id
}, /*#__PURE__*/React.createElement("div", {
className: groupClassName
}, /*#__PURE__*/React.createElement("label", {
className: labelClassName,
htmlFor: inputId
}, label), /*#__PURE__*/React.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.createElement("legend", {
className: className
}, heading ? /*#__PURE__*/React.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.createElement("option", {
value: value,
id: id,
className: className,
disabled: disabled,
"aria-label": ariaLabel || label
}, label));
const OptionGroup = ({
label,
displayCount,
options
}) => ( /*#__PURE__*/React.createElement("optgroup", {
label: displayCount ? `${label} (${options.length})` : label
}, options.map((item, index) => ( /*#__PURE__*/React.createElement(Option, _extends({
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] = useState(conditional ? conditional.value : '');
const onChangeHandler = event => {
if (onChange) onChange(event);
};
const input = /*#__PURE__*/React.createElement("input", _extends({
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.createElement("label", _extends({}, labelProps, {
className: labelClassName
}), input, label)) : ( /*#__PURE__*/React.createElement(React.Fragment, null, input, /*#__PURE__*/React.createElement("label", _extends({}, labelProps, {
htmlFor: id,
className: labelClassName
}), label)));
const getConditionalElement = () => {
let hydratedElm = /*#__PURE__*/React.createElement(React.Fragment, null);
if (!hydrated && conditional !== undefined) {
hydratedElm = Conditional(_extends({}, conditional, {
value: conditionalValue
}));
} else if (conditional !== undefined && conditionalReveal()) {
hydratedElm = Conditional(_extends({}, conditional, {
value: conditionalValue,
onChange: event => {
setConditionalValue(event.currentTarget.value);
if (onChange) onChange(event, event.currentTarget.value);
}
}));
}
return hydratedElm;
};
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", _extends({}, itemProps, {
className: itemClassName
}), hint && hint.position === 'above' && /*#__PURE__*/React.createElement(Hint, _extends({}, hint)), el, hint && hint.position !== 'above' && /*#__PURE__*/React.createElement(Hint, _extends({}, hint)), conditional && !conditional.position && getConditionalElement()), conditional && conditional.position === 'sibling' && getConditionalElement());
};
const Label$1 = ({
label,
labelProperties,
htmlFor,
className
}) => label ? ( /*#__PURE__*/React.createElement("label", _extends({}, labelProperties, {
htmlFor: htmlFor,
className: className
}), label)) : null;
const FormCheckbox = ({
id,
name,
value,
label,
inputProps,
itemProps,
labelProps,
ariaLabel,
ariaDataControls: _ariaDataControls = '',
ariaDescribedBy: _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.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 == null ? void 0 : 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 = _extends({}, newSelection, {
[item.id]: true
});
} else {
newSelection = _extends({}, newSelection, {
[item.id]: false
});
}
});
return newSelection;
};
const [selection, setSelection] = 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(_extends({}, 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.createElement("div", {
key: `${id}_${index.toString()}`,
id: item.id,
className: item.className
}, item.text);
}
if (isString(item)) {
return /*#__PURE__*/React.createElement(CheckboxRadioBase, {
id: item,
type: type,
key: `${id}_${index.toString()}`,
name: name,
inputProps: _extends({}, inputProps),
itemProps: _extends({}, itemProps),
labelProps: _extends({}, labelProps),
inputClassName: inputClassName,
labelClassName: labelClassName,
itemClassName: itemClassName,
label: item,
value: item,
onChange: event => {
handleChange(item, event);
}
});
}
if (type === 'radio') {
return /*#__PURE__*/React.createElement(CheckboxRadioBase, _extends({
type: type,
key: `${id}_${index.toString()}`
}, item, {
name: name,
inputProps: _extends({}, inputProps, item.inputProps),
itemProps: _extends({}, itemProps, item.itemProps),
labelProps: _extends({}, 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.createElement(FormCheckbox, _extends({
key: `${id}_${index.toString()}`
}, item, {
name: name,
inputProps: _extends({}, inputProps, item.inputProps),
itemProps: _extends({}, itemProps, item.itemProps),
labelProps: _extends({}, 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.createElement("div", {
id: id,
className: groupClasses
}, /*#__PURE__*/React.createElement("fieldset", {
className: fieldsetClasses,
"aria-describedby": ariaDescribedBy || ''
}, legend && /*#__PURE__*/React.createElement(Legend, _extends({}, legend)), hint && /*#__PURE__*/React.createElement(Hint, _extends({}, hint)), error && /*#__PURE__*/React.createElement(ErrorMessage, _extends({}, error)), /*#__PURE__*/React.createElement("div", {
className: itemsClasses
}, formGroupItems)))) : ( /*#__PURE__*/React.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.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.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'];
var ErrorPosition;
(function (ErrorPosition) {
ErrorPosition["BEFORE_LABEL"] = "before-label";
ErrorPosition["BOTTOM"] = "bottom";
ErrorPosition["AFTER_LABEL"] = "after-label";
ErrorPosition["AFTER_HINT"] = "after-hint";
})(ErrorPosition || (ErrorPosition = {}));
const FormInput = ({
name,
type,
value,
label,
validation: _validation = null,
inputProps,
labelProps,
errorProps,
prefix,
suffix,
onChange,
onFocus,
onBlur,
isValid,
staticErrorMessage,
errorPosition,
ariaLabel,
ariaRequired,
displayError: _displayError = false,
inputClassName,
containerClassName,
containerClassNameError,
labelClassName,
required,
hint,
variant: _variant = 'normal',
inputDivProps: _inputDivProps = {
style: {
display: 'flex'
}
},
tabIndex,
hiddenErrorText: _hiddenErrorText = '',
hiddenErrorTextProps
}) => {
const {
validity,
onValueChange
} = useValidationOnChange(_validation, value);
const [showError, setShowError] = React.useState(_displayError);
React.useEffect(() => {
if (isValid && validity) isValid(validity.valid, showError && !validity.valid);
// eslint-disable-next-line
}, [validity == null ? void 0 : validity.valid, showError]);
React.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.createElement("p", _extends({}, errorProps, {
className: classNames(['dcx-error-message', errorProps == null ? void 0 : errorProps.className]),
role: Roles.alert
}), !isEmpty(_hiddenErrorText) && ( /*#__PURE__*/React.createElement("span", _extends({}, hiddenErrorTextProps), _hiddenErrorText + ' ')), staticErrorMessage);
}
if (validity && !validity.valid && showError) {
return /*#__PURE__*/React.createElement("p", _extends({}, errorProps, {
className: classNames(['dcx-error-message', errorProps == null ? void 0 : errorProps.className]),
role: Roles.alert
}), !isEmpty(_hiddenErrorText) && ( /*#__PURE__*/React.createElement("span", _extends({}, hiddenErrorTextProps), _hiddenErrorText + ' ')), validity.message);
}
return null;
};
const isStaticOrDynamicError = () => isStaticMessageValid() || validity && !validity.valid || false;
const inputEl = /*#__PURE__*/React.createElement("input", _extends({
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.createElement(Label$1, {
label: label,
labelProperties: labelProps,
htmlFor: inputProps == null ? void 0 : inputProps.id,
className: labelClassName
});
const containerClasses = classNames(['dcx-form-input', containerClassName, {
'dcx-form-input--filled': !!value,
'dcx-form-input--placeholder': !!(inputProps != null && inputProps.placeholder),
'dcx-error-bottom': errorPosition === 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.createElement("div", {
className: containerClasses
}, errorPosition && errorPosition === ErrorPosition.BEFORE_LABEL && ( /*#__PURE__*/React.createElement(ErrorMessage, null)), !floatVariants.includes(_variant) && labelEl, errorPosition && errorPosition === ErrorPosition.AFTER_LABEL && ( /*#__PURE__*/React.createElement(ErrorMessage, null)), hint && hint.position === 'above' && /*#__PURE__*/React.createElement(Hint, _extends({}, hint)), errorPosition && errorPosition === ErrorPosition.AFTER_HINT && ( /*#__PURE__*/React.createElement(ErrorMessage, null)), prefix || suffix ? ( /*#__PURE__*/React.createElement("div", _extends({}, _inputDivProps), prefix && !isEmpty(prefix) && ( /*#__PURE__*/React.createElement("div", _extends({}, prefix.properties, {
className: classNames(['dcx-form-input__prefix', prefix.properties.className])
}), prefix.content)), floatVariants.includes(_variant) ? ( /*#__PURE__*/React.createElement("div", {
className: "dcx-wrapper-label"
}, labelEl, inputEl)) : inputEl, suffix && !isEmpty(suffix) && ( /*#__PURE__*/React.createElement("div", _extends({}, suffix.properties, {
className: classNames(['dcx-form-input__suffix', suffix.properties.className])
}), suffix.content)))) : floatVariants.includes(_variant) ? ( /*#__PURE__*/React.createElement("div", {
className: "dcx-wrapper-label"
}, labelEl, inputEl)) : inputEl, hint && hint.position !== 'above' && /*#__PURE__*/React.createElement(Hint, _extends({}, hint)), errorPosition && errorPosition === ErrorPosition.BOTTOM && ( /*#__PURE__*/React.createElement(ErrorMessage, null)));
};
const _excluded$f = ["options", "onChange", "value", "name", "type", "ariaLabel"];
const FormInputMasked = _ref => {
let {
options,
onChange,
value,
name,
type,
ariaLabel
} = _ref,
props = _objectWithoutPropertiesLoose(_ref, _excluded$f);
const inputRef = useRef(null);
const [mask, setMask] = useState(null);
useEffect(() => {
if (mask && value) {
mask.value = value;
}
}, [mask, value]);
useEffect(() => {
if (inputRef.current && !mask) {
setMask(IMask(inputRef.current, _extends({}, options)));
}
}, [mask, options]);
useEffect(() => {
if (inputRef.current && mask && onChange) {
const {
current
} = inputRef;
mask.on('accept', () => {
onChange({
value: current.value,
unmaskedValue: mask.unmaskedValue
});
});
}
}, [mask, onChange]);
return /*#__PURE__*/React.createElement("input", _extends({
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.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 == null ? void 0 : 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: _options = [],
onChange,
value,
id,
ariaLabel,
label,
labelProps,
hint,
error,
errorMessage,
errorMessageClassName,
errorMessageVisuallyHidden,
errorMessageId,
style,
nullOption,
containerProps,
defaultValue,
tabIndex,
variant: _variant = 'normal',
disabled: _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] = 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 = _extends({}, item);
}
return /*#__PURE__*/React.createElement(Option, _extends({
key: index
}, convertedItem));
});
const getOptionGroups = optionGroups => optionGroups.map((groupOption, index) => ( /*#__PURE__*/React.createElement(OptionGroup, _extends({
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.createElement("div", _extends({
className: containerClasses
}, containerProps), /*#__PURE__*/React.createElement(Label$1, {
label: label,
labelProperties: labelProps,
htmlFor: id,
className: labelClassName
}), hint && /*#__PURE__*/React.createElement(Hint, _extends({}, hint)), error && /*#__PURE__*/React.createElement(ErrorMessage, _extends({}, error)), errorMessage && ( /*#__PURE__*/React.createElement(ErrorMessage, {
text: errorMessage,
className: errorMessageClassName,
visuallyHiddenText: errorMessageVisuallyHidden,
id: errorMessageId
})), /*#__PURE__*/React.createElement("select", _extends({
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.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.createElement("ul", {
id: ulContainerId,
className: ulContainerClass,
style: ulContainerStyle,
"aria-labelledby": ariaLabeledBy,
role: "listbox"
}, userInput && list.length > 0 ? list.map((optionName, index) => ( /*#__PURE__*/React.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 = _extends({}, resultLiRef.current, {
[index]: ref
});
},
role: "option",
"aria-selected": index === activeOption,
"aria-setsize": list.length,
"aria-posinset": index + 1,
tabIndex: -1
}, optionName))) : noElFoundText && ( /*#__PURE__*/React.createElement("li", {
className: classNames([liContainerClass, noOptionClass])
}, noElFoundText))));
const SelectedItem = ({
label,
ariaLabel,
className,
role,
style,
tabIndex,
onClick,
onFocus,
onKeyDown
}) => ( /*#__PURE__*/React.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.createElement("div", {
id: select.id,
className: className,
role: Roles.listItem,
style: style
}, /*#__PURE__*/React.createElement(SelectedItem, {
className: labelClassName,
label: select.label
}), /*#__PURE__*/React.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
}));
};
var AutoCompleteErrorPosition;
(function (AutoCompleteErrorPosition) {
AutoCompleteErrorPosition["BEFORE_LABEL"] = "before-label";
AutoCompleteErrorPosition["BOTTOM"] = "bottom";
AutoCompleteErrorPosition["AFTER_LABEL"] = "after-label";
AutoCompleteErrorPosition["AFTER_HINT"] = "after-hint";
})(AutoCompleteErrorPosition || (AutoCompleteErrorPosition = {}));
const Autocomplete = ({
options,
optionsId,
minCharsBeforeSearch: _minCharsBeforeSearch = 1,
minCharsMessage: _minCharsMessage = '',
promptCondition: _promptCondition = () => false,
promptMessage: _promptMessage = '',
promptId: _promptId = 'input-prompt',
promptClassName,
debounceMs: _debounceMs = 0,
inputProps,
defaultValue: _defaultValue = '',
hintText,
hintClass,
hintId,
multiSelect: _multiSelect = false,
notFoundText,
resultId,
resultActiveClass,
resultUlClass,
resultUlStyle,
resultlLiClass,
resultLiStyle,
resultNoOptionClass,
removeAllButtonClass,
searchContainerStyle,
selectedListItemStyle,
selected,
onSelected,
onChange,
onRemove,
onRemoveAll,
onFocus,
required: _required = false,
containerClassName,
labelText,
labelClassName,
labelProps,
id,
errorPosition,
errorMessageText: _errorMessageText = '',
errorMessageClassName,
errorId,
errorVisuallyHiddenText,
name,
selectProps,
prefix,
suffix,
tabIndex,
search,
accessibilityStatus: _accessibilityStatus = '',
accessibilityHintText: _accessibilityHintText = '',
statusUpdate,
customNonJSComp: _customNonJSComp = undefined
}) => {
const [activeOption, setActiveOption] = useState(0);
const [filterList, setFilterList] = useState([]);
const [showOptions, setShowOptions] = useState(false);
const [showPrompt, setShowPrompt] = useState(false);
const [userInput, setUserInput] = useState(_defaultValue);
const [currentAutocompleteStatus, setCurrentAutocompleteStatus] = useState(true);
const resultRef = useRef(null);
const [accessibilityStatusA, setAccessibilityStatusA] = useState('');
const [accessibilityStatusB, setAccessibilityStatusB] = useState('');
let hydrated = useHydrated();
const displayComp = () => {
if (hydrated) {
return formInput;
} else {
if (_customNonJSComp !== undefined) {
return _customNonJSComp;
} else {
if (_multiSelect) {
return /*#__PURE__*/React.createElement(FormSelect, _extends({
name: "multiSelect",
options: options
}, inputProps));
} else {
return /*#__PURE__*/React.createElement(FormSelect, _extends({
name: name != null ? 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.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.useCallback(delayResult, [delayResult]);
const searchMemo = React.useMemo(() => debounce(value =>
//@ts-ignore
onChange(value), _debounceMs), [_debounceMs, onChange]);
const debounceSearch = React.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.useEffect(() => {
if (onChange) {
setActiveOption(0);
setFilterList(options);
setShowOptions(true);
}
}, [options, onChange]);
React.useEffect(() => {
setAccessibilityStatus(_accessibilityStatus);
}, [_accessibilityStatus]);
React.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.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(FormInput, {
name: name || 'autocompleteSearch',
type: "text",
value: userInput,
onChange: handleChange,
onFocus: handlePrompt,
onBlur: onBlur,
prefix: prefix,
suffix: suffix,
required: _required,
inputProps: _extends({
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.createElement("div", {
className: promptClassName,
id: _promptId
}, showPromptMessage() && _promptMessage, showMinCharsMessage() && _minCharsMessage)));
const searchEl = _multiSelect ? ( /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.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.createElement(Selected, {
key: index,
select: {
id,
label,
value
},
onRemove: onRemove,
onFocus: onFocus,
style: _extends({}, selectedListItemStyle, {
display: 'inline-flex'
})
}))), displayComp()), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.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.createElement("div", {
style: {
position: 'relative'
}
}, errorPosition && errorPosition === AutoCompleteErrorPosition.BEFORE_LABEL && ( /*#__PURE__*/React.createElement(ErrorMessage, {
text: _errorMessageText,
className: errorMessageClassName,
id: errorId,
visuallyHiddenText: errorVisuallyHiddenText
})), labelText && ( /*#__PURE__*/React.createElement("label", _extends({
htmlFor: id,
className: labelClassName
}, labelProps), labelText)), errorPosition && errorPosition === AutoCompleteErrorPosition.AFTER_LABEL && ( /*#__PURE__*/React.createElement(ErrorMessage, {
text: _errorMessageText,
className: errorMessageClassName,
id: errorId,
visuallyHiddenText: errorVisuallyHiddenText
})), hintText && ( /*#__PURE__*/React.createElement(Hint, {
text: hintText,
className: hintClass,
id: hintId,
useLabel: false
})), errorPosition && errorPosition === AutoCompleteErrorPosition.AFTER_HINT && ( /*#__PURE__*/React.createElement(ErrorMessage, {
text: _errorMessageText,
className: errorMessageClassName,
id: errorId,
visuallyHiddenText: errorVisuallyHiddenText
})), /*#__PURE__*/React.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.createElement("div", {
id: `autocomplete-status-${id}-A`,
role: "status",
"aria-atomic": "true",
"aria-live": "polite"
}, accessibilityStatusA), /*#__PURE__*/React.createElement("div", {
id: `autocomplete-status-${id}-B`,
role: "status",
"aria-atomic": "true",
"aria-live": "polite"
}, accessibilityStatusB)), displayComp()));
return /*#__PURE__*/React.createElement(React.Fragment, null, _multiSelect && hintText && ( /*#__PURE__*/React.createElement(Hint, {
text: hintText,
className: hintClass,
id: hintId,
useLabel: false
})), /*#__PURE__*/React.createElement("div", {
className: containerClassName,
style: _extends({}, searchContainerStyle)
}, searchEl, displayResultList() && ( /*#__PURE__*/React.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.createElement("span", {
id: `autocomplete-${id}-assistiveHint`,
style: {
display: 'none'
}
}, _accessibilityHintText)));
};
const _excluded$e = ["label", "onClick", "type", "disabled", "ariaLabel", "disableClickForMs", "customPrefixImg", "customPostfixImg", "isLoading", "loadingLabel", "customLoadingPreImage", "customLoadingPostImage", "formAction", "name", "value", "className", "variant", "children", "visuallyHiddenText"];
var BUTTON_TYPE;
(function (BUTTON_TYPE) {
BUTTON_TYPE["BUTTON"] = "button";
BUTTON_TYPE["SUBMIT"] = "submit";
BUTTON_TYPE["RESET"] = "reset";
})(BUTTON_TYPE || (BUTTON_TYPE = {}));
const Button = _ref => {
let {
label,
onClick,
type = BUTTON_TYPE.BUTTON,
disabled = false,
ariaLabel,
disableClickForMs,
customPrefixImg,
customPostfixImg,
isLoading,
loadingLabel,
customLoadingPreImage,
customLoadingPostImage,
formAction,
name,
value,
className,
variant,
children,
visuallyHiddenText
} = _ref,
props = _objectWithoutPropertiesLoose(_ref, _excluded$e);
const [disable, setDisable] = React.useState(disabled);
const debouncedClick = React.useMemo(() => debounce(() => setDisable(false), disableClickForMs), [setDisable, disableClickForMs]);
const delayNextClick = React.useCallback(debouncedClick, [debouncedClick]);
React.useEffect(() => {
if (isLoading === true) {
setDisable(true);
} else if (isLoading === false) {
setDisable(false);
}
}, [isLoading]);
React.useEffect(() => {
setDisable(disabled);
}, [disabled]);
const handleClick = evt => {
if (disableClickForMs) {
setDisable(true);
delayNextClick();
}
if (onClick) onClick(evt);
};
let prefix = /*#__PURE__*/React.createElement(React.Fragment, null);
let postfix = /*#__PURE__*/React.createElement(React.Fragment, null);
if (isLoading) {
prefix = /*#__PURE__*/React.createElement(React.Fragment, null, customLoadingPreImage);
postfix = /*#__PURE__*/React.createElement(React.Fragment, null, customLoadingPostImage);
} else {
if (customPrefixImg) prefix = /*#__PURE__*/React.createElement(React.Fragment, null, customPrefixImg);
if (customPostfixImg) postfix = /*#__PURE__*/React.createElement(React.Fragment, null, customPostfixImg);
}
const btnClassName = classNames(['dcx-button', className, {
[`dcx-button--${variant}`]: variant !== undefined
}]);
if (label !== undefined && children !== undefined) {
throw new Error('You can use label or children but not both at the same time');
}
return /*#__PURE__*/React.createElement("button", _extends({
onClick: handleClick,
disabled: disable,
type: type,
"aria-label": ariaLabel,
formAction: formAction,
name: name,
className: btnClassName,
value: value
}, props), prefix, isLoading && loadingLabel ? loadingLabel : label, visuallyHiddenText && ( /*#__PURE__*/React.createElement("span", {
className: visuallyHiddenText.className
}, visuallyHiddenText.text)), !isLoading && children, postfix);
};
const Progress = ({
label,
max,
className,
id,
labelClassName,
value,
tabIndex
}) => ( /*#__PURE__*/React.createElement("div", {
tabIndex: tabIndex,
"data-testid": id
}, /*#__PURE__*/React.createElement("label", {
className: labelClassName,
htmlFor: id
}, `${label}: `), /*#__PURE__*/React.createElement("progress", {
id: id,
className: className,
max: max,
value: value
}, /*#__PURE__*/React.createElement("div", {
id: id,
className: className
}, /*#__PURE__*/React.createElement("span", {
style: {
width: value
}
}, value ? `${label}: ${value}%` : '')))));
var style$2 = {"tooltip":"_LAOWL"};
const _excluded$d = ["min", "max", "value", "prefix", "suffix", "disabled", "ariaLabel", "onChange", "onChangeMax", "onChangeMin", "inputClass", "showTooltip", "tabIndex"];
const Range = _ref => {
let {
min = 0,
max = 100,
value = (min + max) / 2,
prefix,
suffix,
disabled = false,
ariaLabel,
onChange,
onChangeMax,
onChangeMin,
inputClass,
showTooltip = false,
tabIndex
} = _ref,
props = _objectWithoutPropertiesLoose(_ref, _excluded$d);
const [defaultValue, setDefaultValue] = React.useState(value);
const [scrubberPosition, setScrubberPosition] = React.useState();
const [styling, setStyling] = React.useState();
useEffect(() => {
setScrubberPosition((defaultValue - min) * 100 / (max - min));
setStyling({
'--left': scrubberPosition + '%'
});
}, [defaultValue, scrubberPosition]);
const handleClickMin = () => {
setDefaultValue(min);
if (onChangeMin) {
onChangeMin(min);
}
};
const handleClickMax = () => {
setDefaultValue(max);
if (onChangeMax) {
onChangeMax(max);
}
};
const handleChange = event => {
const {
value
} = event.target;
setScrubberPosition((defaultValue - min) * 100 / (max - min));
setDefaultValue(parseInt(value));
if (onChange) {
onChange(parseInt(value));
}
};
return /*#__PURE__*/React.createElement("div", {
style: {
display: 'flex'
}
}, prefix && /*#__PURE__*/React.createElement("div", {
onClick: handleClickMin
}, prefix), /*#__PURE__*/React.createElement("input", _extends({
type: "range",