devextreme
Version:
JavaScript/TypeScript Component Suite for Responsive Web Development
241 lines (240 loc) • 9.15 kB
JavaScript
/**
* DevExtreme (esm/__internal/ui/m_text_area.js)
* Version: 25.2.7
* Build date: Tue May 05 2026
*
* Copyright (c) 2012 - 2026 Developer Express Inc. ALL RIGHTS RESERVED
* Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/
*/
import eventsEngine from "../../common/core/events/core/events_engine";
import scrollEvents from "../../common/core/events/gesture/emitter.gesture.scroll";
import pointerEvents from "../../common/core/events/pointer";
import {
addNamespace,
eventData
} from "../../common/core/events/utils/index";
import registerComponent from "../../core/component_registrator";
import $ from "../../core/renderer";
import {
ensureDefined,
noop
} from "../../core/utils/common";
import {
getElementBoxParams,
getOuterHeight,
getVerticalOffsets,
parseHeight
} from "../../core/utils/size";
import {
isDefined
} from "../../core/utils/type";
import {
getWindow,
hasWindow
} from "../../core/utils/window";
import TextBox from "../ui/text_box/m_text_box";
import {
allowScroll,
prepareScrollData
} from "../ui/text_box/utils.scroll";
export const TEXTAREA_CLASS = "dx-textarea";
export const TEXTEDITOR_INPUT_CLASS_AUTO_RESIZE = "dx-texteditor-input-auto-resize";
class TextArea extends TextBox {
_getDefaultOptions() {
return Object.assign({}, super._getDefaultOptions(), {
spellcheck: true,
autoResizeEnabled: false,
_shouldAttachKeyboardEvents: false
})
}
_shouldAttachKeyboardEvents() {
const {
_shouldAttachKeyboardEvents: shouldAttachKeyboardEvents,
readOnly: readOnly
} = this.option();
return shouldAttachKeyboardEvents || !readOnly
}
_initMarkup() {
this.$element().addClass("dx-textarea");
super._initMarkup();
this.setAria("multiline", "true")
}
_renderContentImpl() {
this._updateInputHeight();
super._renderContentImpl()
}
_renderInput() {
super._renderInput();
this._renderScrollHandler()
}
_createInput() {
const $input = $("<textarea>");
this._applyInputAttributes($input, this.option("inputAttr"));
this._updateInputAutoResizeAppearance($input);
return $input
}
_setInputMinHeight() {}
_renderScrollHandler() {
this._eventY = 0;
const $input = this._input();
const initScrollData = prepareScrollData($input, true);
eventsEngine.on($input, addNamespace(scrollEvents.init, this.NAME), initScrollData, noop);
eventsEngine.on($input, addNamespace(pointerEvents.down, this.NAME), this._pointerDownHandler.bind(this));
eventsEngine.on($input, addNamespace(pointerEvents.move, this.NAME), this._pointerMoveHandler.bind(this))
}
_pointerDownHandler(e) {
this._eventY = eventData(e).y
}
_pointerMoveHandler(e) {
const currentEventY = eventData(e).y;
const delta = this._eventY - currentEventY;
if (allowScroll(this._input(), delta)) {
e.isScrollingEvent = true;
e.stopPropagation()
}
this._eventY = currentEventY
}
_renderDimensions() {
const $element = this.$element();
const element = $element.get(0);
const width = this._getOptionValue("width", element);
const height = this._getOptionValue("height", element);
const minHeight = this.option("minHeight");
const maxHeight = this.option("maxHeight");
$element.css({
minHeight: void 0 !== minHeight ? minHeight : "",
maxHeight: void 0 !== maxHeight ? maxHeight : "",
width: width,
height: height
})
}
_resetDimensions() {
this.$element().css({
height: "",
minHeight: "",
maxHeight: ""
})
}
_renderEvents() {
if (this.option("autoResizeEnabled")) {
eventsEngine.on(this._input(), addNamespace("input paste", this.NAME), this._updateInputHeight.bind(this))
}
super._renderEvents()
}
_refreshEvents() {
eventsEngine.off(this._input(), addNamespace("input paste", this.NAME));
super._refreshEvents()
}
_getHeightDifference($input) {
var _this$_$textEditorCon, _this$_$textEditorInp;
const verticalElementOffset = getVerticalOffsets(this.$element().get(0), false);
const verticalEditorContainerOffset = getVerticalOffsets(null === (_this$_$textEditorCon = this._$textEditorContainer) || void 0 === _this$_$textEditorCon ? void 0 : _this$_$textEditorCon.get(0), false);
const verticalInputContainerOffsets = getVerticalOffsets(null === (_this$_$textEditorInp = this._$textEditorInputContainer) || void 0 === _this$_$textEditorInp ? void 0 : _this$_$textEditorInp.get(0), true);
const inputMargin = getElementBoxParams("height", getWindow().getComputedStyle($input.get(0))).margin;
const sum = verticalElementOffset + verticalEditorContainerOffset + verticalInputContainerOffsets + inputMargin;
return sum
}
_updateInputHeight() {
if (!hasWindow()) {
return
}
const {
autoResizeEnabled: autoResizeEnabled,
height: height,
minHeight: minHeightOptionValue
} = this.option();
const $input = this._input();
const shouldCalculateInputHeight = autoResizeEnabled || void 0 === height && minHeightOptionValue;
if (!shouldCalculateInputHeight) {
$input.css("height", "");
return
}
this._resetDimensions();
this.$element().css("height", getOuterHeight(this.$element()));
$input.css("height", 0);
const heightDifference = this._getHeightDifference($input);
this._renderDimensions();
const minHeight = this._getBoundaryHeight("minHeight");
const maxHeight = this._getMaxHeight();
let inputHeight = $input[0].scrollHeight;
if (void 0 !== minHeight) {
inputHeight = Math.max(inputHeight, minHeight - heightDifference)
}
if (void 0 !== maxHeight) {
const adjustedMaxHeight = this._getAdjustedMaxHeight(maxHeight, heightDifference);
const needScroll = inputHeight > adjustedMaxHeight;
inputHeight = Math.min(inputHeight, adjustedMaxHeight);
this._updateInputAutoResizeAppearance($input, !needScroll)
}
$input.css("height", inputHeight);
if (autoResizeEnabled) {
this.$element().css("height", "auto")
}
}
_getAdjustedMaxHeight(maxHeight, heightDifference) {
const adjustedMaxHeight = maxHeight - heightDifference;
return adjustedMaxHeight
}
_getMaxHeight() {
return this._getBoundaryHeight("maxHeight")
}
_getBoundaryHeight(optionName) {
const boundaryValue = this.option(optionName);
if (isDefined(boundaryValue)) {
return "number" === typeof boundaryValue ? boundaryValue : parseHeight(boundaryValue, this.$element().get(0).parentElement, this.$element().get(0))
}
return
}
_renderInputType() {}
_visibilityChanged(visible) {
if (visible) {
this._updateInputHeight()
}
}
_updateInputAutoResizeAppearance($input, isAutoResizeEnabled) {
if ($input) {
const autoResizeEnabled = ensureDefined(isAutoResizeEnabled, Boolean(this.option("autoResizeEnabled")));
$input.toggleClass("dx-texteditor-input-auto-resize", autoResizeEnabled)
}
}
_dimensionChanged() {
if (this.option("visible")) {
this._updateInputHeight()
}
}
_optionChanged(args) {
const {
name: name,
value: value
} = args;
switch (name) {
case "_shouldAttachKeyboardEvents":
case "autoResizeEnabled":
this._updateInputAutoResizeAppearance(this._input(), Boolean(value));
this._refreshEvents();
this._renderDimensions();
this._updateInputHeight();
break;
case "value":
case "height":
super._optionChanged(args);
this._updateInputHeight();
break;
case "minHeight":
case "maxHeight":
this._renderDimensions();
this._updateInputHeight();
break;
case "visible":
super._optionChanged(args);
if (value) {
this._updateInputHeight()
}
break;
default:
super._optionChanged(args)
}
}
}
registerComponent("dxTextArea", TextArea);
export default TextArea;