suomifi-ui-components
Version:
Suomi.fi UI component library
216 lines (213 loc) • 10.1 kB
JavaScript
import { __rest, __assign, __makeTemplateObject } from 'tslib';
import React, { useState, useEffect, useRef, forwardRef } from 'react';
import { styled } from 'styled-components';
import classnames from 'classnames';
import { AutoId } from '../../utils/AutoId/AutoId.js';
import { SuomifiThemeConsumer } from '../../theme/SuomifiThemeProvider/SuomifiThemeProvider.js';
import '../../theme/SuomifiTheme/SuomifiTheme.js';
import { SpacingConsumer } from '../../theme/SpacingProvider/SpacingProvider.js';
import { separateMarginProps } from '../../theme/utils/spacing.js';
import { Debounce } from '../../utils/Debounce/Debounce.js';
import { getConditionalAriaProp } from '../../../utils/aria/aria.js';
import { filterDuplicateKeys, forkRefs } from '../../../utils/common/common.js';
import '../../../reset/HtmlA/HtmlA.js';
import '../../../reset/HtmlButton/HtmlButton.js';
import { HtmlDiv } from '../../../reset/HtmlDiv/HtmlDiv.js';
import '../../../reset/HtmlFieldSet/HtmlFieldSet.js';
import '../../../reset/HtmlH/HtmlH.js';
import { HtmlInput } from '../../../reset/HtmlInput/HtmlInput.js';
import '../../../reset/HtmlLabel/HtmlLabel.js';
import '../../../reset/HtmlLegend/HtmlLegend.js';
import '../../../reset/HtmlLi/HtmlLi.js';
import '../../../reset/HtmlNav/HtmlNav.js';
import '../../../reset/HtmlOl/HtmlOl.js';
import { HtmlSpan } from '../../../reset/HtmlSpan/HtmlSpan.js';
import '../../../reset/HtmlTextarea/HtmlTextarea.js';
import '../../../reset/HtmlUl/HtmlUl.js';
import '../../../reset/HtmlTable/HtmlTable.js';
import '../../../reset/HtmlTable/HtmlTableCaption.js';
import '../../../reset/HtmlTable/HtmlTableHeader.js';
import '../../../reset/HtmlTable/HtmlTableRow.js';
import '../../../reset/HtmlTable/HtmlTableBody.js';
import '../../../reset/HtmlTable/HtmlTableHeaderCell.js';
import '../../../reset/HtmlTable/HtmlTableCell.js';
import { VisuallyHidden } from '../../VisuallyHidden/VisuallyHidden.js';
import { Label } from '../Label/Label.js';
import { StatusText } from '../StatusText/StatusText.js';
import { HintText } from '../HintText/HintText.js';
import { baseStyles } from './TextInput.baseStyles.js';
var baseClassName = 'fi-text-input';
var textInputClassNames = {
baseClassName: baseClassName,
fullWidth: "".concat(baseClassName, "--full-width"),
disabled: "".concat(baseClassName, "--disabled"),
error: "".concat(baseClassName, "--error"),
success: "".concat(baseClassName, "--success"),
labelIsVisible: "".concat(baseClassName, "_label--visible"),
icon: "".concat(baseClassName, "_with-icon"),
inputElementContainer: "".concat(baseClassName, "_input-element-container"),
inputElement: "".concat(baseClassName, "_input"),
styleWrapper: "".concat(baseClassName, "_wrapper"),
statusTextHasContent: "".concat(baseClassName, "_statusText--has-content"),
bottomWrapper: "".concat(baseClassName, "_bottom-wrapper"),
characterCounter: "".concat(baseClassName, "_character-counter"),
characterCounterError: "".concat(baseClassName, "_character-counter--error")
};
var BaseTextInput = function BaseTextInput(props) {
var _a, _b, _c, _d;
var _e = useState(0),
charCount = _e[0],
setCharCount = _e[1];
var _f = useState(''),
characterCounterAriaText = _f[0],
setCharacterCounterAriaText = _f[1];
var _g = useState(null),
typingTimer = _g[0],
setTypingTimer = _g[1];
var _h = useState(false),
isMounted = _h[0],
setIsMounted = _h[1];
useEffect(function () {
setIsMounted(true);
return function () {
setIsMounted(false);
};
}, []);
var className = props.className,
labelText = props.labelText,
labelMode = props.labelMode,
propOnChange = props.onChange,
style = props.style,
optionalText = props.optionalText,
status = props.status,
statusText = props.statusText,
hintText = props.hintText,
visualPlaceholder = props.visualPlaceholder,
id = props.id,
_j = props.type,
type = _j === void 0 ? 'text' : _j,
fullWidth = props.fullWidth,
icon = props.icon,
forwardedRef = props.forwardedRef;
props.globalMargins;
var debounce = props.debounce,
_k = props.statusTextAriaLiveMode,
statusTextAriaLiveMode = _k === void 0 ? 'assertive' : _k,
ariaDescribedBy = props["aria-describedby"],
tooltipComponent = props.tooltipComponent,
characterLimit = props.characterLimit,
ariaCharactersRemainingText = props.ariaCharactersRemainingText,
ariaCharactersExceededText = props.ariaCharactersExceededText,
rest = __rest(props, ["className", "labelText", "labelMode", "onChange", "style", "optionalText", "status", "statusText", "hintText", "visualPlaceholder", "id", "type", "fullWidth", "icon", "forwardedRef", "globalMargins", "debounce", "statusTextAriaLiveMode", 'aria-describedby', "tooltipComponent", "characterLimit", "ariaCharactersRemainingText", "ariaCharactersExceededText"]);
var _l = separateMarginProps(rest),
passProps = _l[1];
useEffect(function () {
var _a, _b;
if (characterLimit !== undefined && ((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value.length)) {
setCharCount((_b = inputRef.current) === null || _b === void 0 ? void 0 : _b.value.length);
}
}, []);
var inputRef = useRef(null);
var handleOnChange = function handleOnChange(event, debouncer) {
if (typingTimer) {
clearTimeout(typingTimer);
}
if (characterLimit !== undefined && ariaCharactersRemainingText && ariaCharactersExceededText) {
var charCountInInput_1 = event.target.value.length;
setCharCount(charCountInInput_1);
var newTypingTimer = setTimeout(function () {
if (isMounted) {
setCharacterCounterAriaText(charCountInInput_1 <= characterLimit ? ariaCharactersRemainingText(characterLimit - charCountInInput_1) : ariaCharactersExceededText(charCountInInput_1 - characterLimit));
}
}, 1500);
setTypingTimer(newTypingTimer);
}
if (!!propOnChange) {
debouncer(propOnChange, event.target.value);
}
};
var definedRef = forwardedRef || null;
var hintTextId = "".concat(id, "-hintText");
var statusTextId = "".concat(id, "-statusText");
return /*#__PURE__*/React.createElement(HtmlDiv, {
className: classnames(baseClassName, className, (_a = {}, _a[textInputClassNames.disabled] = !!passProps.disabled, _a[textInputClassNames.icon] = !!icon, _a[textInputClassNames.error] = status === 'error', _a[textInputClassNames.success] = status === 'success', _a[textInputClassNames.fullWidth] = fullWidth, _a)),
style: style
}, /*#__PURE__*/React.createElement(HtmlSpan, {
className: textInputClassNames.styleWrapper
}, /*#__PURE__*/React.createElement(Label, {
htmlFor: id,
labelMode: labelMode,
optionalText: optionalText,
className: classnames((_b = {}, _b[textInputClassNames.labelIsVisible] = labelMode !== 'hidden', _b)),
tooltipComponent: tooltipComponent
}, labelText), /*#__PURE__*/React.createElement(HintText, {
id: hintTextId
}, hintText), /*#__PURE__*/React.createElement(HtmlDiv, {
className: textInputClassNames.inputElementContainer
}, /*#__PURE__*/React.createElement(Debounce, {
waitFor: debounce
}, function (debouncer) {
return /*#__PURE__*/React.createElement(HtmlInput, __assign({}, passProps, {
id: id,
className: textInputClassNames.inputElement,
type: type,
forwardedRef: forkRefs(inputRef, definedRef),
placeholder: visualPlaceholder
}, {
'aria-invalid': status === 'error'
}, getConditionalAriaProp('aria-describedby', [statusText ? statusTextId : undefined, hintText ? hintTextId : undefined, ariaDescribedBy]), {
onChange: function onChange(event) {
handleOnChange(event, debouncer);
}
}));
}), icon), /*#__PURE__*/React.createElement(HtmlDiv, {
className: textInputClassNames.bottomWrapper
}, /*#__PURE__*/React.createElement(StatusText, {
id: statusTextId,
className: classnames((_c = {}, _c[textInputClassNames.statusTextHasContent] = !!statusText, _c)),
status: status,
ariaLiveMode: statusTextAriaLiveMode,
disabled: passProps.disabled
}, characterLimit && ( /*#__PURE__*/React.createElement(VisuallyHidden, null, characterCounterAriaText)), statusText), characterLimit && ( /*#__PURE__*/React.createElement(HtmlDiv, {
className: classnames(textInputClassNames.characterCounter, (_d = {}, _d[textInputClassNames.characterCounterError] = charCount > characterLimit, _d))
}, "".concat(charCount, "/").concat(characterLimit))))));
};
var StyledTextInput = styled(function (_a) {
_a.theme;
var passProps = __rest(_a, ["theme"]);
return /*#__PURE__*/React.createElement(BaseTextInput, __assign({}, passProps));
}).withConfig({
componentId: "sc-1hi9v5l-0"
})(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n ", "\n"], ["\n ", "\n"])), function (_a) {
var theme = _a.theme,
globalMargins = _a.globalMargins,
rest = __rest(_a, ["theme", "globalMargins"]);
var _b = separateMarginProps(rest),
marginProps = _b[0];
var cleanedGlobalMargins = filterDuplicateKeys(globalMargins.textInput, marginProps);
return baseStyles(theme, cleanedGlobalMargins, marginProps);
});
var TextInput = /*#__PURE__*/forwardRef(function (props, ref) {
var propId = props.id,
passProps = __rest(props, ["id"]);
return /*#__PURE__*/React.createElement(SpacingConsumer, null, function (_a) {
var margins = _a.margins;
return /*#__PURE__*/React.createElement(SuomifiThemeConsumer, null, function (_a) {
var suomifiTheme = _a.suomifiTheme;
return /*#__PURE__*/React.createElement(AutoId, {
id: propId
}, function (id) {
return /*#__PURE__*/React.createElement(StyledTextInput, __assign({
theme: suomifiTheme,
id: id,
forwardedRef: ref,
globalMargins: margins
}, passProps));
});
});
});
});
TextInput.displayName = 'TextInput';
var templateObject_1;
export { TextInput, textInputClassNames };
//# sourceMappingURL=TextInput.js.map