devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
608 lines (598 loc) • 25.4 kB
JavaScript
/**
* DevExtreme (cjs/ui/html_editor/modules/toolbar.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 _devextremeQuill = _interopRequireDefault(require("devextreme-quill"));
var _renderer = _interopRequireDefault(require("../../../core/renderer"));
var _base = _interopRequireDefault(require("./base"));
var _toolbar = _interopRequireDefault(require("../../toolbar"));
require("../../select_box");
require("../../color_box/color_view");
require("../../number_box");
var _ui = _interopRequireDefault(require("../../widget/ui.errors"));
var _widget_collector = _interopRequireDefault(require("./widget_collector"));
var _iterator = require("../../../core/utils/iterator");
var _type = require("../../../core/utils/type");
var _extend = require("../../../core/utils/extend");
var _message = _interopRequireDefault(require("../../../localization/message"));
var _inflector = require("../../../core/utils/inflector");
var _events_engine = _interopRequireDefault(require("../../../events/core/events_engine"));
var _index = require("../../../events/utils/index");
var _table_helper = require("../utils/table_helper");
var _toolbar_helper = require("../utils/toolbar_helper");
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 ToolbarModule = _base.default;
if (_devextremeQuill.default) {
const TOOLBAR_WRAPPER_CLASS = "dx-htmleditor-toolbar-wrapper";
const TOOLBAR_CLASS = "dx-htmleditor-toolbar";
const TOOLBAR_FORMAT_WIDGET_CLASS = "dx-htmleditor-toolbar-format";
const TOOLBAR_SEPARATOR_CLASS = "dx-htmleditor-toolbar-separator";
const TOOLBAR_MENU_SEPARATOR_CLASS = "dx-htmleditor-toolbar-menu-separator";
const ACTIVE_FORMAT_CLASS = "dx-format-active";
const SELECTED_STATE_CLASS = "dx-state-selected";
const ICON_CLASS = "dx-icon";
const SELECTION_CHANGE_EVENT = "selection-change";
const USER_ACTION = "user";
const SILENT_ACTION = "silent";
const FORMAT_HOTKEYS = {
66: "bold",
73: "italic",
85: "underline"
};
const KEY_CODES = {
b: 66,
i: 73,
u: 85
};
const localize = name => _message.default.format("dxHtmlEditor-".concat((0, _inflector.camelize)(name)));
const localizeValue = (value, name) => {
if ("header" === name) {
const isHeaderValue = (0, _type.isDefined)(value) && false !== value;
return isHeaderValue ? "".concat(localize("heading"), " ").concat(value) : localize("normalText")
}
return localize(value) || value
};
ToolbarModule = function(_BaseModule) {
_inheritsLoose(ToolbarModule, _BaseModule);
function ToolbarModule(quill, options) {
var _this;
_this = _BaseModule.call(this, quill, options) || this;
_this._toolbarWidgets = new _widget_collector.default;
_this._formatHandlers = (0, _toolbar_helper.getFormatHandlers)(_assertThisInitialized(_this));
_this._tableFormats = (0, _table_helper.getTableFormats)(quill);
if ((0, _type.isDefined)(options.items)) {
_this._addCallbacks();
_this._renderToolbar();
const toolbarMenu = _this.toolbarInstance._layoutStrategy._menu;
if (toolbarMenu) {
const _renderPopup = toolbarMenu._renderPopup;
toolbarMenu._renderPopup = function() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key]
}
_renderPopup.apply(toolbarMenu, ...args);
toolbarMenu._popup.on("showing", () => {
_this._updateToolbar(true)
})
}
}
_this.quill.on("editor-change", (eventName, newValue, oldValue, eventSource) => {
const isSilentMode = eventSource === SILENT_ACTION && (0, _type.isEmptyObject)(_this.quill.getFormat());
if (!isSilentMode) {
const isSelectionChanged = eventName === SELECTION_CHANGE_EVENT;
_this._updateToolbar(isSelectionChanged)
}
})
}
return _this
}
var _proto = ToolbarModule.prototype;
_proto._addCallbacks = function() {
this.addCleanCallback(this.clean.bind(this));
this.editorInstance.addContentInitializedCallback(this.updateHistoryWidgets.bind(this))
};
_proto._updateToolbar = function(isSelectionChanged) {
this.updateFormatWidgets(isSelectionChanged);
this.updateHistoryWidgets();
this.updateTableWidgets()
};
_proto._updateFormatWidget = function(name, isApplied, formats) {
const widget = this._toolbarWidgets.getByName(name);
if (!widget) {
return
}
if (isApplied) {
this._markActiveFormatWidget(name, widget, formats)
} else {
this._resetFormatWidget(name, widget);
if (Object.prototype.hasOwnProperty.call(name)) {
delete formats[name]
}
}
this._toggleClearFormatting(isApplied || !(0, _type.isEmptyObject)(formats))
};
_proto._renderToolbar = function() {
const container = this.options.container || this._getContainer();
this._$toolbar = (0, _renderer.default)("<div>").addClass(TOOLBAR_CLASS).appendTo(container);
this._$toolbarContainer = (0, _renderer.default)(container).addClass(TOOLBAR_WRAPPER_CLASS);
_events_engine.default.on(this._$toolbarContainer, (0, _index.addNamespace)("mousedown", this.editorInstance.NAME), e => {
e.target.focus();
e.preventDefault()
});
this._subscribeFormatHotKeys();
this.toolbarInstance = this.editorInstance._createComponent(this._$toolbar, _toolbar.default, this.toolbarConfig);
this.editorInstance.on("optionChanged", _ref => {
let {
name: name
} = _ref;
if ("readOnly" === name || "disabled" === name) {
this.toolbarInstance.option("disabled", this.isInteractionDisabled)
}
})
};
_proto.isMultilineMode = function() {
var _this$options$multili;
return null !== (_this$options$multili = this.options.multiline) && void 0 !== _this$options$multili ? _this$options$multili : true
};
_proto.clean = function() {
this._toolbarWidgets.clear();
if (this._$toolbarContainer) {
this._$toolbarContainer.empty().removeClass(TOOLBAR_WRAPPER_CLASS)
}
};
_proto.repaint = function() {
this.toolbarInstance && this.toolbarInstance.repaint()
};
_proto._getContainer = function() {
const $container = (0, _renderer.default)("<div>");
this.editorInstance.$element().prepend($container);
return $container
};
_proto._detectRenamedOptions = function(item) {
const optionsInfo = [{
newName: "name",
oldName: "formatName"
}, {
newName: "acceptedValues",
oldName: "formatValues"
}];
if ((0, _type.isObject)(item)) {
(0, _iterator.each)(optionsInfo, (index, optionName) => {
if (Object.prototype.hasOwnProperty.call(item, optionName.oldName)) {
_ui.default.log("W1016", optionName.oldName, optionName.newName)
}
})
}
};
_proto._subscribeFormatHotKeys = function() {
this.quill.keyboard.addBinding({
which: KEY_CODES.b,
shortKey: true
}, this._handleFormatHotKey.bind(this));
this.quill.keyboard.addBinding({
which: KEY_CODES.i,
shortKey: true
}, this._handleFormatHotKey.bind(this));
this.quill.keyboard.addBinding({
which: KEY_CODES.u,
shortKey: true
}, this._handleFormatHotKey.bind(this))
};
_proto._handleFormatHotKey = function(range, context, _ref2) {
let {
which: which
} = _ref2;
const formatName = FORMAT_HOTKEYS[which];
this._updateButtonState(formatName)
};
_proto._updateButtonState = function(formatName) {
const formatWidget = this._toolbarWidgets.getByName(formatName);
const currentFormat = this.quill.getFormat();
const formatValue = currentFormat[formatName];
if (formatValue) {
this._markActiveFormatWidget(formatName, formatWidget, currentFormat)
} else {
this._resetFormatWidget(formatName, formatWidget)
}
};
_proto._prepareToolbarItems = function() {
const resultItems = [];
(0, _iterator.each)(this.options.items, (index, item) => {
let newItem;
this._detectRenamedOptions(item);
if ((0, _type.isObject)(item)) {
newItem = this._handleObjectItem(item)
} else if ((0, _type.isString)(item)) {
const buttonItemConfig = this._prepareButtonItemConfig(item);
newItem = this._getToolbarItem(buttonItemConfig)
}
if (newItem) {
resultItems.push(newItem)
}
});
return resultItems
};
_proto._handleObjectItem = function(item) {
if (item.name && item.acceptedValues && this._isAcceptableItem(item.widget, "dxSelectBox")) {
const selectItemConfig = this._prepareSelectItemConfig(item);
return this._getToolbarItem(selectItemConfig)
} else if (item.name && this._isAcceptableItem(item.widget, "dxButton")) {
const defaultButtonItemConfig = this._prepareButtonItemConfig(item.name);
const buttonItemConfig = (0, _extend.extend)(true, defaultButtonItemConfig, item);
return this._getToolbarItem(buttonItemConfig)
} else {
return this._getToolbarItem(item)
}
};
_proto._isAcceptableItem = function(widget, acceptableWidgetName) {
return !widget || widget === acceptableWidgetName
};
_proto._prepareButtonItemConfig = function(name) {
var _ICON_MAP$name;
const iconName = null !== (_ICON_MAP$name = _toolbar_helper.ICON_MAP[name]) && void 0 !== _ICON_MAP$name ? _ICON_MAP$name : name;
const buttonText = (0, _inflector.titleize)(name);
return {
widget: "dxButton",
name: name,
options: {
hint: localize(buttonText),
text: localize(buttonText),
icon: iconName.toLowerCase(),
onClick: this._formatHandlers[name] || (0, _toolbar_helper.getDefaultClickHandler)(this, name),
stylingMode: "text"
},
showText: "inMenu"
}
};
_proto._prepareSelectItemConfig = function(item) {
const {
name: name,
acceptedValues: acceptedValues
} = item;
return (0, _extend.extend)(true, {
widget: "dxSelectBox",
name: name,
options: {
stylingMode: "filled",
dataSource: acceptedValues,
displayExpr: value => localizeValue(value, name),
placeholder: localize(name),
onValueChanged: e => {
if (!this._isReset) {
this._hideAdaptiveMenu();
(0, _toolbar_helper.applyFormat)(this, [name, e.value, USER_ACTION], e.event);
this._setValueSilent(e.component, e.value)
}
}
}
}, item)
};
_proto._hideAdaptiveMenu = function() {
if (this.toolbarInstance.option("overflowMenuVisible")) {
this.toolbarInstance.option("overflowMenuVisible", false)
}
};
_proto._getToolbarItem = function(item) {
const baseItem = {
options: {
onInitialized: e => {
if (item.name) {
e.component.$element().addClass(TOOLBAR_FORMAT_WIDGET_CLASS);
e.component.$element().toggleClass("dx-".concat(item.name.toLowerCase(), "-format"), !!item.name);
this._toolbarWidgets.add(item.name, e.component)
}
},
onDisposing: () => {
this._toolbarWidgets.remove(item.name)
}
}
};
return (0, _extend.extend)(true, {
location: "before",
locateInMenu: "auto"
}, this._getDefaultConfig(item.name), item, baseItem)
};
_proto._getDefaultItemsConfig = function() {
return {
clear: {
options: {
disabled: true
}
},
undo: {
options: {
disabled: true
}
},
redo: {
options: {
disabled: true
}
},
insertRowAbove: {
options: {
disabled: true
}
},
insertRowBelow: {
options: {
disabled: true
}
},
insertHeaderRow: {
options: {
disabled: true
}
},
insertColumnLeft: {
options: {
disabled: true
}
},
insertColumnRight: {
options: {
disabled: true
}
},
deleteRow: {
options: {
disabled: true
}
},
deleteColumn: {
options: {
disabled: true
}
},
deleteTable: {
options: {
disabled: true
}
},
cellProperties: {
options: {
disabled: true
}
},
tableProperties: {
options: {
disabled: true
}
},
separator: {
template: (data, index, element) => {
(0, _renderer.default)(element).addClass(TOOLBAR_SEPARATOR_CLASS)
},
menuItemTemplate: (data, index, element) => {
(0, _renderer.default)(element).addClass(TOOLBAR_MENU_SEPARATOR_CLASS)
}
}
}
};
_proto._getDefaultConfig = function(name) {
return this._getDefaultItemsConfig()[name]
};
_proto.updateHistoryWidgets = function() {
const historyModule = this.quill.history;
if (!historyModule) {
return
}
const {
undo: undoOps,
redo: redoOps
} = historyModule.stack;
this._updateManipulationWidget(this._toolbarWidgets.getByName("undo"), Boolean(undoOps.length));
this._updateManipulationWidget(this._toolbarWidgets.getByName("redo"), Boolean(redoOps.length))
};
_proto.updateTableWidgets = function() {
const table = this.quill.getModule("table");
if (!table) {
return
}
const selection = this.quill.getSelection();
const formats = selection && this.quill.getFormat(selection) || {};
const isTableOperationsEnabled = this._tableFormats.some(format => Boolean(formats[format]));
_table_helper.TABLE_OPERATIONS.forEach(operationName => {
const isInsertTable = "insertTable" === operationName;
const widget = this._toolbarWidgets.getByName(operationName);
this._updateManipulationWidget(widget, isInsertTable ? !isTableOperationsEnabled : isTableOperationsEnabled)
})
};
_proto._updateManipulationWidget = function(widget, isOperationEnabled) {
if (!widget) {
return
}
widget.option("disabled", !isOperationEnabled)
};
_proto.updateFormatWidgets = function(isResetRequired) {
const selection = this.quill.getSelection();
if (!selection) {
return
}
const formats = this.quill.getFormat(selection);
const hasFormats = !(0, _type.isEmptyObject)(formats);
if (!hasFormats || isResetRequired) {
this._resetFormatWidgets()
}
for (const formatName in formats) {
const widgetName = this._getFormatWidgetName(formatName, formats);
const formatWidget = this._toolbarWidgets.getByName(widgetName) || this._toolbarWidgets.getByName(formatName);
if (!formatWidget) {
continue
}
this._markActiveFormatWidget(formatName, formatWidget, formats)
}
this._toggleClearFormatting(hasFormats || selection.length > 1)
};
_proto._markActiveFormatWidget = function(name, widget, formats) {
if (this._isColorFormat(name)) {
this._updateColorWidget(name, formats[name])
}
if ("value" in widget.option()) {
this._setValueSilent(widget, formats[name])
} else {
widget.$element().addClass(ACTIVE_FORMAT_CLASS);
widget.$element().addClass(SELECTED_STATE_CLASS)
}
};
_proto._toggleClearFormatting = function(hasFormats) {
const clearWidget = this._toolbarWidgets.getByName("clear");
if (clearWidget) {
clearWidget.option("disabled", !hasFormats)
}
};
_proto._isColorFormat = function(name) {
return "color" === name || "background" === name
};
_proto._updateColorWidget = function(name, color) {
const formatWidget = this._toolbarWidgets.getByName(name);
if (!formatWidget) {
return
}
formatWidget.$element().find(".".concat(ICON_CLASS)).css("borderBottomColor", color || "transparent")
};
_proto._getFormatWidgetName = function(name, formats) {
let widgetName;
switch (name) {
case "align":
widgetName = name + (0, _inflector.titleize)(formats[name]);
break;
case "list":
widgetName = formats[name] + (0, _inflector.titleize)(name);
break;
case "code-block":
widgetName = "codeBlock";
break;
case "script":
widgetName = formats[name] + name;
break;
case "imageSrc":
widgetName = "image";
break;
default:
widgetName = name
}
return widgetName
};
_proto._setValueSilent = function(widget, value) {
this._isReset = true;
widget.option("value", value);
this._isReset = false
};
_proto._resetFormatWidgets = function() {
this._toolbarWidgets.each((name, widget) => {
this._resetFormatWidget(name, widget)
})
};
_proto._resetFormatWidget = function(name, widget) {
widget.$element().removeClass(ACTIVE_FORMAT_CLASS);
widget.$element().removeClass(SELECTED_STATE_CLASS);
if (this._isColorFormat(name)) {
this._updateColorWidget(name)
}
if ("clear" === name) {
widget.option("disabled", true)
}
if ("dxSelectBox" === widget.NAME) {
this._setValueSilent(widget, null)
}
};
_proto.addClickHandler = function(name, handler) {
this._formatHandlers[name] = handler;
const formatWidget = this._toolbarWidgets.getByName(name);
if (formatWidget && "dxButton" === formatWidget.NAME) {
formatWidget.option("onClick", handler)
}
};
_createClass(ToolbarModule, [{
key: "toolbarConfig",
get: function() {
return {
dataSource: this._prepareToolbarItems(),
disabled: this.isInteractionDisabled,
menuContainer: this._$toolbarContainer,
multiline: this.isMultilineMode()
}
}
}, {
key: "isInteractionDisabled",
get: function() {
return this.editorInstance.option("readOnly") || this.editorInstance.option("disabled")
}
}]);
return ToolbarModule
}(_base.default)
}
var _default = ToolbarModule;
exports.default = _default;
module.exports = exports.default;
module.exports.default = exports.default;