rn-rich-text-editor 1.2.3 → 1.3.1
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/package.json +1 -1
- package/src/Toolbar.js +94 -10
- package/src/actions.js +1 -0
- package/src/editor/createHTML.js +12 -1
- package/src/index.d.ts +2 -0
package/package.json
CHANGED
package/src/Toolbar.js
CHANGED
|
@@ -105,6 +105,7 @@ export default class Toolbar extends Component {
|
|
|
105
105
|
iconTint: '#71787F',
|
|
106
106
|
iconSize: 20,
|
|
107
107
|
iconGap: 16,
|
|
108
|
+
separatorStyle: undefined,
|
|
108
109
|
};
|
|
109
110
|
|
|
110
111
|
constructor(props) {
|
|
@@ -169,19 +170,21 @@ export default class Toolbar extends Component {
|
|
|
169
170
|
};
|
|
170
171
|
|
|
171
172
|
static getDerivedStateFromProps(nextProps, prevState) {
|
|
172
|
-
const { actions } = nextProps;
|
|
173
|
-
if (
|
|
173
|
+
const { actions: actionsList } = nextProps;
|
|
174
|
+
if (actionsList !== prevState.actions) {
|
|
174
175
|
const items = prevState.items || [];
|
|
175
176
|
const isItemSelected = (action) =>
|
|
176
177
|
items.includes(action) || items.some(item => item && item.type === action);
|
|
177
178
|
return {
|
|
178
|
-
actions,
|
|
179
|
-
data:
|
|
179
|
+
actions: actionsList,
|
|
180
|
+
data: actionsList.map(action => ({
|
|
180
181
|
action,
|
|
181
182
|
selected:
|
|
182
|
-
action === actions.
|
|
183
|
-
?
|
|
184
|
-
:
|
|
183
|
+
action === actions.separator
|
|
184
|
+
? false // Separators are never selected
|
|
185
|
+
: action === actions.align
|
|
186
|
+
? ALIGN_ACTIONS.some(a => isItemSelected(a))
|
|
187
|
+
: isItemSelected(action),
|
|
185
188
|
})),
|
|
186
189
|
};
|
|
187
190
|
}
|
|
@@ -224,6 +227,9 @@ export default class Toolbar extends Component {
|
|
|
224
227
|
items,
|
|
225
228
|
selectedAlign,
|
|
226
229
|
data: this.state.actions.map(action => {
|
|
230
|
+
if (action === actions.separator) {
|
|
231
|
+
return { action, selected: false };
|
|
232
|
+
}
|
|
227
233
|
const isAlignAction = action === actions.align;
|
|
228
234
|
const selected = isAlignAction
|
|
229
235
|
? !!selectedAlign
|
|
@@ -277,6 +283,11 @@ export default class Toolbar extends Component {
|
|
|
277
283
|
return;
|
|
278
284
|
}
|
|
279
285
|
|
|
286
|
+
// Separators are not clickable
|
|
287
|
+
if (action === actions.separator) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
280
291
|
if (action === actions.align) {
|
|
281
292
|
const { selectedAlign } = this.state;
|
|
282
293
|
// Cycle through alignments: Left -> Center -> Right -> Justify -> Left
|
|
@@ -448,7 +459,48 @@ export default class Toolbar extends Component {
|
|
|
448
459
|
);
|
|
449
460
|
}
|
|
450
461
|
|
|
462
|
+
_renderSeparator() {
|
|
463
|
+
const { style, separatorStyle, iconGap = 16 } = this.props;
|
|
464
|
+
// Extract separator from style prop (handles both object and array)
|
|
465
|
+
let separatorFromStyleProp = null;
|
|
466
|
+
if (style) {
|
|
467
|
+
if (Array.isArray(style)) {
|
|
468
|
+
for (const styleItem of style) {
|
|
469
|
+
if (styleItem && typeof styleItem === 'object' && styleItem.separator) {
|
|
470
|
+
separatorFromStyleProp = styleItem.separator;
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
} else if (typeof style === 'object' && style.separator) {
|
|
475
|
+
separatorFromStyleProp = style.separator;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
// Merge: defaults, then style.separator, then separatorStyle (later overrides)
|
|
479
|
+
const merged = {
|
|
480
|
+
width: 1,
|
|
481
|
+
height: 24,
|
|
482
|
+
backgroundColor: '#E2E2E4',
|
|
483
|
+
marginHorizontal: iconGap / 2,
|
|
484
|
+
};
|
|
485
|
+
if (separatorFromStyleProp) {
|
|
486
|
+
Object.assign(merged, StyleSheet.flatten(separatorFromStyleProp));
|
|
487
|
+
}
|
|
488
|
+
if (separatorStyle) {
|
|
489
|
+
Object.assign(merged, StyleSheet.flatten(separatorStyle));
|
|
490
|
+
}
|
|
491
|
+
return (
|
|
492
|
+
<View
|
|
493
|
+
key="separator"
|
|
494
|
+
pointerEvents="none"
|
|
495
|
+
style={merged}
|
|
496
|
+
/>
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
451
500
|
_renderAction(action, selected) {
|
|
501
|
+
if (action === actions.separator) {
|
|
502
|
+
return this._renderSeparator();
|
|
503
|
+
}
|
|
452
504
|
if (action === actions.align) {
|
|
453
505
|
return this._renderAlignButton(action, selected);
|
|
454
506
|
}
|
|
@@ -463,8 +515,40 @@ export default class Toolbar extends Component {
|
|
|
463
515
|
return null;
|
|
464
516
|
}
|
|
465
517
|
const disabledStyle = disabled ? { backgroundColor: '#C9CED7' } : {};
|
|
466
|
-
|
|
467
|
-
|
|
518
|
+
// Extract separator from style prop if present, keep rest for container
|
|
519
|
+
let containerStyle = style;
|
|
520
|
+
if (style) {
|
|
521
|
+
if (Array.isArray(style)) {
|
|
522
|
+
// Remove separator from any object in the array
|
|
523
|
+
containerStyle = style.map(styleItem => {
|
|
524
|
+
if (styleItem && typeof styleItem === 'object' && styleItem.separator) {
|
|
525
|
+
const { separator, ...rest } = styleItem;
|
|
526
|
+
return rest;
|
|
527
|
+
}
|
|
528
|
+
return styleItem;
|
|
529
|
+
});
|
|
530
|
+
} else if (typeof style === 'object' && style.separator) {
|
|
531
|
+
const { separator, ...rest } = style;
|
|
532
|
+
containerStyle = rest;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
const vStyle = [styles.barContainer, disabledStyle, containerStyle, disabled && this._getButtonDisabledStyle()];
|
|
536
|
+
// Extract backgroundColor from containerStyle (handle array case)
|
|
537
|
+
let barBg = TOOLBAR_BG;
|
|
538
|
+
if (disabled) {
|
|
539
|
+
barBg = '#C9CED7';
|
|
540
|
+
} else if (containerStyle) {
|
|
541
|
+
if (Array.isArray(containerStyle)) {
|
|
542
|
+
for (const styleItem of containerStyle) {
|
|
543
|
+
if (styleItem && typeof styleItem === 'object' && styleItem.backgroundColor) {
|
|
544
|
+
barBg = styleItem.backgroundColor;
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
} else if (typeof containerStyle === 'object' && containerStyle.backgroundColor) {
|
|
549
|
+
barBg = containerStyle.backgroundColor;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
468
552
|
const showFades = horizontal && !disabled;
|
|
469
553
|
return (
|
|
470
554
|
<View style={vStyle}>
|
|
@@ -473,7 +557,7 @@ export default class Toolbar extends Component {
|
|
|
473
557
|
horizontal={horizontal}
|
|
474
558
|
style={[flatContainerStyle, showFades && styles.scrollList]}
|
|
475
559
|
keyboardShouldPersistTaps={'always'}
|
|
476
|
-
keyExtractor={(item, index) => item.action + '-' + index}
|
|
560
|
+
keyExtractor={(item, index) => (item.action === actions.separator ? 'separator' : item.action) + '-' + index}
|
|
477
561
|
data={this.state.data}
|
|
478
562
|
alwaysBounceHorizontal={false}
|
|
479
563
|
showsHorizontalScrollIndicator={false}
|
package/src/actions.js
CHANGED
|
@@ -44,6 +44,7 @@ export const actions = {
|
|
|
44
44
|
hiliteColor: 'hiliteColor',
|
|
45
45
|
blockquote: 'quote',
|
|
46
46
|
keyboard: 'keyboard',
|
|
47
|
+
separator: 'separator', // Visual separator for toolbar
|
|
47
48
|
setTitlePlaceholder: 'SET_TITLE_PLACEHOLDER',
|
|
48
49
|
setContentPlaceholder: 'SET_CONTENT_PLACEHOLDER',
|
|
49
50
|
setTitleFocusHandler: 'SET_TITLE_FOCUS_HANDLER',
|
package/src/editor/createHTML.js
CHANGED
|
@@ -801,11 +801,22 @@ function createReadOnlyHTML(options = {}) {
|
|
|
801
801
|
${useDefaultFont ? 'font-family: Arial, Helvetica, sans-serif; font-size: 1em;' : ''}
|
|
802
802
|
color: ${color};
|
|
803
803
|
padding: 0;
|
|
804
|
+
margin: 0;
|
|
804
805
|
height: auto !important;
|
|
805
806
|
min-height: 0 !important;
|
|
806
807
|
width: 100%;
|
|
807
808
|
${contentCSSText}
|
|
808
809
|
}
|
|
810
|
+
.readonly-container > *:first-child {
|
|
811
|
+
margin-top: 0 !important;
|
|
812
|
+
}
|
|
813
|
+
.readonly-container p {
|
|
814
|
+
margin-top: 0;
|
|
815
|
+
margin-bottom: 0.5em;
|
|
816
|
+
}
|
|
817
|
+
.readonly-container p:last-child {
|
|
818
|
+
margin-bottom: 0;
|
|
819
|
+
}
|
|
809
820
|
</style>
|
|
810
821
|
${getContentCSS()}
|
|
811
822
|
<style>${cssText}</style>
|
|
@@ -822,7 +833,7 @@ function createReadOnlyHTML(options = {}) {
|
|
|
822
833
|
var sendHeight = function() {
|
|
823
834
|
var scrollH = el.scrollHeight;
|
|
824
835
|
var offsetH = el.offsetHeight;
|
|
825
|
-
var h = Math.ceil(Math.max(scrollH, offsetH)) +
|
|
836
|
+
var h = Math.ceil(Math.max(scrollH, offsetH)) + 16;
|
|
826
837
|
if (h !== lastH && window.ReactNativeWebView) {
|
|
827
838
|
lastH = h;
|
|
828
839
|
window.ReactNativeWebView.postMessage(JSON.stringify({type: 'OFFSET_HEIGHT', data: h}));
|
package/src/index.d.ts
CHANGED
|
@@ -81,6 +81,7 @@ export interface ToolbarProps {
|
|
|
81
81
|
selectedButtonStyle?: object;
|
|
82
82
|
unselectedButtonStyle?: object;
|
|
83
83
|
disabledButtonStyle?: object;
|
|
84
|
+
separatorStyle?: object;
|
|
84
85
|
iconMap?: Record<string, unknown>;
|
|
85
86
|
renderAction?: (action: string, selected: boolean) => React.ReactElement;
|
|
86
87
|
onPressAddImage?: () => void;
|
|
@@ -139,6 +140,7 @@ export const actions: {
|
|
|
139
140
|
hiliteColor: string;
|
|
140
141
|
blockquote: string;
|
|
141
142
|
keyboard: string;
|
|
143
|
+
separator: string;
|
|
142
144
|
[key: string]: string;
|
|
143
145
|
};
|
|
144
146
|
|