@dnb/eufemia
Version:
DNB Eufemia Design System UI Library
435 lines (434 loc) • 15.8 kB
JavaScript
"use client";
import _extends from "@babel/runtime-corejs3/helpers/esm/extends";
import _defineProperty from "@babel/runtime-corejs3/helpers/esm/defineProperty";
var _AlignmentHelper, _span;
import _JSON$parse from "core-js-pure/stable/json/parse.js";
import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import FormLabel from "../form-label/FormLabel.js";
import FormStatus from "../form-status/FormStatus.js";
import TextCounter from "../../fragments/text-counter/TextCounter.js";
import { isTrue, makeUniqueId, extendPropsWithContextInClassComponent, validateDOMAttributes, processChildren, getStatusState, combineDescribedBy, warn, dispatchCustomElementEvent, convertJsxToString } from "../../shared/component-helper.js";
import { pickFormElementProps } from "../../shared/helpers/filterValidProps.js";
import AlignmentHelper from "../../shared/AlignmentHelper.js";
import { spacingPropTypes, createSpacingClasses } from "../space/SpacingHelper.js";
import { skeletonDOMAttributes, createSkeletonClass } from "../skeleton/SkeletonHelper.js";
import Context from "../../shared/Context.js";
import Suffix from "../../shared/helpers/Suffix.js";
export default class Textarea extends React.PureComponent {
static getDerivedStateFromProps(props, state) {
const value = Textarea.getValue(props);
if (value !== 'initval' && value !== state.value && value !== state._value) {
if (value !== state.value && typeof props.on_state_update === 'function') {
dispatchCustomElementEvent({
props
}, 'on_state_update', {
value
});
}
state.value = value;
}
if (props.textarea_state) {
state.textareaState = props.textarea_state;
}
state._value = props.value;
return state;
}
static hasValue(value) {
return (typeof value === 'string' || typeof value === 'number') && String(value).length > 0 || false;
}
static getValue(props) {
const value = processChildren(props);
if (value === '' || Textarea.hasValue(value)) {
return value;
}
return props.value;
}
constructor(_props) {
super(_props);
_defineProperty(this, "state", {
textareaState: 'virgin',
value: null,
_value: null
});
_defineProperty(this, "onFocusHandler", event => {
const {
value
} = this._ref.current;
this.setState({
value,
textareaState: 'focus'
});
dispatchCustomElementEvent(this, 'on_focus', {
value,
event
});
});
_defineProperty(this, "onBlurHandler", event => {
const {
value
} = event.target;
this.setState({
value,
textareaState: Textarea.hasValue(value) ? 'dirty' : 'initial'
});
dispatchCustomElementEvent(this, 'on_blur', {
value,
event
});
});
_defineProperty(this, "onChangeHandler", event => {
const {
value
} = event.target;
const props = this.getProps();
const autoresize = isTrue(props.autoresize);
if (autoresize) {
this.prepareAutosize();
}
const rows = this.getRows(value);
const ret = dispatchCustomElementEvent(this, 'on_change', {
value,
rows,
event
});
if (ret !== false) {
this.setState({
value
});
if (autoresize) {
this.setAutosize(rows);
}
}
});
_defineProperty(this, "onKeyDownHandler", event => {
const rows = this.getRows();
const {
value
} = event.target;
dispatchCustomElementEvent(this, 'on_key_down', {
value,
rows,
event
});
});
_defineProperty(this, "prepareAutosize", () => {
const elem = this._ref.current;
if (!elem) {
return;
}
try {
elem.style.height = 'auto';
} catch (e) {
warn(e);
}
});
_defineProperty(this, "setAutosize", (rows = null) => {
const elem = this._ref.current;
if (!elem) {
return;
}
try {
if (typeof this._heightOffset === 'undefined') {
this._heightOffset = elem.offsetHeight - elem.clientHeight;
}
elem.style.height = 'auto';
const lineHeight = this.getLineHeight();
let newHeight = elem.scrollHeight + this._heightOffset;
if (!rows) {
rows = this.getRows();
}
if (rows === 1) {
if (newHeight > lineHeight) {
newHeight = lineHeight;
}
}
const props = this.getProps();
const maxRows = parseFloat(props.autoresize_max_rows);
if (maxRows > 0) {
const maxHeight = maxRows * lineHeight;
if (rows > maxRows || newHeight > maxHeight) {
newHeight = maxHeight;
}
}
elem.style.height = newHeight + 'px';
} catch (e) {
warn(e);
}
});
this._ref = React.createRef();
this._id = _props.id || makeUniqueId();
if (_props.textarea_state) {
this.state.textareaState = _props.textarea_state;
}
try {
if (typeof navigator !== 'undefined') {
this.resizeModifier = /Firefox|Edg/.test(navigator.userAgent) || /Chrome/.test(navigator.userAgent) && /Win/.test(navigator.platform) ? 'large' : false;
if (!this.resizeModifier) {
this.resizeModifier = /Safari|Chrome/.test(navigator.userAgent) && /Mac/.test(navigator.platform) ? 'medium' : false;
}
}
} catch (error) {
console.error(error);
}
}
componentDidMount() {
const props = this.getProps();
if (props.inner_ref) {
typeof props.inner_ref === 'function' ? props.inner_ref(this._ref.current) : props.inner_ref.current = this._ref.current;
}
if (isTrue(props.autoresize) && typeof window !== 'undefined') {
this.setAutosize();
try {
this.resizeObserver = new ResizeObserver(entries => {
window.requestAnimationFrame(() => {
if (!Array.isArray(entries) || !entries.length) {
return;
}
this.setAutosize();
});
});
this.resizeObserver.observe(document.body);
} catch (e) {
window.addEventListener('resize', this.setAutosize);
}
}
}
componentWillUnmount() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
this.resizeObserver = null;
}
if (typeof window !== 'undefined') {
window.removeEventListener('resize', this.setAutosize);
}
}
getRows() {
return Math.floor(this._ref.current.scrollHeight / this.getLineHeight()) || 1;
}
getLineHeight() {
return parseFloat(getComputedStyle(this._ref.current).lineHeight) || 0;
}
getProps() {
var _this$context, _this$context2, _this$context3;
return extendPropsWithContextInClassComponent(this.props, Textarea.defaultProps, {
skeleton: (_this$context = this.context) === null || _this$context === void 0 ? void 0 : _this$context.skeleton
}, this.context.getTranslation(this.props).Textarea, pickFormElementProps((_this$context2 = this.context) === null || _this$context2 === void 0 ? void 0 : _this$context2.FormRow), pickFormElementProps((_this$context3 = this.context) === null || _this$context3 === void 0 ? void 0 : _this$context3.formElement), this.context.Textarea);
}
render() {
const props = this.getProps();
const {
label,
label_direction,
label_sr_only,
status,
status_state,
status_props,
status_no_animation,
globalStatus,
suffix,
disabled,
skeleton,
stretch,
placeholder,
keepPlaceholder,
align,
size,
textarea_class,
readOnly,
textarea_attributes,
className,
autoresize,
characterCounter,
autoresize_max_rows,
id: _id,
children,
value: _value,
textarea_element: _textarea_element,
...attributes
} = props;
const {
value,
textareaState
} = this.state;
const id = this._id;
const showStatus = getStatusState(status);
const hasValue = Textarea.hasValue(value);
let {
textarea_element: TextareaElement
} = props;
const textareaAttributes = textarea_attributes ? typeof textarea_attributes === 'string' ? _JSON$parse(textarea_attributes) : textarea_attributes : {};
const textareaParams = {
className: classnames("dnb-textarea__textarea dnb-input__border", textarea_class),
role: 'textbox',
value: hasValue ? value : '',
id,
name: id,
disabled: isTrue(disabled) || isTrue(skeleton),
'aria-placeholder': placeholder ? convertJsxToString(placeholder) : undefined,
...attributes,
...textareaAttributes,
onChange: this.onChangeHandler,
onFocus: this.onFocusHandler,
onBlur: this.onBlurHandler,
onKeyDown: this.onKeyDownHandler
};
if (showStatus || suffix) {
textareaParams['aria-describedby'] = combineDescribedBy(textareaParams, showStatus ? id + '-status' : null, suffix ? id + '-suffix' : null);
}
if (readOnly) {
textareaParams['aria-readonly'] = textareaParams.readOnly = true;
}
const mainParams = {
className: classnames(`dnb-textarea dnb-textarea--${textareaState} dnb-form-component`, createSkeletonClass(null, skeleton), createSpacingClasses(props), className, autoresize ? 'dnb-textarea__autoresize' : this.resizeModifier && `dnb-textarea__resize--${this.resizeModifier}`, disabled && 'dnb-textarea--disabled', hasValue && 'dnb-textarea--has-content', align && `dnb-textarea__align--${align}`, size && `dnb-textarea__size--${size}`, status && `dnb-textarea__status--${status_state}`, label_direction && `dnb-textarea--${label_direction}`, isTrue(stretch) && `dnb-textarea--stretch`, isTrue(keepPlaceholder) && `dnb-textarea--keep-placeholder`)
};
const innerParams = {
className: classnames('dnb-textarea__inner', createSkeletonClass('shape', skeleton, this.context))
};
const shellParams = {
className: 'dnb-textarea__shell'
};
if (isTrue(disabled) || isTrue(skeleton)) {
shellParams['aria-disabled'] = true;
}
const placeholderStyle = parseFloat(props.rows) > 0 ? {
'--textarea-rows': parseFloat(props.rows)
} : null;
skeletonDOMAttributes(innerParams, skeleton, this.context);
validateDOMAttributes(this.props, textareaParams);
validateDOMAttributes(null, innerParams);
validateDOMAttributes(null, shellParams);
if (TextareaElement && typeof TextareaElement === 'function') {
TextareaElement = TextareaElement(textareaParams, this._ref);
} else if (!TextareaElement && _textarea_element) {
TextareaElement = _textarea_element;
}
return React.createElement("span", mainParams, label && React.createElement(FormLabel, {
id: id + '-label',
forId: id,
text: label,
labelDirection: label_direction,
srOnly: label_sr_only,
disabled: disabled,
skeleton: skeleton
}), React.createElement("span", innerParams, _AlignmentHelper || (_AlignmentHelper = React.createElement(AlignmentHelper, null)), React.createElement(FormStatus, _extends({
show: showStatus,
id: id + '-form-status',
globalStatus: globalStatus,
label: label,
text_id: id + '-status',
text: status,
state: status_state,
no_animation: status_no_animation,
skeleton: skeleton
}, status_props)), React.createElement("span", {
className: "dnb-textarea__row"
}, React.createElement("span", shellParams, TextareaElement || React.createElement("textarea", _extends({
ref: this._ref
}, textareaParams)), !hasValue && placeholder && (textareaState !== 'focus' || keepPlaceholder) && React.createElement("span", {
className: 'dnb-textarea__placeholder' + (align ? ` dnb-textarea__align--${align}` : ""),
style: placeholderStyle,
"aria-hidden": true
}, placeholder), _span || (_span = React.createElement("span", {
className: "dnb-textarea__state"
}))), suffix && React.createElement(Suffix, {
className: "dnb-textarea__suffix",
id: id + '-suffix',
context: props
}, suffix)), characterCounter && React.createElement(TextCounter, _extends({
top: "x-small",
text: value,
max: characterCounter,
lang: props.lang,
locale: props.locale
}, characterCounter))));
}
}
_defineProperty(Textarea, "contextType", Context);
_defineProperty(Textarea, "defaultProps", {
value: 'initval',
id: null,
label: null,
label_direction: null,
label_sr_only: null,
status: null,
textarea_state: null,
status_state: 'error',
status_props: null,
status_no_animation: null,
globalStatus: null,
suffix: null,
placeholder: null,
keepPlaceholder: null,
align: null,
size: null,
stretch: null,
disabled: null,
skeleton: null,
autoresize: null,
autoresize_max_rows: null,
characterCounter: null,
textarea_class: null,
textarea_attributes: null,
readOnly: false,
rows: null,
cols: null,
inner_ref: null,
className: null,
textarea_element: null,
children: null,
on_change: null,
on_focus: null,
on_blur: null,
on_key_down: null,
on_state_update: null
});
process.env.NODE_ENV !== "production" ? Textarea.propTypes = {
value: PropTypes.string,
id: PropTypes.string,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.node]),
label_direction: PropTypes.oneOf(['horizontal', 'vertical']),
label_sr_only: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
status: PropTypes.oneOfType([PropTypes.string, PropTypes.bool, PropTypes.func, PropTypes.node]),
textarea_state: PropTypes.string,
status_state: PropTypes.string,
status_props: PropTypes.object,
status_no_animation: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
globalStatus: PropTypes.shape({
id: PropTypes.string,
message: PropTypes.oneOfType([PropTypes.string, PropTypes.node])
}),
suffix: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.node]),
placeholder: PropTypes.node,
keepPlaceholder: PropTypes.bool,
align: PropTypes.oneOf(['left', 'right']),
size: PropTypes.oneOf(['small', 'medium', 'large']),
stretch: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
disabled: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
skeleton: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
characterCounter: PropTypes.oneOfType([PropTypes.shape({
max: PropTypes.number,
variant: PropTypes.oneOf(['down', 'up'])
}), PropTypes.number]),
autoresize: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
autoresize_max_rows: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
textarea_class: PropTypes.string,
textarea_attributes: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
readOnly: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
rows: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
cols: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
inner_ref: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
...spacingPropTypes,
className: PropTypes.string,
textarea_element: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
on_change: PropTypes.func,
on_focus: PropTypes.func,
on_blur: PropTypes.func,
on_key_down: PropTypes.func,
on_state_update: PropTypes.func
} : void 0;
Textarea._formElement = true;
Textarea._supportsSpacingProps = true;
//# sourceMappingURL=Textarea.js.map