UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

461 lines (451 loc) • 18.8 kB
/** * DevExtreme (cjs/ui/html_editor/modules/mentions.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 _devextremeQuill = _interopRequireDefault(require("devextreme-quill")); var _data = require("../../../core/utils/data"); var _type = require("../../../core/utils/type"); var _extend = require("../../../core/utils/extend"); var _element = require("../../../core/element"); var _events_engine = _interopRequireDefault(require("../../../events/core/events_engine")); var _base = _interopRequireDefault(require("./base")); var _popup = _interopRequireDefault(require("./popup")); var _mention = _interopRequireDefault(require("../formats/mention")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) { descriptor.writable = true } Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor) } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) { _defineProperties(Constructor.prototype, protoProps) } if (staticProps) { _defineProperties(Constructor, staticProps) } Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor } function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return "symbol" === typeof key ? key : String(key) } function _toPrimitive(input, hint) { if ("object" !== typeof input || null === input) { return input } var prim = input[Symbol.toPrimitive]; if (void 0 !== prim) { var res = prim.call(input, hint || "default"); if ("object" !== typeof res) { return res } throw new TypeError("@@toPrimitive must return a primitive value.") } return ("string" === hint ? String : Number)(input) } function _assertThisInitialized(self) { if (void 0 === self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called") } return self } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass) } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function(o, p) { o.__proto__ = p; return o }; return _setPrototypeOf(o, p) } let MentionModule = _base.default; if (_devextremeQuill.default) { const USER_ACTION = "user"; const DEFAULT_MARKER = "@"; const KEYS = { ARROW_UP: "upArrow", ARROW_DOWN: "downArrow", ARROW_LEFT: "leftArrow", ARROW_RIGHT: "rightArrow", ENTER: "enter", ESCAPE: "escape", SPACE: "space", PAGE_UP: "pageUp", PAGE_DOWN: "pageDown", END: "end", HOME: "home" }; const NAVIGATION_KEYS = [KEYS.ARROW_LEFT, KEYS.ARROW_RIGHT, KEYS.PAGE_UP, KEYS.PAGE_DOWN, KEYS.END, KEYS.HOME]; const ALLOWED_PREFIX_CHARS = [" ", "\n"]; const DISABLED_STATE_CLASS = "dx-state-disabled"; _devextremeQuill.default.register({ "formats/mention": _mention.default }, true); MentionModule = function(_PopupModule) { _inheritsLoose(MentionModule, _PopupModule); var _proto = MentionModule.prototype; _proto._getDefaultOptions = function() { const baseConfig = _PopupModule.prototype._getDefaultOptions.call(this); return (0, _extend.extend)(baseConfig, { itemTemplate: "item", valueExpr: "this", displayExpr: "this", template: null, searchExpr: null, searchTimeout: 500, minSearchLength: 0 }) }; function MentionModule(quill, options) { var _this; _this = _PopupModule.call(this, quill, options) || this; _this._mentions = {}; options.mentions.forEach(item => { let marker = item.marker; if (!marker) { item.marker = marker = DEFAULT_MARKER } const template = item.template; if (template) { const preparedTemplate = _this.editorInstance._getTemplate(template); preparedTemplate && _mention.default.addTemplate({ marker: marker, editorKey: _this.editorInstance.getMentionKeyInTemplateStorage() }, preparedTemplate) } _this._mentions[marker] = (0, _extend.extend)({}, _this._getDefaultOptions(), item) }); _this._attachKeyboardHandlers(); _this.addCleanCallback(_this.clean.bind(_assertThisInitialized(_this))); _this.quill.on("text-change", _this.onTextChange.bind(_assertThisInitialized(_this))); return _this } _proto._attachKeyboardHandlers = function() { this.quill.keyboard.addBinding({ key: KEYS.ARROW_UP }, this._moveToItem.bind(this, "prev")); this.quill.keyboard.addBinding({ key: KEYS.ARROW_DOWN }, this._moveToItem.bind(this, "next")); this.quill.keyboard.addBinding({ key: [KEYS.ENTER, KEYS.SPACE] }, this._selectItemHandler.bind(this)); const enterBindings = this.quill.keyboard.bindings[KEYS.ENTER]; enterBindings.unshift(enterBindings.pop()); this.quill.keyboard.addBinding({ key: KEYS.ESCAPE }, this._escapeKeyHandler.bind(this)); this.quill.keyboard.addBinding({ key: [KEYS.ARROW_LEFT, KEYS.ARROW_RIGHT], shiftKey: true }, this._ignoreKeyHandler.bind(this)); this.quill.keyboard.addBinding({ key: NAVIGATION_KEYS }, this._ignoreKeyHandler.bind(this)) }; _proto._moveToItem = function(direction) { const dataSource = this._list.getDataSource(); if (this._isMentionActive && !dataSource.isLoading()) { const $focusedItem = (0, _renderer.default)(this._list.option("focusedElement")); const defaultItemPosition = "next" === direction ? "first" : "last"; let $nextItem = $focusedItem[direction](); $nextItem = $nextItem.length ? $nextItem : this._activeListItems[defaultItemPosition](); this._list.option("focusedElement", (0, _element.getPublicElement)($nextItem)); this._list.scrollToItem($nextItem) } return !this._isMentionActive }; _proto._ignoreKeyHandler = function() { return !this._isMentionActive }; _proto._fitIntoRange = function(value, start, end) { if (value > end) { return start } if (value < start) { return end } return value }; _proto._selectItemHandler = function() { if (this._isMentionActive) { this._list.option("items").length ? this._list.selectItem(this._list.option("focusedElement")) : this._popup.hide() } return !this._isMentionActive }; _proto._escapeKeyHandler = function() { if (this._isMentionActive) { this._popup.hide() } return !this._isMentionActive }; _proto.renderList = function($container, options) { this.compileGetters(this.options); _PopupModule.prototype.renderList.call(this, $container, options) }; _proto.compileGetters = function(_ref) { let { displayExpr: displayExpr, valueExpr: valueExpr } = _ref; this._valueGetter = (0, _data.compileGetter)(displayExpr); this._idGetter = (0, _data.compileGetter)(valueExpr) }; _proto._getListConfig = function(options) { const baseConfig = _PopupModule.prototype._getListConfig.call(this, options); return (0, _extend.extend)(baseConfig, { itemTemplate: this.options.itemTemplate, onContentReady: () => { if (this._hasSearch) { this._popup.repaint(); this._focusFirstElement(); this._hasSearch = false } } }) }; _proto.insertEmbedContent = function() { const markerLength = this._activeMentionConfig.marker.length; const textLength = markerLength + this._searchValue.length; const caretPosition = this.getPosition(); const selectedItem = this._list.option("selectedItem"); const value = { value: this._valueGetter(selectedItem), id: this._idGetter(selectedItem), marker: this._activeMentionConfig.marker, keyInTemplateStorage: this.editorInstance.getMentionKeyInTemplateStorage() }; const Delta = _devextremeQuill.default.import("delta"); const startIndex = Math.max(0, caretPosition - markerLength); const newDelta = (new Delta).retain(startIndex).delete(textLength).insert({ mention: value }).insert(" "); this.quill.updateContents(newDelta); this.quill.setSelection(startIndex + 2) }; _proto._getLastInsertOperation = function(ops) { const lastOperation = ops[ops.length - 1]; const isLastOperationInsert = "insert" in lastOperation; if (isLastOperationInsert) { return lastOperation } const isLastOperationDelete = "delete" in lastOperation; if (isLastOperationDelete && ops.length >= 2) { const penultOperation = ops[ops.length - 2]; const isPenultOperationInsert = "insert" in penultOperation; const isSelectionReplacing = isLastOperationDelete && isPenultOperationInsert; if (isSelectionReplacing) { return penultOperation } } return null }; _proto.onTextChange = function(newDelta, oldDelta, source) { if (source === USER_ACTION) { const lastOperation = newDelta.ops[newDelta.ops.length - 1]; if (this._isMentionActive && this._isPopupVisible) { this._processSearchValue(lastOperation) && this._filterList(this._searchValue) } else { const { ops: ops } = newDelta; const lastInsertOperation = this._getLastInsertOperation(ops); if (lastInsertOperation) { this.checkMentionRequest(lastInsertOperation, ops) } } } }; _proto._processSearchValue = function(operation) { const isInsertOperation = "insert" in operation; if (isInsertOperation) { this._searchValue += operation.insert } else if (!this._searchValue.length || operation.delete > 1) { this._popup.hide(); return false } else { this._searchValue = this._searchValue.slice(0, -1) } return true }; _proto.checkMentionRequest = function(_ref2, ops) { let { insert: insert } = _ref2; const caret = this.quill.getSelection(); if (!insert || !(0, _type.isString)(insert) || !caret || this._isMarkerPartOfText(ops[0].retain)) { return } this._activeMentionConfig = this._mentions[insert]; if (this._activeMentionConfig) { this._updateList(this._activeMentionConfig); const isOnNewLine = caret.index && "\n" === this._getCharByIndex(caret.index - 1); this.savePosition(caret.index + isOnNewLine); this._popup.option("position", this._popupPosition); this._searchValue = ""; this._popup.show() } }; _proto._isMarkerPartOfText = function(retain) { if (!retain || -1 !== ALLOWED_PREFIX_CHARS.indexOf(this._getCharByIndex(retain - 1))) { return false } return true }; _proto._getCharByIndex = function(index) { return this.quill.getContents(index, 1).ops[0].insert }; _proto._updateList = function(_ref3) { let { dataSource: dataSource, displayExpr: displayExpr, valueExpr: valueExpr, itemTemplate: itemTemplate, searchExpr: searchExpr } = _ref3; this.compileGetters({ displayExpr: displayExpr, valueExpr: valueExpr }); this._list.unselectAll(); this._list.option({ dataSource: dataSource, displayExpr: displayExpr, itemTemplate: itemTemplate, searchExpr: searchExpr }) }; _proto._filterList = function(searchValue) { if (!this._isMinSearchLengthExceeded(searchValue)) { this._resetFilter(); return } const searchTimeout = this._activeMentionConfig.searchTimeout; if (searchTimeout) { clearTimeout(this._searchTimer); this._searchTimer = setTimeout(() => { this._search(searchValue) }, searchTimeout) } else { this._search(searchValue) } }; _proto._isMinSearchLengthExceeded = function(searchValue) { return searchValue.length >= this._activeMentionConfig.minSearchLength }; _proto._resetFilter = function() { clearTimeout(this._searchTimer); this._search(null) }; _proto._search = function(searchValue) { this._hasSearch = true; this._list.option("searchValue", searchValue) }; _proto._focusFirstElement = function() { if (!this._list) { return } const $firstItem = this._activeListItems.first(); this._list.option("focusedElement", (0, _element.getPublicElement)($firstItem)); this._list.scrollToItem($firstItem) }; _proto._getPopupConfig = function() { return (0, _extend.extend)(_PopupModule.prototype._getPopupConfig.call(this), { hideOnParentScroll: false, onShown: () => { this._isMentionActive = true; this._hasSearch = false; this._focusFirstElement() }, onHidden: () => { this._list.unselectAll(); this._list.option("focusedElement", null); this._isMentionActive = false; this._search(null) }, focusStateEnabled: false }) }; _proto.clean = function() { Object.keys(this._mentions).forEach(marker => { if (this._mentions[marker].template) { _mention.default.removeTemplate({ marker: marker, editorKey: this.editorInstance.getMentionKeyInTemplateStorage() }) } }) }; _createClass(MentionModule, [{ key: "_isPopupVisible", get: function() { var _this$_popup; return null === (_this$_popup = this._popup) || void 0 === _this$_popup ? void 0 : _this$_popup.option("visible") } }, { key: "_popupPosition", get: function() { const position = this.getPosition(); const { left: mentionLeft, top: mentionTop, height: mentionHeight } = this.quill.getBounds(position ? position - 1 : position); const { left: leftOffset, top: topOffset } = (0, _renderer.default)(this.quill.root).offset(); const positionEvent = _events_engine.default.Event("positionEvent", { pageX: leftOffset + mentionLeft, pageY: topOffset + mentionTop }); return { of: positionEvent, offset: { v: mentionHeight }, my: "top left", at: "top left", collision: { y: "flip", x: "flipfit" } } } }, { key: "_activeListItems", get: function() { return this._list.itemElements().filter(":not(.".concat(DISABLED_STATE_CLASS, ")")) } }]); return MentionModule }(_popup.default) } var _default = MentionModule; exports.default = _default; module.exports = exports.default; module.exports.default = exports.default;