react-bootstrap-front 3.9.0 → 3.9.5
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/CHANGELOG +21 -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/advanced/ResponsiveModalInner.js +1 -1
- package/src/features/basic/Dropdown.js +2 -2
- package/src/features/basic/InputJson.js +0 -1
- 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 +177 -36
- package/src/features/draft/InputHtml.scss +17 -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 -0
- package/src/features/list/DefaultHeader.jsx +4 -4
- package/src/features/list/MobileList.jsx +1 -1
- package/src/features/page/MobilePage.jsx +1 -1
- package/src/styles/mixins/container.scss +22 -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.5",
|
|
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
|
</>
|
|
@@ -298,7 +298,7 @@ export default class ResponsiveModalInner extends Component {
|
|
|
298
298
|
</div>
|
|
299
299
|
}
|
|
300
300
|
</div>
|
|
301
|
-
{this.props.buttons && (
|
|
301
|
+
{(this.props.buttons || this.props.help || this.props.saving) && (
|
|
302
302
|
<div className={classnames('modal-footer', this.props.modalClassName)}>
|
|
303
303
|
{(this.props.help || this.props.saving ) &&
|
|
304
304
|
<div className="modal-left-buttons">
|
|
@@ -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
|
}
|
|
@@ -8,7 +8,6 @@ import { getFieldId } from '../helpers';
|
|
|
8
8
|
export const InputJson = props => {
|
|
9
9
|
let myId = getFieldId(props.name, props.id);
|
|
10
10
|
const data = JSON.parse(props.value) || {};
|
|
11
|
-
console.log(data, props.value);
|
|
12
11
|
return (
|
|
13
12
|
<InputGroup {...props} id={myId}>
|
|
14
13
|
{props.prepend && props.prepend !== '' && (
|
|
@@ -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 {
|
|
4
|
+
import { camelCase } from 'lodash';
|
|
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,20 +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
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (block.type === 'unstyled' && block.text.trim() === '') {
|
|
69
|
+
return <br />;
|
|
49
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
|
-
|
|
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;
|
|
54
97
|
}
|
|
55
98
|
return originalText;
|
|
56
99
|
},
|
|
@@ -96,40 +139,99 @@ const fromHtml = {
|
|
|
96
139
|
if (fontSize && fontSize !== '') {
|
|
97
140
|
style.add(`fontsize-${fontSize.replace(/px$/g, '')}`);
|
|
98
141
|
}
|
|
142
|
+
const textAlign = node.style.textAlign;
|
|
143
|
+
if (textAlign && textAlign !== '') {
|
|
144
|
+
style.add(`textalign-${textAlign}`);
|
|
145
|
+
}
|
|
99
146
|
}
|
|
100
147
|
})
|
|
101
148
|
.toOrderedSet();
|
|
102
149
|
return currentStyle;
|
|
103
150
|
},
|
|
104
151
|
htmlToEntity: (nodeName, node, createEntity) => {
|
|
152
|
+
let entity = null;
|
|
105
153
|
if (nodeName === 'a') {
|
|
106
|
-
|
|
154
|
+
entity = createEntity('LINK', 'MUTABLE', { url: node.href });
|
|
155
|
+
}
|
|
156
|
+
if (nodeName === 'span' && node.dataset && node.dataset.mailmerge) {
|
|
157
|
+
entity = createEntity('MAILMERGE', 'IMMUTABLE', { mailmerge: node.dataset.mailmerge });
|
|
107
158
|
}
|
|
159
|
+
if (nodeName === 'img') {
|
|
160
|
+
entity = createEntity('IMAGE', 'MUTABLE', { src: node.src });
|
|
161
|
+
}
|
|
162
|
+
return entity;
|
|
108
163
|
},
|
|
109
164
|
textToEntity: (text, createEntity) => {
|
|
110
165
|
const result = [];
|
|
111
|
-
text.replace(/\@(\w+)/g, (match, name, offset) => {
|
|
112
|
-
const entityKey = createEntity('AT-MENTION', 'IMMUTABLE', { name });
|
|
113
|
-
result.push({
|
|
114
|
-
entity: entityKey,
|
|
115
|
-
offset,
|
|
116
|
-
length: match.length,
|
|
117
|
-
result: match,
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
166
|
return result;
|
|
121
167
|
},
|
|
122
168
|
htmlToBlock: (nodeName, node) => {
|
|
169
|
+
if (nodeName === "hr" || nodeName === "img") {
|
|
170
|
+
return "atomic";
|
|
171
|
+
}
|
|
123
172
|
if (nodeName === 'blockquote') {
|
|
124
173
|
return {
|
|
125
174
|
type: 'blockquote',
|
|
126
175
|
data: {},
|
|
127
176
|
};
|
|
128
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;
|
|
129
197
|
},
|
|
130
198
|
};
|
|
131
199
|
|
|
132
|
-
|
|
200
|
+
function findMailmergeEntities(contentBlock, callback, contentState) {
|
|
201
|
+
contentBlock.findEntityRanges(
|
|
202
|
+
(character) => {
|
|
203
|
+
const entityKey = character.getEntity();
|
|
204
|
+
const found = entityKey !== null && contentState.getEntity(entityKey).getType() === 'MAILMERGE';
|
|
205
|
+
return found;
|
|
206
|
+
},
|
|
207
|
+
callback
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const Mailmerge = (props) => {
|
|
212
|
+
const {mailmerge} = props.contentState.getEntity(props.entityKey).getData();
|
|
213
|
+
return (
|
|
214
|
+
<a href={mailmerge}>
|
|
215
|
+
{props.children}
|
|
216
|
+
</a>
|
|
217
|
+
);
|
|
218
|
+
};
|
|
219
|
+
|
|
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 {
|
|
133
235
|
static propTypes = {
|
|
134
236
|
id: PropTypes.string,
|
|
135
237
|
name: PropTypes.string.isRequired,
|
|
@@ -142,8 +244,14 @@ export default class InputTextarea extends Component {
|
|
|
142
244
|
disabled: PropTypes.bool,
|
|
143
245
|
clearIcon: PropTypes.element,
|
|
144
246
|
toolbarIcon: PropTypes.element,
|
|
247
|
+
sizeIcon: PropTypes.element,
|
|
248
|
+
fontIcon: PropTypes.element,
|
|
249
|
+
colorIcon: PropTypes.element,
|
|
250
|
+
imageIcon: PropTypes.element,
|
|
145
251
|
presetTextIcon: PropTypes.element,
|
|
252
|
+
presetTexts: PropTypes.array,
|
|
146
253
|
onPresetText: PropTypes.func,
|
|
254
|
+
t: PropTypes.func,
|
|
147
255
|
};
|
|
148
256
|
|
|
149
257
|
static defaultProps = {
|
|
@@ -157,8 +265,14 @@ export default class InputTextarea extends Component {
|
|
|
157
265
|
required: false,
|
|
158
266
|
clearIcon: null,
|
|
159
267
|
toolbarIcon: null,
|
|
268
|
+
sizeIcon: null,
|
|
269
|
+
fontIcon: null,
|
|
270
|
+
colorIcon: null,
|
|
271
|
+
imageIcon: null,
|
|
160
272
|
presetTextIcon: null,
|
|
273
|
+
presetTexts: [],
|
|
161
274
|
onPresetText: null,
|
|
275
|
+
t: () => {},
|
|
162
276
|
};
|
|
163
277
|
|
|
164
278
|
constructor(props) {
|
|
@@ -169,18 +283,22 @@ export default class InputTextarea extends Component {
|
|
|
169
283
|
} else {
|
|
170
284
|
content = '<p/>';
|
|
171
285
|
}
|
|
286
|
+
const decorator = new CompositeDecorator([
|
|
287
|
+
{
|
|
288
|
+
strategy: findMailmergeEntities,
|
|
289
|
+
component: Mailmerge,
|
|
290
|
+
},
|
|
291
|
+
]);
|
|
172
292
|
const value = convertFromHTML(fromHtml)(content);
|
|
173
293
|
this.state = {
|
|
174
|
-
editorState: EditorState.createWithContent(value),
|
|
294
|
+
editorState: EditorState.createWithContent(value, decorator),
|
|
175
295
|
toolbar: false,
|
|
176
296
|
};
|
|
177
297
|
this.onEditorStateChange = this.onEditorStateChange.bind(this);
|
|
178
298
|
this.onChange = this.onChange.bind(this);
|
|
179
299
|
this.onToolbar = this.onToolbar.bind(this);
|
|
180
300
|
this.onClear = this.onClear.bind(this);
|
|
181
|
-
this.onPresetText = this.onPresetText.bind(this);
|
|
182
301
|
this.onAddPresetText = this.onAddPresetText.bind(this);
|
|
183
|
-
this.onPresetTextClose = this.onPresetTextClose.bind(this);
|
|
184
302
|
}
|
|
185
303
|
|
|
186
304
|
onEditorStateChange(editorState) {
|
|
@@ -216,11 +334,6 @@ export default class InputTextarea extends Component {
|
|
|
216
334
|
this.props.onChange(event);
|
|
217
335
|
}
|
|
218
336
|
|
|
219
|
-
onPresetText() {
|
|
220
|
-
this.props.onPresetText();
|
|
221
|
-
this.setState({ presetText: true });
|
|
222
|
-
}
|
|
223
|
-
|
|
224
337
|
onAddPresetText(contentMore) {
|
|
225
338
|
const editorState = this.state.editorState;
|
|
226
339
|
let content = editorState.getCurrentContent();
|
|
@@ -238,27 +351,54 @@ export default class InputTextarea extends Component {
|
|
|
238
351
|
this.props.onChange(event);
|
|
239
352
|
}
|
|
240
353
|
|
|
241
|
-
onPresetTextClose() {
|
|
242
|
-
this.setState({ presetText: false });
|
|
243
|
-
}
|
|
244
|
-
|
|
245
354
|
render() {
|
|
246
355
|
//<div className={classnames('input-group', (props.error || props.warning) && 'is-invalid')}>
|
|
247
356
|
const { editorState, toolbar } = this.state;
|
|
357
|
+
const { t } = this.props;
|
|
248
358
|
const editorToolbar = {
|
|
249
|
-
options: ['inline','fontSize','colorPicker'],
|
|
359
|
+
options: ['inline','fontSize', 'fontFamily', 'list', 'textAlign', 'colorPicker', 'image'],
|
|
250
360
|
inline: {
|
|
251
361
|
inDropdown: false,
|
|
252
362
|
component: Inline,
|
|
253
|
-
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' }) },
|
|
254
371
|
},
|
|
255
372
|
fontSize: {
|
|
256
373
|
component: FontSize,
|
|
257
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'],
|
|
258
390
|
},
|
|
259
391
|
colorPicker: {
|
|
260
|
-
component: ColorPicker
|
|
261
|
-
|
|
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
|
+
},
|
|
262
402
|
};
|
|
263
403
|
return (
|
|
264
404
|
<div className={classnames('form-group', !this.props.labelTop && 'row')}>
|
|
@@ -276,8 +416,9 @@ export default class InputTextarea extends Component {
|
|
|
276
416
|
<Editor
|
|
277
417
|
toolbarHidden={!toolbar}
|
|
278
418
|
toolbar={editorToolbar}
|
|
419
|
+
toolbarCustomButtons={MailmergeOptions(this.props)}
|
|
279
420
|
editorState={editorState}
|
|
280
|
-
wrapperClassName="form-control border-
|
|
421
|
+
wrapperClassName="form-control border-ui h-auto"
|
|
281
422
|
onEditorStateChange={this.onEditorStateChange}
|
|
282
423
|
onBlur={this.onChange}
|
|
283
424
|
/>
|
|
@@ -285,7 +426,7 @@ export default class InputTextarea extends Component {
|
|
|
285
426
|
<button
|
|
286
427
|
type="button"
|
|
287
428
|
className={classnames(
|
|
288
|
-
'btn btn-input btn-outline-
|
|
429
|
+
'btn btn-input btn-outline-ui bg-light',
|
|
289
430
|
this.props.size === 'sm' && `btn-${this.props.size}`
|
|
290
431
|
)}
|
|
291
432
|
onClick={this.onToolbar}
|
|
@@ -295,7 +436,7 @@ export default class InputTextarea extends Component {
|
|
|
295
436
|
<button
|
|
296
437
|
type="button"
|
|
297
438
|
className={classnames(
|
|
298
|
-
'btn btn-input btn-outline-
|
|
439
|
+
'btn btn-input btn-outline-ui bg-light',
|
|
299
440
|
this.props.size === 'sm' && `btn-${this.props.size}`
|
|
300
441
|
)}
|
|
301
442
|
onClick={this.onClear}
|