UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

788 lines (785 loc) • 31 kB
/** * DevExtreme (cjs/ui/text_box/ui.text_editor.base.js) * Version: 23.2.6 * Build date: Wed May 01 2024 * * Copyright (c) 2012 - 2024 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; exports.default = void 0; var _renderer = _interopRequireDefault(require("../../core/renderer")); var _dom_adapter = _interopRequireDefault(require("../../core/dom_adapter")); var _events_engine = _interopRequireDefault(require("../../events/core/events_engine")); var _selectors = require("../widget/selectors"); var _type = require("../../core/utils/type"); var _extend = require("../../core/utils/extend"); var _iterator = require("../../core/utils/iterator"); var _themes = require("../themes"); var _devices = _interopRequireDefault(require("../../core/devices")); var _editor = _interopRequireDefault(require("../editor/editor")); var _index = require("../../events/utils/index"); var _pointer = _interopRequireDefault(require("../../events/pointer")); var _uiText_editor = _interopRequireDefault(require("./ui.text_editor.clear")); var _index2 = _interopRequireDefault(require("./texteditor_button_collection/index")); var _config = _interopRequireDefault(require("../../core/config")); var _ui = _interopRequireDefault(require("../widget/ui.errors")); var _deferred = require("../../core/utils/deferred"); var _load_indicator = _interopRequireDefault(require("../load_indicator")); var _uiText_editor2 = require("./ui.text_editor.label"); var _size = require("../../core/utils/size"); var _resize_observer = _interopRequireDefault(require("../../core/resize_observer")); var _guid = _interopRequireDefault(require("../../core/guid")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj } } const TEXTEDITOR_CLASS = "dx-texteditor"; const TEXTEDITOR_INPUT_CONTAINER_CLASS = "dx-texteditor-input-container"; const TEXTEDITOR_INPUT_CLASS = "dx-texteditor-input"; const TEXTEDITOR_INPUT_SELECTOR = ".dx-texteditor-input"; const TEXTEDITOR_CONTAINER_CLASS = "dx-texteditor-container"; const TEXTEDITOR_BUTTONS_CONTAINER_CLASS = "dx-texteditor-buttons-container"; const TEXTEDITOR_PLACEHOLDER_CLASS = "dx-placeholder"; const TEXTEDITOR_EMPTY_INPUT_CLASS = "dx-texteditor-empty"; const STATE_INVISIBLE_CLASS = "dx-state-invisible"; const TEXTEDITOR_PENDING_INDICATOR_CLASS = "dx-pending-indicator"; const TEXTEDITOR_VALIDATION_PENDING_CLASS = "dx-validation-pending"; const TEXTEDITOR_VALID_CLASS = "dx-valid"; const EVENTS_LIST = ["KeyDown", "KeyPress", "KeyUp", "Change", "Cut", "Copy", "Paste", "Input"]; const CONTROL_KEYS = ["tab", "enter", "shift", "control", "alt", "escape", "pageUp", "pageDown", "end", "home", "leftArrow", "upArrow", "rightArrow", "downArrow"]; let TextEditorLabelCreator = _uiText_editor2.TextEditorLabel; function checkButtonsOptionType(buttons) { if ((0, _type.isDefined)(buttons) && !Array.isArray(buttons)) { throw _ui.default.Error("E1053") } } const TextEditorBase = _editor.default.inherit({ ctor: function(_, options) { if (options) { checkButtonsOptionType(options.buttons) } this._buttonCollection = new _index2.default(this, this._getDefaultButtons()); this._$beforeButtonsContainer = null; this._$afterButtonsContainer = null; this._labelContainerElement = null; this.callBase.apply(this, arguments) }, _getDefaultOptions: function() { return (0, _extend.extend)(this.callBase(), { buttons: void 0, value: "", spellcheck: false, showClearButton: false, valueChangeEvent: "change", placeholder: "", inputAttr: {}, onFocusIn: null, onFocusOut: null, onKeyDown: null, onKeyUp: null, onChange: null, onInput: null, onCut: null, onCopy: null, onPaste: null, onEnterKey: null, mode: "text", hoverStateEnabled: true, focusStateEnabled: true, text: void 0, displayValueFormatter: function(value) { return (0, _type.isDefined)(value) && false !== value ? value : "" }, stylingMode: (0, _config.default)().editorStylingMode || "outlined", showValidationMark: true, label: "", labelMode: "static", labelMark: "" }) }, _defaultOptionsRules: function() { return this.callBase().concat([{ device: function() { const themeName = (0, _themes.current)(); return (0, _themes.isMaterial)(themeName) }, options: { labelMode: "floating", stylingMode: (0, _config.default)().editorStylingMode || "filled" } }, { device: function() { const themeName = (0, _themes.current)(); return (0, _themes.isFluent)(themeName) }, options: { labelMode: "outside" } }]) }, _getDefaultButtons: function() { return [{ name: "clear", Ctor: _uiText_editor.default }] }, _isClearButtonVisible: function() { return this.option("showClearButton") && !this.option("readOnly") }, _input: function() { return this.$element().find(".dx-texteditor-input").first() }, _isFocused: function() { return (0, _selectors.focused)(this._input()) || this.callBase() }, _inputWrapper: function() { return this.$element() }, _buttonsContainer: function() { return this._inputWrapper().find(".dx-texteditor-buttons-container").eq(0) }, _isControlKey: function(key) { return -1 !== CONTROL_KEYS.indexOf(key) }, _renderStylingMode: function() { this.callBase(); this._updateButtonsStyling(this.option("stylingMode")) }, _initMarkup: function() { this.$element().addClass("dx-texteditor"); this._renderInput(); this._renderStylingMode(); this._renderInputType(); this._renderPlaceholder(); this._renderProps(); this.callBase(); this._renderValue(); this._renderLabel() }, _render: function() { this.callBase(); this._refreshValueChangeEvent(); this._renderEvents(); this._renderEnterKeyAction(); this._renderEmptinessEvent() }, _renderInput: function() { this._$buttonsContainer = this._$textEditorContainer = (0, _renderer.default)("<div>").addClass("dx-texteditor-container").appendTo(this.$element()); this._$textEditorInputContainer = (0, _renderer.default)("<div>").addClass("dx-texteditor-input-container").appendTo(this._$textEditorContainer); this._$textEditorInputContainer.append(this._createInput()); this._renderButtonContainers() }, _getInputContainer() { return this._$textEditorInputContainer }, _renderPendingIndicator: function() { this.$element().addClass("dx-validation-pending"); const $inputContainer = this._getInputContainer(); const $indicatorElement = (0, _renderer.default)("<div>").addClass("dx-pending-indicator").appendTo($inputContainer); this._pendingIndicator = this._createComponent($indicatorElement, _load_indicator.default) }, _disposePendingIndicator: function() { if (!this._pendingIndicator) { return } this._pendingIndicator.dispose(); this._pendingIndicator.$element().remove(); this._pendingIndicator = null; this.$element().removeClass("dx-validation-pending") }, _renderValidationState: function() { this.callBase(); const isPending = "pending" === this.option("validationStatus"); if (isPending) { !this._pendingIndicator && this._renderPendingIndicator(); this._showValidMark = false } else { if ("invalid" === this.option("validationStatus")) { this._showValidMark = false } if (!this._showValidMark && true === this.option("showValidationMark")) { this._showValidMark = "valid" === this.option("validationStatus") && !!this._pendingIndicator } this._disposePendingIndicator() } this._toggleValidMark() }, _renderButtonContainers: function() { const buttons = this.option("buttons"); this._$beforeButtonsContainer = this._buttonCollection.renderBeforeButtons(buttons, this._$buttonsContainer); this._$afterButtonsContainer = this._buttonCollection.renderAfterButtons(buttons, this._$buttonsContainer) }, _cleanButtonContainers: function() { var _this$_$beforeButtons, _this$_$afterButtonsC; null === (_this$_$beforeButtons = this._$beforeButtonsContainer) || void 0 === _this$_$beforeButtons ? void 0 : _this$_$beforeButtons.remove(); null === (_this$_$afterButtonsC = this._$afterButtonsContainer) || void 0 === _this$_$afterButtonsC ? void 0 : _this$_$afterButtonsC.remove(); this._buttonCollection.clean() }, _clean() { this._buttonCollection.clean(); this._disposePendingIndicator(); this._unobserveLabelContainerResize(); this._$beforeButtonsContainer = null; this._$afterButtonsContainer = null; this._$textEditorContainer = null; this._$buttonsContainer = null; this.callBase() }, _createInput: function() { const $input = (0, _renderer.default)("<input>"); this._applyInputAttributes($input, this.option("inputAttr")); return $input }, _setSubmitElementName: function(name) { const inputAttrName = this.option("inputAttr.name"); return this.callBase(name || inputAttrName || "") }, _applyInputAttributes: function($input, customAttributes) { const inputAttributes = (0, _extend.extend)(this._getDefaultAttributes(), customAttributes); $input.attr(inputAttributes).addClass("dx-texteditor-input"); this._setInputMinHeight($input) }, _setInputMinHeight: function($input) { $input.css("minHeight", this.option("height") ? "0" : "") }, _getPlaceholderAttr() { const { ios: ios, mac: mac } = _devices.default.real(); const { placeholder: placeholder } = this.option(); const value = placeholder || (ios || mac ? " " : null); return value }, _getDefaultAttributes() { const defaultAttributes = { autocomplete: "off", placeholder: this._getPlaceholderAttr() }; return defaultAttributes }, _updateButtons: function(names) { this._buttonCollection.updateButtons(names) }, _updateButtonsStyling: function(editorStylingMode) { (0, _iterator.each)(this.option("buttons"), (_, _ref) => { let { options: options, name: buttonName } = _ref; if (options && !options.stylingMode && this.option("visible")) { const buttonInstance = this.getButton(buttonName); buttonInstance.option && buttonInstance.option("stylingMode", "underlined" === editorStylingMode ? "text" : "contained") } }) }, _renderValue: function() { const renderInputPromise = this._renderInputValue(); return renderInputPromise.promise() }, _renderInputValue: function(value) { var _value; value = null !== (_value = value) && void 0 !== _value ? _value : this.option("value"); let text = this.option("text"); const displayValue = this.option("displayValue"); const displayValueFormatter = this.option("displayValueFormatter"); if (void 0 !== displayValue && null !== value) { text = displayValueFormatter(displayValue) } else if (!(0, _type.isDefined)(text)) { text = displayValueFormatter(value) } this.option("text", text); if (this._input().val() !== ((0, _type.isDefined)(text) ? text : "")) { this._renderDisplayText(text) } else { this._toggleEmptinessEventHandler() } return (new _deferred.Deferred).resolve() }, _renderDisplayText: function(text) { this._input().val(text); this._toggleEmptinessEventHandler() }, _isValueValid: function() { if (this._input().length) { const validity = this._input().get(0).validity; if (validity) { return validity.valid } } return true }, _toggleEmptiness: function(isEmpty) { this.$element().toggleClass("dx-texteditor-empty", isEmpty); this._togglePlaceholder(isEmpty) }, _togglePlaceholder: function(isEmpty) { this.$element().find(".".concat("dx-placeholder")).eq(0).toggleClass("dx-state-invisible", !isEmpty) }, _renderProps: function() { this._toggleReadOnlyState(); this._toggleSpellcheckState(); this._toggleTabIndex() }, _toggleDisabledState: function(value) { this.callBase.apply(this, arguments); const $input = this._input(); $input.prop("disabled", value) }, _toggleTabIndex: function() { const $input = this._input(); const disabled = this.option("disabled"); const focusStateEnabled = this.option("focusStateEnabled"); if (disabled || !focusStateEnabled) { $input.attr("tabIndex", -1) } else { $input.removeAttr("tabIndex") } }, _toggleReadOnlyState: function() { this._input().prop("readOnly", this._readOnlyPropValue()); this.callBase() }, _readOnlyPropValue: function() { return this.option("readOnly") }, _toggleSpellcheckState: function() { this._input().prop("spellcheck", this.option("spellcheck")) }, _unobserveLabelContainerResize: function() { if (this._labelContainerElement) { _resize_observer.default.unobserve(this._labelContainerElement); this._labelContainerElement = null } }, _getLabelContainer: function() { return this._input() }, _getLabelContainerWidth: function() { return (0, _size.getWidth)(this._getLabelContainer()) }, _getLabelBeforeWidth: function() { const buttonsBeforeWidth = this._$beforeButtonsContainer && (0, _size.getWidth)(this._$beforeButtonsContainer); return null !== buttonsBeforeWidth && void 0 !== buttonsBeforeWidth ? buttonsBeforeWidth : 0 }, _updateLabelWidth: function() { this._label.updateBeforeWidth(this._getLabelBeforeWidth()); this._label.updateMaxWidth(this._getLabelContainerWidth()) }, _getFieldElement() { return this._getLabelContainer() }, _setFieldAria(force) { const { "aria-label": ariaLabel } = this.option("inputAttr"); const labelId = this._label.getId(); const value = ariaLabel ? void 0 : labelId; if (value || force) { const aria = { labelledby: value, label: ariaLabel }; this.setAria(aria, this._getFieldElement()) } }, _renderLabel: function() { this._unobserveLabelContainerResize(); this._labelContainerElement = (0, _renderer.default)(this._getLabelContainer()).get(0); const { label: label, labelMode: labelMode, labelMark: labelMark, rtlEnabled: rtlEnabled } = this.option(); const labelConfig = { onClickHandler: () => { this.focus() }, onHoverHandler: e => { e.stopPropagation() }, onActiveHandler: e => { e.stopPropagation() }, $editor: this.$element(), text: label, mark: labelMark, mode: labelMode, rtlEnabled: rtlEnabled, containsButtonsBefore: !!this._$beforeButtonsContainer, getContainerWidth: () => this._getLabelContainerWidth(), getBeforeWidth: () => this._getLabelBeforeWidth() }; this._label = new TextEditorLabelCreator(labelConfig); this._setFieldAria(); if (this._labelContainerElement) { _resize_observer.default.observe(this._labelContainerElement, this._updateLabelWidth.bind(this)) } }, _renderPlaceholder: function() { this._renderPlaceholderMarkup(); this._attachPlaceholderEvents() }, _renderPlaceholderMarkup: function() { if (this._$placeholder) { this._$placeholder.remove(); this._$placeholder = null } const $input = this._input(); const placeholder = this.option("placeholder"); const placeholderAttributes = { id: placeholder ? "dx-".concat(new _guid.default) : void 0, "data-dx_placeholder": placeholder }; const $placeholder = this._$placeholder = (0, _renderer.default)("<div>").attr(placeholderAttributes); $placeholder.insertAfter($input); $placeholder.addClass("dx-placeholder") }, _attachPlaceholderEvents: function() { const startEvent = (0, _index.addNamespace)(_pointer.default.up, this.NAME); _events_engine.default.on(this._$placeholder, startEvent, () => { _events_engine.default.trigger(this._input(), "focus") }); this._toggleEmptinessEventHandler() }, _placeholder: function() { return this._$placeholder || (0, _renderer.default)() }, _clearValueHandler: function(e) { const $input = this._input(); e.stopPropagation(); this._saveValueChangeEvent(e); this._clearValue(); !this._isFocused() && _events_engine.default.trigger($input, "focus"); _events_engine.default.trigger($input, "input") }, _clearValue: function() { this.clear() }, _renderEvents: function() { const $input = this._input(); (0, _iterator.each)(EVENTS_LIST, (_, event) => { if (this.hasActionSubscription("on" + event)) { const action = this._createActionByOption("on" + event, { excludeValidators: ["readOnly"] }); _events_engine.default.on($input, (0, _index.addNamespace)(event.toLowerCase(), this.NAME), e => { if (this._disposed) { return } action({ event: e }) }) } }) }, _refreshEvents: function() { const $input = this._input(); (0, _iterator.each)(EVENTS_LIST, (_, event) => { _events_engine.default.off($input, (0, _index.addNamespace)(event.toLowerCase(), this.NAME)) }); this._renderEvents() }, _keyPressHandler: function() { this.option("text", this._input().val()) }, _keyDownHandler: function(e) { const $input = this._input(); const isCtrlEnter = e.ctrlKey && "enter" === (0, _index.normalizeKeyName)(e); const isNewValue = $input.val() !== this.option("value"); if (isCtrlEnter && isNewValue) { _events_engine.default.trigger($input, "change") } }, _getValueChangeEventOptionName: function() { return "valueChangeEvent" }, _renderValueChangeEvent: function() { const keyPressEvent = (0, _index.addNamespace)(this._renderValueEventName(), "".concat(this.NAME, "TextChange")); const valueChangeEvent = (0, _index.addNamespace)(this.option(this._getValueChangeEventOptionName()), "".concat(this.NAME, "ValueChange")); const keyDownEvent = (0, _index.addNamespace)("keydown", "".concat(this.NAME, "TextChange")); const $input = this._input(); _events_engine.default.on($input, keyPressEvent, this._keyPressHandler.bind(this)); _events_engine.default.on($input, valueChangeEvent, this._valueChangeEventHandler.bind(this)); _events_engine.default.on($input, keyDownEvent, this._keyDownHandler.bind(this)) }, _cleanValueChangeEvent: function() { const valueChangeNamespace = ".".concat(this.NAME, "ValueChange"); const textChangeNamespace = ".".concat(this.NAME, "TextChange"); _events_engine.default.off(this._input(), valueChangeNamespace); _events_engine.default.off(this._input(), textChangeNamespace) }, _refreshValueChangeEvent: function() { this._cleanValueChangeEvent(); this._renderValueChangeEvent() }, _renderValueEventName: function() { return "input change keypress" }, _focusTarget: function() { return this._input() }, _focusEventTarget: function() { return this.element() }, _isInput: function(element) { return element === this._input().get(0) }, _preventNestedFocusEvent: function(event) { if (event.isDefaultPrevented()) { return true } let shouldPrevent = this._isNestedTarget(event.relatedTarget); if ("focusin" === event.type) { shouldPrevent = shouldPrevent && this._isNestedTarget(event.target) && !this._isInput(event.target) } else if (!shouldPrevent) { this._toggleFocusClass(false, this.$element()) } shouldPrevent && event.preventDefault(); return shouldPrevent }, _isNestedTarget: function(target) { return !!this.$element().find(target).length }, _focusClassTarget: function() { return this.$element() }, _focusInHandler: function(event) { this._preventNestedFocusEvent(event); this.callBase.apply(this, arguments) }, _focusOutHandler: function(event) { this._preventNestedFocusEvent(event); this.callBase.apply(this, arguments) }, _toggleFocusClass: function(isFocused, $element) { this.callBase(isFocused, this._focusClassTarget($element)) }, _hasFocusClass: function(element) { return this.callBase((0, _renderer.default)(element || this.$element())) }, _renderEmptinessEvent: function() { const $input = this._input(); _events_engine.default.on($input, "input blur", this._toggleEmptinessEventHandler.bind(this)) }, _toggleEmptinessEventHandler: function() { const text = this._input().val(); const isEmpty = ("" === text || null === text) && this._isValueValid(); this._toggleEmptiness(isEmpty) }, _valueChangeEventHandler: function(e, formattedValue) { if (this.option("readOnly")) { return } this._saveValueChangeEvent(e); this.option("value", arguments.length > 1 ? formattedValue : this._input().val()); this._saveValueChangeEvent(void 0) }, _renderEnterKeyAction: function() { this._enterKeyAction = this._createActionByOption("onEnterKey", { excludeValidators: ["readOnly"] }); _events_engine.default.off(this._input(), "keyup.onEnterKey.dxTextEditor"); _events_engine.default.on(this._input(), "keyup.onEnterKey.dxTextEditor", this._enterKeyHandlerUp.bind(this)) }, _enterKeyHandlerUp: function(e) { if (this._disposed) { return } if ("enter" === (0, _index.normalizeKeyName)(e)) { this._enterKeyAction({ event: e }) } }, _updateValue: function() { this._options.silent("text", null); this._renderValue() }, _dispose: function() { this._enterKeyAction = void 0; this.callBase() }, _getSubmitElement: function() { return this._input() }, _hasActiveElement: function() { return this._input().is(_dom_adapter.default.getActiveElement(this._input()[0])) }, _optionChanged: function(args) { const { name: name, fullName: fullName, value: value } = args; const eventName = name.replace("on", ""); if (EVENTS_LIST.includes(eventName)) { this._refreshEvents(); return } switch (name) { case "valueChangeEvent": this._refreshValueChangeEvent(); this._refreshFocusEvent(); this._refreshEvents(); break; case "onValueChanged": this._createValueChangeAction(); break; case "focusStateEnabled": this.callBase(args); this._toggleTabIndex(); break; case "spellcheck": this._toggleSpellcheckState(); break; case "mode": this._renderInputType(); break; case "onEnterKey": this._renderEnterKeyAction(); break; case "placeholder": this._renderPlaceholder(); this._setFieldAria(true); this._input().attr({ placeholder: this._getPlaceholderAttr() }); break; case "label": this._label.updateText(value); this._setFieldAria(true); break; case "labelMark": this._label.updateMark(value); break; case "labelMode": this._label.updateMode(value); this._setFieldAria(); break; case "width": this.callBase(args); this._label.updateMaxWidth(this._getLabelContainerWidth()); break; case "readOnly": case "disabled": this._updateButtons(); this.callBase(args); break; case "showClearButton": this._updateButtons(["clear"]); break; case "text": break; case "value": this._updateValue(); this.callBase(args); break; case "inputAttr": this._applyInputAttributes(this._input(), this.option(name)); break; case "stylingMode": this._renderStylingMode(); this._updateLabelWidth(); break; case "buttons": if (fullName === name) { checkButtonsOptionType(value) } this._cleanButtonContainers(); this._renderButtonContainers(); this._updateButtonsStyling(this.option("stylingMode")); this._updateLabelWidth(); this._label.updateContainsButtonsBefore(!!this._$beforeButtonsContainer); break; case "visible": this.callBase(args); if (value && this.option("buttons")) { this._cleanButtonContainers(); this._renderButtonContainers(); this._updateButtonsStyling(this.option("stylingMode")) } break; case "displayValueFormatter": this._invalidate(); break; case "showValidationMark": break; default: this.callBase(args) } }, _renderInputType: function() { this._setInputType(this.option("mode")) }, _setInputType: function(type) { const input = this._input(); if ("search" === type) { type = "text" } try { input.prop("type", type) } catch (e) { input.prop("type", "text") } }, getButton(name) { return this._buttonCollection.getButton(name) }, focus: function() { _events_engine.default.trigger(this._input(), "focus") }, clear: function() { if (this._showValidMark) { this._showValidMark = false; this._renderValidationState() } const defaultOptions = this._getDefaultOptions(); if (this.option("value") === defaultOptions.value) { this._options.silent("text", ""); this._renderValue() } else { this.option("value", defaultOptions.value) } }, _resetToInitialValue() { if (this.option("value") === this._initialValue) { this._options.silent("text", this._initialValue); this._renderValue() } else { this.callBase() } this._disposePendingIndicator(); this._showValidMark = false; this._toggleValidMark() }, _toggleValidMark() { this.$element().toggleClass("dx-valid", !!this._showValidMark) }, reset: function() { let value = arguments.length > 0 && void 0 !== arguments[0] ? arguments[0] : void 0; if (arguments.length) { this.callBase(value) } else { this.callBase() } }, on: function(eventName, eventHandler) { const result = this.callBase(eventName, eventHandler); const event = eventName.charAt(0).toUpperCase() + eventName.substr(1); if (EVENTS_LIST.indexOf(event) >= 0) { this._refreshEvents() } return result } }); var _default = TextEditorBase; exports.default = _default; module.exports = exports.default; module.exports.default = exports.default;