react-bootstrap-front 3.9.2 → 3.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.project +17 -0
- package/CHANGELOG +8 -0
- package/dist/react-bootstrap-front.js +20 -20
- package/package.json +2 -1
- package/src/features/advanced/DropdownWrapper.js +18 -5
- package/src/features/basic/Dropdown.js +2 -2
- package/src/features/draft/ColorPicker.js +2 -2
- package/src/features/draft/FontFamily.js +70 -0
- package/src/features/draft/FontFamily.scss +0 -0
- package/src/features/draft/FontSize.js +8 -4
- package/src/features/draft/Image.js +64 -0
- package/src/features/draft/Image.scss +3 -0
- package/src/features/draft/Inline.js +3 -2
- package/src/features/draft/InputHtml.js +147 -39
- package/src/features/draft/InputHtml.scss +13 -2
- package/src/features/draft/MailMerge.js +54 -0
- package/src/features/draft/MailMerge.scss +0 -0
- package/src/features/draft/index.js +3 -0
- package/src/features/draft/style.scss +4 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-bootstrap-front",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.4",
|
|
4
4
|
"description": "React Bootstrap Components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Bootstrap 4",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"react-router-dom": "^5.0.1"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
+
"@draft-js-plugins/image": "^4.1.2",
|
|
37
38
|
"@ericblade/quagga2": "^1.4.1",
|
|
38
39
|
"@popperjs/core": "^2.5.4",
|
|
39
40
|
"autosize": "^4.0.2",
|
|
@@ -6,22 +6,32 @@ import { Dropdown } from '../basic';
|
|
|
6
6
|
export default class DropdownWrapper extends Component {
|
|
7
7
|
static propTypes = {
|
|
8
8
|
align: PropTypes.string,
|
|
9
|
-
|
|
9
|
+
autoClose: PropTypes.bool,
|
|
10
10
|
className: PropTypes.string,
|
|
11
11
|
myRef: PropTypes.object,
|
|
12
|
+
open: PropTypes.bool,
|
|
12
13
|
tooltip: PropTypes.string,
|
|
13
14
|
trigger: PropTypes.element.isRequired,
|
|
14
15
|
};
|
|
15
16
|
static defaultProps = {
|
|
16
17
|
align: 'bottom-left',
|
|
18
|
+
autoClose: true,
|
|
17
19
|
className: '',
|
|
18
20
|
myRef: null,
|
|
21
|
+
open: false,
|
|
19
22
|
tooltip: '',
|
|
20
23
|
};
|
|
21
24
|
|
|
25
|
+
static getDerivedStateFromProps(state, props) {
|
|
26
|
+
if (state.origOpen !== props.open && state.open !== props.open) {
|
|
27
|
+
return { open: props.open, origOpen: props.open };
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
22
32
|
constructor(props) {
|
|
23
33
|
super(props);
|
|
24
|
-
this.state = { open: false, ref: props.myRef || React.createRef() };
|
|
34
|
+
this.state = { origOpen: this.props.open, open: this.props.open || false, ref: props.myRef || React.createRef() };
|
|
25
35
|
this.onClose = this.onClose.bind(this);
|
|
26
36
|
this.onClick = this.onClick.bind(this);
|
|
27
37
|
}
|
|
@@ -41,14 +51,17 @@ export default class DropdownWrapper extends Component {
|
|
|
41
51
|
render() {
|
|
42
52
|
const clonedTrigger = React.cloneElement(
|
|
43
53
|
this.props.trigger,
|
|
44
|
-
{ref: this.state.ref, onClick: this.onClick, title: this.props.tooltip}
|
|
54
|
+
{ ref: this.state.ref, onClick: this.onClick, title: this.props.tooltip }
|
|
45
55
|
)
|
|
56
|
+
const { onClose } = this;
|
|
46
57
|
return (
|
|
47
58
|
<>
|
|
48
59
|
{clonedTrigger}
|
|
49
60
|
{this.state.open && (
|
|
50
|
-
<Dropdown align={this.props.align} myRef={this.state.ref} onClose={
|
|
51
|
-
<div onClick={this.
|
|
61
|
+
<Dropdown align={this.props.align} myRef={this.state.ref} onClose={onClose}>
|
|
62
|
+
<div onClick={this.props.autoClose && this.onClose}>
|
|
63
|
+
{typeof this.props.children === 'function' ? this.props.children({ onClose }) : this.props.children}
|
|
64
|
+
</div>
|
|
52
65
|
</Dropdown>
|
|
53
66
|
)}
|
|
54
67
|
</>
|
|
@@ -53,8 +53,8 @@ export default class Dropdown extends Component {
|
|
|
53
53
|
|
|
54
54
|
handleClickOutside(event) {
|
|
55
55
|
const domNode = ReactDOM.findDOMNode(this);
|
|
56
|
-
if (!domNode || !domNode.contains(event.target)) {
|
|
57
|
-
if (this.props.myRef && this.props.myRef.current &&this.props.myRef.current.contains(event.target)) {
|
|
56
|
+
if (!domNode || !domNode.contains(event.target)) {
|
|
57
|
+
if (this.props.myRef && this.props.myRef.current && this.props.myRef.current.contains(event.target)) {
|
|
58
58
|
event.preventDefault();
|
|
59
59
|
event.stopPropagation();
|
|
60
60
|
}
|
|
@@ -35,12 +35,12 @@ export default class ColorPicker extends Component {
|
|
|
35
35
|
type="button"
|
|
36
36
|
className={classnames(this.props.className, 'btn btn-light')}
|
|
37
37
|
>
|
|
38
|
-
Color
|
|
38
|
+
{this.props.config.icon ? this.props.config.icon : 'Color' }
|
|
39
39
|
</button>
|
|
40
40
|
);
|
|
41
41
|
const { color } = this.props.currentState;
|
|
42
42
|
return (
|
|
43
|
-
<DropdownWrapper trigger={trigger} className="
|
|
43
|
+
<DropdownWrapper autoClose={false} trigger={trigger} className="ui-inline-wrapper btn-group pl-2" align="bottom-right" myRef={this.state.myRef}>
|
|
44
44
|
<DropdownMenu>
|
|
45
45
|
<ChromePicker color={color} onChangeComplete={this.onChange} />
|
|
46
46
|
</DropdownMenu>
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import classnames from 'classnames';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { DropdownWrapper } from '../advanced'
|
|
5
|
+
import { DropdownMenu, DropdownMenuOption } from '../basic';
|
|
6
|
+
|
|
7
|
+
export default class FontFamily extends Component {
|
|
8
|
+
static propTypes = {
|
|
9
|
+
expanded: PropTypes.bool,
|
|
10
|
+
onExpandEvent: PropTypes.func,
|
|
11
|
+
onChange: PropTypes.func,
|
|
12
|
+
currentState: PropTypes.object,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
constructor(props) {
|
|
16
|
+
super(props);
|
|
17
|
+
this.state = {
|
|
18
|
+
myRef: React.createRef()
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
stopPropagation = (event) => {
|
|
23
|
+
event.stopPropagation();
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
onChange = (family) => {
|
|
27
|
+
const { onChange } = this.props;
|
|
28
|
+
onChange('family', family.hex);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
render() {
|
|
32
|
+
const trigger = (
|
|
33
|
+
<button
|
|
34
|
+
type="button"
|
|
35
|
+
className={classnames(this.props.className, 'btn btn-light')}
|
|
36
|
+
>
|
|
37
|
+
{this.props.icon ? this.props.icon : 'Font' }
|
|
38
|
+
</button>
|
|
39
|
+
);
|
|
40
|
+
const {
|
|
41
|
+
config: { icon, className, dropdownClassName, options, title },
|
|
42
|
+
onChange,
|
|
43
|
+
expanded,
|
|
44
|
+
doCollapse,
|
|
45
|
+
onExpandEvent,
|
|
46
|
+
doExpand,
|
|
47
|
+
translations,
|
|
48
|
+
} = this.props;
|
|
49
|
+
let { currentState: { fontFamily: currentFontFamily } } = this.props;
|
|
50
|
+
let { defaultFontFamily } = this.state;
|
|
51
|
+
currentFontFamily = currentFontFamily ||
|
|
52
|
+
(options && options.indexOf(defaultFontFamily) >= 0 && defaultFontFamily);
|
|
53
|
+
return (
|
|
54
|
+
<DropdownWrapper trigger={trigger} className="ui-inline-wrapper btn-group pl-2" align="bottom-right" myRef={this.state.myRef}>
|
|
55
|
+
<DropdownMenu>
|
|
56
|
+
{options.map((family, index) =>
|
|
57
|
+
(<DropdownMenuOption
|
|
58
|
+
className="rdw-fontfamily-option"
|
|
59
|
+
active={currentFontFamily === family}
|
|
60
|
+
onClick={() => onChange(family)}
|
|
61
|
+
key={index}
|
|
62
|
+
>
|
|
63
|
+
{family}
|
|
64
|
+
</DropdownMenuOption>),
|
|
65
|
+
)}
|
|
66
|
+
</DropdownMenu>
|
|
67
|
+
</DropdownWrapper>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
File without changes
|
|
@@ -23,9 +23,13 @@ export default class FontSize extends Component {
|
|
|
23
23
|
event.stopPropagation();
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
onChange = (
|
|
26
|
+
onChange = (size) => {
|
|
27
27
|
const { onChange } = this.props;
|
|
28
|
-
|
|
28
|
+
let mySize = size.hex;
|
|
29
|
+
if (!isNaN(mySize)) {
|
|
30
|
+
mySize += 'px';
|
|
31
|
+
}
|
|
32
|
+
onChange('size', mySize);
|
|
29
33
|
}
|
|
30
34
|
|
|
31
35
|
render() {
|
|
@@ -34,7 +38,7 @@ export default class FontSize extends Component {
|
|
|
34
38
|
type="button"
|
|
35
39
|
className={classnames(this.props.className, 'btn btn-light')}
|
|
36
40
|
>
|
|
37
|
-
Size
|
|
41
|
+
{this.props.config.icon ? this.props.config.icon : 'Size' }
|
|
38
42
|
</button>
|
|
39
43
|
);
|
|
40
44
|
const {
|
|
@@ -52,7 +56,7 @@ export default class FontSize extends Component {
|
|
|
52
56
|
currentFontSize = currentFontSize ||
|
|
53
57
|
(options && options.indexOf(defaultFontSize) >= 0 && defaultFontSize);
|
|
54
58
|
return (
|
|
55
|
-
<DropdownWrapper trigger={trigger} className="
|
|
59
|
+
<DropdownWrapper trigger={trigger} className="ui-inline-wrapper btn-group pl-2" align="bottom-right" myRef={this.state.myRef}>
|
|
56
60
|
<DropdownMenu>
|
|
57
61
|
{options.map((size, index) =>
|
|
58
62
|
(<DropdownMenuOption
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { Component } from 'react';
|
|
2
|
+
import classnames from 'classnames';
|
|
3
|
+
import PropTypes from 'prop-types';
|
|
4
|
+
import { DropdownWrapper } from '../advanced'
|
|
5
|
+
import { DropdownMenu, InputText } from '../basic';
|
|
6
|
+
|
|
7
|
+
export default class Image extends Component {
|
|
8
|
+
static propTypes = {
|
|
9
|
+
expanded: PropTypes.bool,
|
|
10
|
+
onExpandEvent: PropTypes.func,
|
|
11
|
+
onChange: PropTypes.func,
|
|
12
|
+
currentState: PropTypes.object,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
constructor(props) {
|
|
16
|
+
super(props);
|
|
17
|
+
this.state = {
|
|
18
|
+
url: '',
|
|
19
|
+
myRef: React.createRef()
|
|
20
|
+
}
|
|
21
|
+
this.onChange = this.onChange.bind(this);
|
|
22
|
+
this.onAdd = this.onAdd.bind(this);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
onChange(ev) {
|
|
26
|
+
if (ev) {
|
|
27
|
+
ev.preventDefault();
|
|
28
|
+
ev.stopPropagation();
|
|
29
|
+
const value = ev.target.value;
|
|
30
|
+
const name = ev.target.name;
|
|
31
|
+
this.setState({ [name]: value });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
onAdd(ev) {
|
|
36
|
+
const url = this.state.url;
|
|
37
|
+
this.setState({ url: '' });
|
|
38
|
+
this.props.onChange(url);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
render() {
|
|
42
|
+
const trigger = (
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
className={classnames(this.props.className, 'btn btn-light')}
|
|
46
|
+
>
|
|
47
|
+
{this.props.config.icon ? this.props.config.icon : 'Image'}
|
|
48
|
+
</button>
|
|
49
|
+
);
|
|
50
|
+
//console.log(this.props.currentState, this.props.config);
|
|
51
|
+
return (
|
|
52
|
+
<DropdownWrapper autoClose={false} trigger={trigger} className="ui-inline-wrapper btn-group pl-2" align="bottom-right" myRef={this.state.myRef}>
|
|
53
|
+
{({ onClose }) =>
|
|
54
|
+
<DropdownMenu>
|
|
55
|
+
<>
|
|
56
|
+
<InputText labelTop={true} label="Url" name="url" value={this.state.url} onChange={this.onChange} />
|
|
57
|
+
<button type="button" class="btn btn-xs btn-success" onClick={(ev) => { onClose(); this.onAdd(ev); }}>Ok</button>
|
|
58
|
+
</>
|
|
59
|
+
</DropdownMenu>
|
|
60
|
+
}
|
|
61
|
+
</DropdownWrapper>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -18,15 +18,16 @@ export default class Inline extends Component {
|
|
|
18
18
|
}
|
|
19
19
|
render() {
|
|
20
20
|
const { config, currentState, onChange, translations } = this.props;
|
|
21
|
+
const textalign = currentState.textAlignment || '';
|
|
21
22
|
return (
|
|
22
|
-
<div className={classnames('
|
|
23
|
+
<div className={classnames('ui-inline-wrapper btn-group', config.className)} aria-label="rdw-inline-control">
|
|
23
24
|
{
|
|
24
25
|
config.options
|
|
25
26
|
.map((style, index) =>
|
|
26
27
|
(<button
|
|
27
28
|
key={index}
|
|
28
29
|
onClick={() => onChange(style)}
|
|
29
|
-
className={classnames("btn btn-light", config[style].className, (
|
|
30
|
+
className={classnames("btn btn-light", config[style].className, (textalign === true ||
|
|
30
31
|
(style === 'MONOSPACE' && currentState.CODE)) && "active")}
|
|
31
32
|
title={config[style].title || translations[`components.controls.inline.${style}`]}
|
|
32
33
|
>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React, { Component } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import classnames from 'classnames';
|
|
4
|
+
import { camelCase } from 'lodash';
|
|
4
5
|
import { EditorState, Modifier, CompositeDecorator } from 'draft-js';
|
|
5
6
|
import { Editor } from 'react-draft-wysiwyg';
|
|
6
7
|
import { convertToHTML, convertFromHTML } from 'draft-convert';
|
|
7
8
|
import { htmlToString } from '../helpers';
|
|
8
|
-
import { ColorPicker, Inline, FontSize } from './';
|
|
9
|
+
import { ColorPicker, Inline, FontSize, FontFamily, Image, MailMerge as MailMergeOption } from './';
|
|
9
10
|
import '../../../node_modules/react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
|
|
10
11
|
|
|
11
12
|
const myStyle = {
|
|
@@ -37,23 +38,62 @@ const toHtml = {
|
|
|
37
38
|
case 'color':
|
|
38
39
|
return <span style={{ color: parts[1] }} />;
|
|
39
40
|
case 'fontsize':
|
|
40
|
-
return <span style={{ fontSize: parts[1] }} />;
|
|
41
|
+
return <span style={{ fontSize: parts[1] + 'px' }} />;
|
|
42
|
+
case 'text':
|
|
43
|
+
if (parts[1] === 'align') {
|
|
44
|
+
return <span style={{ textAlign: parts[2] }} />;
|
|
45
|
+
}
|
|
46
|
+
return '';
|
|
41
47
|
default:
|
|
42
48
|
// nothing
|
|
43
49
|
break;
|
|
44
50
|
}
|
|
45
51
|
},
|
|
46
52
|
blockToHTML: block => {
|
|
53
|
+
let styles = {};
|
|
54
|
+
if (block.data && Object.keys(block.data).length > 0) {
|
|
55
|
+
Object.keys(block.data).forEach(key => {
|
|
56
|
+
styles[camelCase(key)] = block.data[key];
|
|
57
|
+
});
|
|
58
|
+
}
|
|
47
59
|
if (block.type === 'PARAGRAPH') {
|
|
48
|
-
return <p />;
|
|
60
|
+
return <p style={styles} />;
|
|
61
|
+
}
|
|
62
|
+
if (block.type === 'atomic') {
|
|
63
|
+
return {
|
|
64
|
+
start: "",
|
|
65
|
+
end: "",
|
|
66
|
+
}
|
|
49
67
|
}
|
|
68
|
+
if (block.type === 'unstyled' && block.text.trim() === '') {
|
|
69
|
+
return <br />;
|
|
70
|
+
}
|
|
71
|
+
if (Object.keys(styles).length > 0) {
|
|
72
|
+
return <p style={styles} />;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
50
75
|
},
|
|
51
76
|
entityToHTML: (entity, originalText) => {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
77
|
+
switch (entity.type) {
|
|
78
|
+
case 'LINK':
|
|
79
|
+
return <a href={entity.data.url}>{originalText}</a>;
|
|
80
|
+
case 'MAILMERGE':
|
|
81
|
+
return <span data-mailmerge="{originalText}">{originalText}</span>;
|
|
82
|
+
case 'IMAGE':
|
|
83
|
+
let otherDatas = {};
|
|
84
|
+
let alt = '';
|
|
85
|
+
if (entity.data.height) {
|
|
86
|
+
otherDatas['height'] = entity.data.height;
|
|
87
|
+
}
|
|
88
|
+
if (entity.data.width) {
|
|
89
|
+
otherDatas['width'] = entity.data.width;
|
|
90
|
+
}
|
|
91
|
+
if (entity.data.alt) {
|
|
92
|
+
alt = entity.data.alt;
|
|
93
|
+
}
|
|
94
|
+
return <img src={entity.data.src} {...otherDatas} alt={alt} />;
|
|
95
|
+
default:
|
|
96
|
+
break;
|
|
57
97
|
}
|
|
58
98
|
return originalText;
|
|
59
99
|
},
|
|
@@ -99,44 +139,65 @@ const fromHtml = {
|
|
|
99
139
|
if (fontSize && fontSize !== '') {
|
|
100
140
|
style.add(`fontsize-${fontSize.replace(/px$/g, '')}`);
|
|
101
141
|
}
|
|
142
|
+
const textAlign = node.style.textAlign;
|
|
143
|
+
if (textAlign && textAlign !== '') {
|
|
144
|
+
style.add(`textalign-${textAlign}`);
|
|
145
|
+
}
|
|
102
146
|
}
|
|
103
147
|
})
|
|
104
148
|
.toOrderedSet();
|
|
105
149
|
return currentStyle;
|
|
106
150
|
},
|
|
107
151
|
htmlToEntity: (nodeName, node, createEntity) => {
|
|
152
|
+
let entity = null;
|
|
108
153
|
if (nodeName === 'a') {
|
|
109
|
-
|
|
154
|
+
entity = createEntity('LINK', 'MUTABLE', { url: node.href });
|
|
110
155
|
}
|
|
111
156
|
if (nodeName === 'span' && node.dataset && node.dataset.mailmerge) {
|
|
112
|
-
|
|
157
|
+
entity = createEntity('MAILMERGE', 'IMMUTABLE', { mailmerge: node.dataset.mailmerge });
|
|
113
158
|
}
|
|
159
|
+
if (nodeName === 'img') {
|
|
160
|
+
entity = createEntity('IMAGE', 'MUTABLE', { src: node.src });
|
|
161
|
+
}
|
|
162
|
+
return entity;
|
|
114
163
|
},
|
|
115
164
|
textToEntity: (text, createEntity) => {
|
|
116
165
|
const result = [];
|
|
117
|
-
text.replace(/\@(\w+)/g, (match, name, offset) => {
|
|
118
|
-
const entityKey = createEntity('AT-MENTION', 'IMMUTABLE', { name });
|
|
119
|
-
result.push({
|
|
120
|
-
entity: entityKey,
|
|
121
|
-
offset,
|
|
122
|
-
length: match.length,
|
|
123
|
-
result: match,
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
166
|
return result;
|
|
127
167
|
},
|
|
128
168
|
htmlToBlock: (nodeName, node) => {
|
|
169
|
+
if (nodeName === "hr" || nodeName === "img") {
|
|
170
|
+
return "atomic";
|
|
171
|
+
}
|
|
129
172
|
if (nodeName === 'blockquote') {
|
|
130
173
|
return {
|
|
131
174
|
type: 'blockquote',
|
|
132
175
|
data: {},
|
|
133
176
|
};
|
|
134
177
|
}
|
|
178
|
+
if (nodeName === 'br') {
|
|
179
|
+
return {
|
|
180
|
+
type: 'PARAGRAPH',
|
|
181
|
+
data: {},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (nodeName === 'p' && node instanceof HTMLElement) {
|
|
185
|
+
let styles = {};
|
|
186
|
+
if (node.style.textAlign) {
|
|
187
|
+
styles['text-align'] = node.style.textAlign;
|
|
188
|
+
}
|
|
189
|
+
if (Object.keys(styles).length > 0) {
|
|
190
|
+
return {
|
|
191
|
+
type: 'PARAGRAPH',
|
|
192
|
+
data: styles,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
135
197
|
},
|
|
136
198
|
};
|
|
137
199
|
|
|
138
200
|
function findMailmergeEntities(contentBlock, callback, contentState) {
|
|
139
|
-
console.log(callback);
|
|
140
201
|
contentBlock.findEntityRanges(
|
|
141
202
|
(character) => {
|
|
142
203
|
const entityKey = character.getEntity();
|
|
@@ -156,7 +217,21 @@ const Mailmerge = (props) => {
|
|
|
156
217
|
);
|
|
157
218
|
};
|
|
158
219
|
|
|
159
|
-
|
|
220
|
+
const MailmergeOptions = (props) => {
|
|
221
|
+
if (props.presetTexts && Array.isArray(props.presetTexts) && props.presetTexts.length > 0) {
|
|
222
|
+
return (
|
|
223
|
+
[<MailMergeOption
|
|
224
|
+
icon={props.presetTextIcon}
|
|
225
|
+
options={props.presetTexts}
|
|
226
|
+
onChangeText={(text) => this.onAddPresetText(text)}
|
|
227
|
+
/>]
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
export default class InputHtml extends Component {
|
|
160
235
|
static propTypes = {
|
|
161
236
|
id: PropTypes.string,
|
|
162
237
|
name: PropTypes.string.isRequired,
|
|
@@ -169,8 +244,14 @@ export default class InputTextarea extends Component {
|
|
|
169
244
|
disabled: PropTypes.bool,
|
|
170
245
|
clearIcon: PropTypes.element,
|
|
171
246
|
toolbarIcon: PropTypes.element,
|
|
247
|
+
sizeIcon: PropTypes.element,
|
|
248
|
+
fontIcon: PropTypes.element,
|
|
249
|
+
colorIcon: PropTypes.element,
|
|
250
|
+
imageIcon: PropTypes.element,
|
|
172
251
|
presetTextIcon: PropTypes.element,
|
|
252
|
+
presetTexts: PropTypes.array,
|
|
173
253
|
onPresetText: PropTypes.func,
|
|
254
|
+
t: PropTypes.func,
|
|
174
255
|
};
|
|
175
256
|
|
|
176
257
|
static defaultProps = {
|
|
@@ -184,8 +265,14 @@ export default class InputTextarea extends Component {
|
|
|
184
265
|
required: false,
|
|
185
266
|
clearIcon: null,
|
|
186
267
|
toolbarIcon: null,
|
|
268
|
+
sizeIcon: null,
|
|
269
|
+
fontIcon: null,
|
|
270
|
+
colorIcon: null,
|
|
271
|
+
imageIcon: null,
|
|
187
272
|
presetTextIcon: null,
|
|
273
|
+
presetTexts: [],
|
|
188
274
|
onPresetText: null,
|
|
275
|
+
t: () => {},
|
|
189
276
|
};
|
|
190
277
|
|
|
191
278
|
constructor(props) {
|
|
@@ -211,9 +298,7 @@ export default class InputTextarea extends Component {
|
|
|
211
298
|
this.onChange = this.onChange.bind(this);
|
|
212
299
|
this.onToolbar = this.onToolbar.bind(this);
|
|
213
300
|
this.onClear = this.onClear.bind(this);
|
|
214
|
-
this.onPresetText = this.onPresetText.bind(this);
|
|
215
301
|
this.onAddPresetText = this.onAddPresetText.bind(this);
|
|
216
|
-
this.onPresetTextClose = this.onPresetTextClose.bind(this);
|
|
217
302
|
}
|
|
218
303
|
|
|
219
304
|
onEditorStateChange(editorState) {
|
|
@@ -249,11 +334,6 @@ export default class InputTextarea extends Component {
|
|
|
249
334
|
this.props.onChange(event);
|
|
250
335
|
}
|
|
251
336
|
|
|
252
|
-
onPresetText() {
|
|
253
|
-
this.props.onPresetText();
|
|
254
|
-
this.setState({ presetText: true });
|
|
255
|
-
}
|
|
256
|
-
|
|
257
337
|
onAddPresetText(contentMore) {
|
|
258
338
|
const editorState = this.state.editorState;
|
|
259
339
|
let content = editorState.getCurrentContent();
|
|
@@ -271,27 +351,54 @@ export default class InputTextarea extends Component {
|
|
|
271
351
|
this.props.onChange(event);
|
|
272
352
|
}
|
|
273
353
|
|
|
274
|
-
onPresetTextClose() {
|
|
275
|
-
this.setState({ presetText: false });
|
|
276
|
-
}
|
|
277
|
-
|
|
278
354
|
render() {
|
|
279
355
|
//<div className={classnames('input-group', (props.error || props.warning) && 'is-invalid')}>
|
|
280
356
|
const { editorState, toolbar } = this.state;
|
|
357
|
+
const { t } = this.props;
|
|
281
358
|
const editorToolbar = {
|
|
282
|
-
options: ['inline','fontSize','colorPicker'],
|
|
359
|
+
options: ['inline','fontSize', 'fontFamily', 'list', 'textAlign', 'colorPicker', 'image'],
|
|
283
360
|
inline: {
|
|
284
361
|
inDropdown: false,
|
|
285
362
|
component: Inline,
|
|
286
|
-
options: ['bold', 'italic', 'underline', 'superscript', 'subscript'],
|
|
363
|
+
options: ['bold', 'italic', 'underline', 'strikethrough', 'monospace', 'superscript', 'subscript'],
|
|
364
|
+
bold: { title: t({ id: 'ui.textarea.draft.toolbar.bold', defaultMessage: 'Bold' }) },
|
|
365
|
+
italic: { title: t({ id: 'ui.textarea.draft.toolbar.italic', defaultMessage: 'Italic' }) },
|
|
366
|
+
underline: { title: t({ id: 'ui.textarea.draft.toolbar.underline', defaultMessage: 'Underline' }) },
|
|
367
|
+
strikethrough: { title: t({ id: 'ui.textarea.draft.toolbar.strikethrough', defaultMessage: 'Underline' }) },
|
|
368
|
+
monospace: { title: t({ id: 'ui.textarea.draft.toolbar.monospace', defaultMessage: 'monospace' }) },
|
|
369
|
+
superscript: { title: t({ id: 'ui.textarea.draft.toolbar.superscript', defaultMessage: 'Superscript' }) },
|
|
370
|
+
subscript: { title: t({ id: 'ui.textarea.draft.toolbar.subscript', defaultMessage: 'Subscript' }) },
|
|
287
371
|
},
|
|
288
372
|
fontSize: {
|
|
289
373
|
component: FontSize,
|
|
290
374
|
options: [7, 8, 9, 10, 11, 12, 14, 16, 18, 24, 30, 36, 48, 60, 72, 96],
|
|
375
|
+
icon: this.props.sizeIcon,
|
|
376
|
+
},
|
|
377
|
+
fontFamily: {
|
|
378
|
+
component: FontFamily,
|
|
379
|
+
options: ['Arial', 'Georgia', 'Impact', 'Tahoma', 'Times New Roman', 'Verdana'],
|
|
380
|
+
},
|
|
381
|
+
list: {
|
|
382
|
+
inDropdown: false,
|
|
383
|
+
component: Inline,
|
|
384
|
+
options: ['unordered', 'ordered'],
|
|
385
|
+
},
|
|
386
|
+
textAlign: {
|
|
387
|
+
inDropdown: false,
|
|
388
|
+
component: Inline,
|
|
389
|
+
options: ['left', 'center', 'right'],
|
|
291
390
|
},
|
|
292
391
|
colorPicker: {
|
|
293
|
-
component: ColorPicker
|
|
294
|
-
|
|
392
|
+
component: ColorPicker,
|
|
393
|
+
icon: this.props.colorIcon,
|
|
394
|
+
},
|
|
395
|
+
image: {
|
|
396
|
+
component: Image,
|
|
397
|
+
inputAccept: 'image/gif,image/jpeg,image/jpg,image/png,image/svg',
|
|
398
|
+
alt: { present: false, mandatory: false },
|
|
399
|
+
previewImage: true,
|
|
400
|
+
icon: this.props.imageIcon,
|
|
401
|
+
},
|
|
295
402
|
};
|
|
296
403
|
return (
|
|
297
404
|
<div className={classnames('form-group', !this.props.labelTop && 'row')}>
|
|
@@ -309,8 +416,9 @@ export default class InputTextarea extends Component {
|
|
|
309
416
|
<Editor
|
|
310
417
|
toolbarHidden={!toolbar}
|
|
311
418
|
toolbar={editorToolbar}
|
|
419
|
+
toolbarCustomButtons={MailmergeOptions(this.props)}
|
|
312
420
|
editorState={editorState}
|
|
313
|
-
wrapperClassName="form-control border-
|
|
421
|
+
wrapperClassName="form-control border-ui h-auto"
|
|
314
422
|
onEditorStateChange={this.onEditorStateChange}
|
|
315
423
|
onBlur={this.onChange}
|
|
316
424
|
/>
|
|
@@ -318,7 +426,7 @@ export default class InputTextarea extends Component {
|
|
|
318
426
|
<button
|
|
319
427
|
type="button"
|
|
320
428
|
className={classnames(
|
|
321
|
-
'btn btn-input btn-outline-
|
|
429
|
+
'btn btn-input btn-outline-ui bg-light',
|
|
322
430
|
this.props.size === 'sm' && `btn-${this.props.size}`
|
|
323
431
|
)}
|
|
324
432
|
onClick={this.onToolbar}
|
|
@@ -328,7 +436,7 @@ export default class InputTextarea extends Component {
|
|
|
328
436
|
<button
|
|
329
437
|
type="button"
|
|
330
438
|
className={classnames(
|
|
331
|
-
'btn btn-input btn-outline-
|
|
439
|
+
'btn btn-input btn-outline-ui bg-light',
|
|
332
440
|
this.props.size === 'sm' && `btn-${this.props.size}`
|
|
333
441
|
)}
|
|
334
442
|
onClick={this.onClear}
|
|
@@ -3,10 +3,21 @@
|
|
|
3
3
|
}
|
|
4
4
|
|
|
5
5
|
.rdw-editor-toolbar {
|
|
6
|
-
padding:
|
|
7
|
-
border:
|
|
6
|
+
padding: 0px;
|
|
7
|
+
border: 0px solid theme-color('secondary-light') !important;
|
|
8
|
+
|
|
9
|
+
.advanced-dropdown-wrapper {
|
|
10
|
+
margin-left: 5px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
button {
|
|
14
|
+
height: 100%;
|
|
15
|
+
}
|
|
8
16
|
}
|
|
9
17
|
|
|
10
18
|
.rbf-mailmerge {
|
|
11
19
|
color: #105a1c;
|
|
20
|
+
}
|
|
21
|
+
.DraftEditor-root {
|
|
22
|
+
overflow: hidden;
|
|
12
23
|
}
|