devexpress-reporting
Version:
DevExpress Reporting provides the capability to develop a reporting application to create and customize reports.
356 lines (355 loc) • 16.2 kB
JavaScript
/**
* DevExpress HTML/JS Reporting (rich-edit\utils\_toolbar.js)
* Version: 24.2.6
* Build date: Mar 18, 2025
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* License: https://www.devexpress.com/Support/EULAs/universal.xml
*/
import { $dx, extend, getParentContainer } from '@devexpress/analytics-core/analytics-internal';
import { Disposable, getLocalization } from '@devexpress/analytics-core/analytics-utils';
import { PopupComponentBase } from '@devexpress/analytics-core/analytics-widgets-internal';
import * as ko from 'knockout';
import { Locker } from '../../common/utils/_locker';
import { events, ToolbarActionId, ToolbarGroupId } from '../customizeToolbarActions';
import { getRichEditInstance } from '../instance';
class ComponentCommon extends Disposable {
_updateStateInternal(commandIdMap) {
if (!commandIdMap || this.needUpdateState(commandIdMap))
this.locker.lock(() => this.updateState());
}
_executeCommand(value, command) {
if (this.locker.isUpdate)
return;
if (this.action)
this.action(this.options.richEditPublic, value);
else
this.options.executeCommand(command, this.hasCustomValue() ? this.getConverter().toModel(value) : undefined, true);
this._updateStateInternal();
}
executeCommand(value, command) {
this._executeCommand(value, command);
}
constructor(options, info) {
super();
this.itemKey = 'value';
this.locker = new Locker();
this.options = options;
this.value = ko.observable();
if (info.template)
this.template = info.template;
this.init(info);
}
unwrapItem(item) {
return extend({}, item, { command: item.command });
}
getConverter() {
return {
toModel: (newValue) => newValue,
fromModel: (newValue) => newValue,
};
}
init(info) {
if (info) {
this.id = info.id;
this.text = info.text;
this.visible = info.visible === false ? false : true;
if (info.action)
this.action = info.action;
}
}
hasCustomValue() {
return false;
}
}
export class CustomComponent extends ComponentCommon {
updateState() { }
needUpdateState(_commandIdMap) {
return true;
}
}
export class Component extends ComponentCommon {
needUpdateState(commandIdMap) {
return !!commandIdMap[this._command];
}
init(info) {
super.init(info);
this.item = this.unwrapItem(info);
this._command = this.item.command;
if (this._command && !this.options.richEditPublic.isDisposed) {
const state = this.options.richEditPublic.getCommandState(this._command);
if (state.enabled) {
this.value(this.getConverter().fromModel(state.value));
this.locker.lock(() => this._updateStateInternal());
}
}
this._disposables.push(this.value.subscribe((value) => this._executeCommand(value, this.item.command)));
}
updateState() {
if (this._command && this.hasCustomValue()) {
const state = this.options.richEditPublic.getCommandState(this._command);
if (state.enabled)
this.value(this.getConverter().fromModel(state.value));
}
}
}
export class ComponentButtonGroup extends ComponentCommon {
constructor(options, info) {
super(options, info);
this.itemKey = 'command';
this.template = this.template || 'dxrd-rich-edit-toolbar-button-group';
}
needUpdateState(commandIdMap) {
return this.items.some(item => !!commandIdMap[this.getCommand(item)]);
}
init(info) {
super.init(info);
this.selectionMode = info.selectionMode || 'single';
this.selectedItems = ko.observableArray([]);
this.items = info.items.map(item => this.unwrapItem(item));
this._disposables.push(this.selectedItems.subscribe((changes) => {
this.onSelectItems(changes);
}, null, 'arrayChange'));
}
onSelectItems(changes) {
changes.forEach((change) => {
if (this.selectionMode === 'multiple' && change.status === 'deleted' || change.status === 'added') {
this._executeCommand(change.value.value, change.value.command);
}
});
}
getCommand(item) {
return item.command;
}
updateState() {
const selected = [];
this.items.forEach(item => {
const command = this.getCommand(item);
if (item.command === command) {
const commandState = this.options.richEditPublic.getCommandState(command);
if (commandState.enabled) {
const value = commandState.value;
if (!!value)
selected.push(item);
}
}
});
this.selectedItems(selected);
}
}
export class ComponentButton extends Component {
constructor(options, info) {
super(options, info);
this.icon = info.icon;
this.hint = info.hint;
this.template = this.template || 'dxrd-button-with-template';
}
clickAction() {
this._executeCommand(undefined, this.item.command);
}
}
export class ComponentComboBox extends Component {
constructor(options, info) {
super(options, info);
this.validationRules = [];
this.supportCustomValue = false;
this.items = info.items;
this.template = this.template || 'dxrd-rich-toolbar-combobox';
}
hasCustomValue() { return true; }
}
export class ComponentFontSizeComboBox extends ComponentComboBox {
constructor() {
super(...arguments);
this.validationRules = [{ type: 'numeric' }];
this.supportCustomValue = true;
}
}
export class ComponentColorPicker extends Component {
constructor(options, info) {
super(options, info);
this.template = this.template || 'dxrd-richEdit-toolbar-colorpicker';
}
getConverter() {
return {
fromModel: (newValue) => {
switch (newValue) {
case 'Auto':
case 'NoColor':
case undefined:
return this.item.defaultValue;
default: {
const color = newValue;
return `rgb(${[
parseInt(color.substr(1, 2), 16),
parseInt(color.substr(3, 2), 16),
parseInt(color.substr(5, 2), 16)
].join(', ')})`;
}
}
},
toModel: (newValue) => newValue
};
}
hasCustomValue() { return true; }
}
export class ComponentCollection {
constructor(id, title = '', visible = true, template = 'dxrd-rich-edit-toolbar-component-collection') {
this.id = id;
this.title = title;
this.visible = visible;
this.template = template;
this.showTitle = () => this.title;
}
}
export class ToolbarSurface extends Disposable {
_initComponentCollection(items, options) {
return items.map((item) => {
const component = new ComponentCollection(item.id, item.title, item.visible, item.template);
component.items = this._initComponents(item.items, options);
return this._extendTemplateOptions(item, component);
});
}
_initComponents(items, options) {
return items.map((item) => {
let component;
switch (item.actionType) {
case 'ButtonGroup':
component = new ComponentButtonGroup(options, item);
break;
case 'Button':
component = new ComponentButton(options, item);
break;
case 'ComboBox':
component = item.id === ToolbarActionId.FontSizeComboBox ? new ComponentFontSizeComboBox(options, item) : new ComponentComboBox(options, item);
break;
case 'ColorBox':
component = new ComponentColorPicker(options, item);
break;
default:
component = new CustomComponent(options, item);
}
return this._extendTemplateOptions(item, component);
});
}
_extendTemplateOptions(item, el) {
if (item.template)
return extend(true, {}, el, item);
return el;
}
constructor(options) {
super();
this._popover = new PopupComponentBase();
this._getDefaultItems = (fonts) => {
return [
{
id: ToolbarGroupId.AlignmentAndFormatting,
items: [
{
id: ToolbarActionId.ParagraphAlignmentButtonGroup,
actionType: 'ButtonGroup',
selectionMode: 'single',
_customComponent: 'alignmentButtonGroup',
items: [
{ actionType: 'Button', command: getRichEditInstance().HomeTabCommandId.ToggleParagraphAlignmentLeft, icon: ' dxre-icon-AlignLeft', hint: getLocalization('Align Text Left', 'XtraRichEditStringId.MenuCmd_ParagraphAlignmentLeft') },
{ actionType: 'Button', command: getRichEditInstance().HomeTabCommandId.ToggleParagraphAlignmentCenter, icon: ' dxre-icon-AlignCenter', hint: getLocalization('Center', 'XtraRichEditStringId.MenuCmd_ParagraphAlignmentCenter') },
{ actionType: 'Button', command: getRichEditInstance().HomeTabCommandId.ToggleParagraphAlignmentRight, icon: ' dxre-icon-AlignRight', hint: getLocalization('Align Text Right', 'XtraRichEditStringId.MenuCmd_ParagraphAlignmentRight') }
]
},
{
id: ToolbarActionId.HyperlinkButton, actionType: 'Button', command: getRichEditInstance().InsertTabCommandId.ShowHyperlinkDialog, icon: ' dxre-icon-Hyperlink', hint: getLocalization('Hyperlink...', 'XtraRichEditStringId.MenuCmd_Hyperlink'),
},
{
id: ToolbarActionId.ClearFormattingButton, actionType: 'Button', command: getRichEditInstance().HomeTabCommandId.ClearFormatting, icon: ' dxre-icon-ClearFormatting', hint: getLocalization('Clear Formatting', 'XtraRichEditStringId.MenuCmd_ClearFormatting')
}
],
},
{
id: ToolbarGroupId.FontStyleAndCase,
items: [
{
id: ToolbarActionId.FontStyleButtonGroup,
actionType: 'ButtonGroup',
selectionMode: 'multiple',
items: [
{ actionType: 'Button', command: getRichEditInstance().HomeTabCommandId.ToggleFontBold, icon: ' dxre-icon-Bold', hint: getLocalization('Bold', 'System.Drawing.Font.Bold') },
{ actionType: 'Button', command: getRichEditInstance().HomeTabCommandId.ToggleFontItalic, icon: ' dxre-icon-Italic', hint: getLocalization('Italic', 'System.Drawing.Font.Italic') },
{ actionType: 'Button', command: getRichEditInstance().HomeTabCommandId.ToggleFontUnderline, icon: ' dxre-icon-Underline', hint: getLocalization('Underline', 'System.Drawing.Font.Underline') },
{ actionType: 'Button', command: getRichEditInstance().HomeTabCommandId.ToggleFontStrikeout, icon: ' dxre-icon-Strikeout', hint: getLocalization('Strikeout', 'System.Drawing.Font.Strikeout') }
]
},
{ id: ToolbarActionId.ToggleCaseButton, actionType: 'Button', command: getRichEditInstance().HomeTabCommandId.CapitalizationToggleCase, icon: ' dxre-icon-ChangeTextCase', hint: getLocalization('tOGGLE cASE', 'XtraRichEditStringId.MenuCmd_ToggleTextCase') }
]
},
{
id: ToolbarGroupId.FontSize,
title: getLocalization('Font Size', 'XtraRichEditStringId.MenuCmd_ChangeFontSize'),
items: [{ id: ToolbarActionId.FontSizeComboBox, actionType: 'ComboBox', command: getRichEditInstance().HomeTabCommandId.ChangeFontSize, items: [8, 9, 10, 11, 12, 14, 16, 18, 20, 24, 26, 28, 36, 48, 72] }],
},
{
id: ToolbarGroupId.Font,
title: getLocalization('Font', 'XtraRichEditStringId.MenuCmd_ChangeFontName'),
items: [{ id: ToolbarActionId.FontComboBox, actionType: 'ComboBox', command: getRichEditInstance().HomeTabCommandId.ChangeFontName, items: fonts }],
},
{
id: ToolbarGroupId.FontColor,
title: getLocalization('Font Color', 'XtraRichEditStringId.MenuCmd_ChangeFontColor'),
items: [{ id: ToolbarActionId.FontColorBox, actionType: 'ColorBox', command: getRichEditInstance().HomeTabCommandId.ChangeFontForeColor, defaultValue: 'rgb(0, 0, 0)' }],
},
{
id: ToolbarGroupId.BackgroundColor,
title: getLocalization('Background Color', 'DevExpress.XtraReports.UI.XRRichTextBoxBase.BackColor'),
items: [{ id: ToolbarActionId.BackgroundColorBox, actionType: 'ColorBox', command: getRichEditInstance().HomeTabCommandId.ChangeFontHighlightColor, defaultValue: 'rgb(255, 255, 255)' }]
}
];
};
this.onContentReady = this._popover.onContentReady;
this.getPositionTarget = (element) => {
return $dx(element).closest('.' + this.parentClass).closest('.dxrd-control')[0];
};
this.hideOnOutsideClick = (e) => {
if (this._popover.hideOnOutsideClick(e)) {
this.visible(false);
return false;
}
return true;
};
this.template = 'dxrd-richedit-toolbar';
this.parentClass = 'dxrd-rich-surface';
this.getPopupContainer = getParentContainer;
this.componentCollection = [];
this.visible = options.visible;
const toolbarItems = extend(true, [], this._getDefaultItems(options.fonts));
const getById = (itemId) => {
let matchedItem;
const group = toolbarItems.filter(item => {
if (matchedItem)
return false;
if (item.id === itemId)
return true;
matchedItem = item.items.filter(x => x.id === itemId)[0];
})[0];
return matchedItem || group;
};
events().call('customizeToolbarActions', { actions: toolbarItems, getById: getById });
this.componentCollection = this._initComponentCollection(toolbarItems, options);
if ((this.componentCollection || []).every(component => !component.visible))
this.visible = ko.observable(false);
}
onCommandStateChanged(sender, args) {
if (args.commands) {
const commandIdMap = {};
args.commands.forEach(commandId => commandIdMap[commandId] = true);
for (const group of this.componentCollection) {
if (group.items) {
for (const item of group.items) {
if (item._updateStateInternal) {
item._updateStateInternal(commandIdMap);
}
}
}
}
}
else
this.componentCollection.forEach(group => (group.items || []).forEach(item => item._updateStateInternal && item._updateStateInternal()));
}
}