UNPKG

@capgeminiuk/dcx-react-library

Version:

[![CircleCI](https://circleci.com/gh/Capgemini/dcx-react-library.svg?style=svg)](https://circleci.com/gh/Capgemini/dcx-react-library)

1,643 lines (1,608 loc) 103 kB
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",