Custom Text Editor Buttons
Text editors have built-in action buttons that allow users to open a drop-down menu, increase, decrease, or nullify the value, and perform other actions. To add custom action buttons for different scenarios, use the buttons[] array.
Each object in the buttons[] array should have the name field—the button's identifier. In addition, specify the button's location relative to the input text field and options of the Button component used as the action button.
The buttons[] array also accepts string values—the names of built-in buttons. Declare them in the order the buttons should have in the component. String and object declarations can be used in the same array.
The USD/EUR rate is taken from www.wikipedia.org and is relevant on 14 April 2021
Feel free to share demo-related thoughts here.
If you have technical questions, please create a support ticket in the DevExpress Support Center.
Thank you for the feedback!
If you have technical questions, please create a support ticket in the DevExpress Support Center.
Backend API
import React from 'react';
import { TextBox, Button as TextBoxButton } from 'devextreme-react/text-box';
import { NumberBox, Button as NumberBoxButton } from 'devextreme-react/number-box';
import { DateBox, Button as DateBoxButton } from 'devextreme-react/date-box';
const millisecondsInDay = 24 * 60 * 60 * 1000;
const currencyLabel = { 'aria-label': 'Multi Currency' };
const dateBoxLabel = { 'aria-label': 'Date' };
const passwordLabel = { 'aria-label': 'Password' };
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
passwordMode: 'password',
currencyFormat: '$ #.##',
currencyValue: 14500.55,
dateValue: new Date().getTime(),
};
this.passwordButton = {
icon: '../../../../images/icons/eye.png',
type: 'default',
onClick: () => {
this.setState({
passwordMode: (this.state.passwordMode === 'text' ? 'password' : 'text'),
});
},
};
this.currencyButton = {
text: '€',
stylingMode: 'text',
width: 32,
elementAttr: {
class: 'currency',
},
onClick: (e) => {
if (e.component.option('text') === '$') {
e.component.option('text', '€');
this.setState({
currencyFormat: '$ #.##',
currencyValue: this.state.currencyValue / 0.836,
});
} else {
e.component.option('text', '$');
this.setState({
currencyFormat: '€ #.##',
currencyValue: this.state.currencyValue * 0.836,
});
}
},
};
this.changeCurrency = this.changeCurrency.bind(this);
this.todayButton = {
text: 'Today',
onClick: () => {
this.setState({
dateValue: new Date().getTime(),
});
},
};
this.prevDateButton = {
icon: 'spinprev',
stylingMode: 'text',
onClick: () => {
this.setState({
dateValue: this.state.dateValue - millisecondsInDay,
});
},
};
this.nextDateButton = {
icon: 'spinnext',
stylingMode: 'text',
onClick: () => {
this.setState({
dateValue: this.state.dateValue + millisecondsInDay,
});
},
};
this.onDateChanged = (e) => {
this.setState({
dateValue: e.value,
});
};
}
changeCurrency(data) {
this.setState({
currencyValue: data.value,
});
}
render() {
return (
<React.Fragment>
<div className="dx-fieldset">
<div className="dx-field">
<div className="dx-field-label">Password TextBox</div>
<div className="dx-field-value">
<TextBox
placeholder="password"
stylingMode="filled"
defaultValue="password"
inputAttr={passwordLabel}
mode={this.state.passwordMode}>
<TextBoxButton
name="password"
location="after"
options={this.passwordButton}
/>
</TextBox>
</div>
</div>
<div className="dx-field">
<div className="dx-field-label">Multi-currency NumberBox</div>
<div className="dx-field-value">
<NumberBox
showClearButton={true}
showSpinButtons={true}
format={this.state.currencyFormat}
value={this.state.currencyValue}
inputAttr={currencyLabel}
onValueChanged={this.changeCurrency}>
<NumberBoxButton
name="currency"
location="after"
options={this.currencyButton}
/>
<NumberBoxButton name="clear" />
<NumberBoxButton name="spins" />
</NumberBox>
</div>
</div>
<div className="dx-field">
<div className="dx-field-label">Advanced DateBox</div>
<div className="dx-field-value">
<DateBox value={this.state.dateValue}
stylingMode="outlined"
inputAttr={dateBoxLabel}
onValueChanged={this.onDateChanged}>
<DateBoxButton
name="today"
location="before"
options={this.todayButton}
/>
<DateBoxButton
name="prevDate"
location="before"
options={this.prevDateButton}
/>
<DateBoxButton
name="nextDate"
location="after"
options={this.nextDateButton}
/>
<DateBoxButton name="dropDown" />
</DateBox>
</div>
</div>
</div>
</React.Fragment>
);
}
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';
ReactDOM.render(
<App />,
document.getElementById('app'),
);
<!DOCTYPE html>
<html>
<head>
<title>DevExtreme Demo</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/23.1.5/css/dx.light.css" />
<link rel="stylesheet" type="text/css" href="styles.css" />
<script src="https://unpkg.com/core-js@2.6.12/client/shim.min.js"></script>
<script src="https://unpkg.com/systemjs@0.21.3/dist/system.js"></script>
<script type="text/javascript" src="config.js"></script>
<script type="text/javascript">
System.import("./index.js");
</script>
</head>
<body class="dx-viewport">
<div class="demo-container">
<div id="app"></div>
</div>
</body>
</html>
.dx-fieldset {
min-height: 560px;
width: 560px;
margin: 0 auto;
}
.currency {
min-width: 32px;
}
.dx-button.currency .dx-button-content {
font-size: 120%;
padding-left: 5px;
padding-right: 5px;
}
window.exports = window.exports || {};
window.config = {
transpiler: 'plugin-babel',
meta: {
'devextreme/localization.js': {
'esModule': true,
},
},
paths: {
'npm:': 'https://unpkg.com/',
},
defaultExtension: 'js',
map: {
'react': 'npm:react@17.0.2/umd/react.development.js',
'react-dom': 'npm:react-dom@17.0.2/umd/react-dom.development.js',
'prop-types': 'npm:prop-types@15.8.1/prop-types.js',
'rrule': 'npm:rrule@2.6.4/dist/es5/rrule.js',
'luxon': 'npm:luxon@1.28.1/build/global/luxon.min.js',
'es6-object-assign': 'npm:es6-object-assign@1.1.0',
'devextreme': 'npm:devextreme@23.1.6/cjs',
'devextreme-react': 'npm:devextreme-react@23.1.6',
'jszip': 'npm:jszip@3.7.1/dist/jszip.min.js',
'devextreme-quill': 'npm:devextreme-quill@1.6.2/dist/dx-quill.min.js',
'devexpress-diagram': 'npm:devexpress-diagram@2.2.2/dist/dx-diagram.js',
'devexpress-gantt': 'npm:devexpress-gantt@4.1.49/dist/dx-gantt.js',
'@devextreme/runtime': 'npm:@devextreme/runtime@3.0.12',
'inferno': 'npm:inferno@7.4.11/dist/inferno.min.js',
'inferno-compat': 'npm:inferno-compat/dist/inferno-compat.min.js',
'inferno-create-element': 'npm:inferno-create-element@7.4.11/dist/inferno-create-element.min.js',
'inferno-dom': 'npm:inferno-dom/dist/inferno-dom.min.js',
'inferno-hydrate': 'npm:inferno-hydrate@7.4.11/dist/inferno-hydrate.min.js',
'inferno-clone-vnode': 'npm:inferno-clone-vnode/dist/inferno-clone-vnode.min.js',
'inferno-create-class': 'npm:inferno-create-class/dist/inferno-create-class.min.js',
'inferno-extras': 'npm:inferno-extras/dist/inferno-extras.min.js',
// SystemJS plugins
'plugin-babel': 'npm:systemjs-plugin-babel@0.0.25/plugin-babel.js',
'systemjs-babel-build': 'npm:systemjs-plugin-babel@0.0.25/systemjs-babel-browser.js',
// Prettier
'prettier/standalone': 'npm:prettier@2.8.4/standalone.js',
'prettier/parser-html': 'npm:prettier@2.8.4/parser-html.js',
},
packages: {
'devextreme': {
defaultExtension: 'js',
},
'devextreme-react': {
main: 'index.js',
},
'devextreme/events/utils': {
main: 'index',
},
'devextreme/events': {
main: 'index',
},
'es6-object-assign': {
main: './index.js',
defaultExtension: 'js',
},
},
packageConfigPaths: [
'npm:@devextreme/*/package.json',
'npm:@devextreme/runtime@3.0.12/inferno/package.json',
],
babelOptions: {
sourceMaps: false,
stage0: true,
react: true,
},
};
System.config(window.config);