react-native-richify 1.0.2 → 1.0.3
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/lib/commonjs/components/OverlayText.d.js.map +1 -1
- package/lib/commonjs/components/OverlayText.js +8 -2
- package/lib/commonjs/components/OverlayText.js.map +1 -1
- package/lib/commonjs/components/RichTextInput.d.js.map +1 -1
- package/lib/commonjs/components/RichTextInput.js +9 -6
- package/lib/commonjs/components/RichTextInput.js.map +1 -1
- package/lib/commonjs/components/Toolbar.d.js.map +1 -1
- package/lib/commonjs/components/Toolbar.js +4 -7
- package/lib/commonjs/components/Toolbar.js.map +1 -1
- package/lib/commonjs/components/ToolbarButton.d.js.map +1 -1
- package/lib/commonjs/components/ToolbarButton.js.map +1 -1
- package/lib/commonjs/constants/defaultStyles.d.js.map +1 -1
- package/lib/commonjs/constants/defaultStyles.js.map +1 -1
- package/lib/commonjs/context/RichTextContext.d.js.map +1 -1
- package/lib/commonjs/context/RichTextContext.js.map +1 -1
- package/lib/commonjs/hooks/useFormatting.d.js.map +1 -1
- package/lib/commonjs/hooks/useFormatting.js.map +1 -1
- package/lib/commonjs/hooks/useRichText.d.js.map +1 -1
- package/lib/commonjs/hooks/useRichText.js +7 -1
- package/lib/commonjs/hooks/useRichText.js.map +1 -1
- package/lib/commonjs/hooks/useSelection.d.js.map +1 -1
- package/lib/commonjs/hooks/useSelection.js.map +1 -1
- package/lib/commonjs/index.d.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types/index.d.js.map +1 -1
- package/lib/commonjs/types/index.js.map +1 -1
- package/lib/commonjs/utils/formatter.d.js.map +1 -1
- package/lib/commonjs/utils/formatter.js.map +1 -1
- package/lib/commonjs/utils/parser.d.js.map +1 -1
- package/lib/commonjs/utils/parser.js.map +1 -1
- package/lib/commonjs/utils/styleMapper.d.js.map +1 -1
- package/lib/commonjs/utils/styleMapper.js.map +1 -1
- package/lib/module/components/OverlayText.d.js.map +1 -1
- package/lib/module/components/OverlayText.js +8 -2
- package/lib/module/components/OverlayText.js.map +1 -1
- package/lib/module/components/RichTextInput.d.js.map +1 -1
- package/lib/module/components/RichTextInput.js +9 -6
- package/lib/module/components/RichTextInput.js.map +1 -1
- package/lib/module/components/Toolbar.d.js.map +1 -1
- package/lib/module/components/Toolbar.js +4 -7
- package/lib/module/components/Toolbar.js.map +1 -1
- package/lib/module/components/ToolbarButton.d.js.map +1 -1
- package/lib/module/components/ToolbarButton.js.map +1 -1
- package/lib/module/constants/defaultStyles.d.js.map +1 -1
- package/lib/module/constants/defaultStyles.js.map +1 -1
- package/lib/module/context/RichTextContext.d.js.map +1 -1
- package/lib/module/context/RichTextContext.js.map +1 -1
- package/lib/module/hooks/useFormatting.d.js.map +1 -1
- package/lib/module/hooks/useFormatting.js.map +1 -1
- package/lib/module/hooks/useRichText.d.js.map +1 -1
- package/lib/module/hooks/useRichText.js +7 -1
- package/lib/module/hooks/useRichText.js.map +1 -1
- package/lib/module/hooks/useSelection.d.js.map +1 -1
- package/lib/module/hooks/useSelection.js.map +1 -1
- package/lib/module/index.d.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/index.d.js.map +1 -1
- package/lib/module/types/index.js.map +1 -1
- package/lib/module/utils/formatter.d.js.map +1 -1
- package/lib/module/utils/formatter.js.map +1 -1
- package/lib/module/utils/parser.d.js.map +1 -1
- package/lib/module/utils/parser.js.map +1 -1
- package/lib/module/utils/styleMapper.d.js.map +1 -1
- package/lib/module/utils/styleMapper.js.map +1 -1
- package/lib/typescript/src/components/OverlayText.d.ts.map +1 -1
- package/lib/typescript/src/components/RichTextInput.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useRichText.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types/index.d.ts +22 -10
- package/lib/typescript/src/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/OverlayText.tsx +11 -3
- package/src/components/RichTextInput.tsx +11 -5
- package/src/components/Toolbar.d.ts +1 -1
- package/src/components/Toolbar.tsx +5 -5
- package/src/components/ToolbarButton.d.ts +1 -1
- package/src/constants/defaultStyles.d.ts +1 -1
- package/src/hooks/useRichText.ts +11 -4
- package/src/index.d.ts +1 -1
- package/src/index.ts +2 -0
- package/src/types/index.d.ts +22 -10
- package/src/types/index.ts +24 -10
|
@@ -17,21 +17,29 @@ export const OverlayText: React.FC<OverlayTextProps> = React.memo(
|
|
|
17
17
|
const overlayStyle =
|
|
18
18
|
resolvedTheme.overlayContainerStyle ??
|
|
19
19
|
DEFAULT_THEME.overlayContainerStyle;
|
|
20
|
+
const resolvedBaseTextStyle =
|
|
21
|
+
baseTextStyle ??
|
|
22
|
+
resolvedTheme.baseTextStyle ??
|
|
23
|
+
DEFAULT_THEME.baseTextStyle;
|
|
24
|
+
const overlayTheme = {
|
|
25
|
+
...resolvedTheme,
|
|
26
|
+
baseTextStyle: resolvedBaseTextStyle,
|
|
27
|
+
};
|
|
20
28
|
|
|
21
29
|
return (
|
|
22
30
|
<View style={overlayStyle} pointerEvents="none">
|
|
23
|
-
<Text>
|
|
31
|
+
<Text style={resolvedBaseTextStyle}>
|
|
24
32
|
{segments.map((segment, index) => {
|
|
25
33
|
if (segment.text.length === 0 && segments.length > 1) {
|
|
26
34
|
return null;
|
|
27
35
|
}
|
|
28
36
|
|
|
29
|
-
const textStyle = segmentToTextStyle(segment,
|
|
37
|
+
const textStyle = segmentToTextStyle(segment, overlayTheme);
|
|
30
38
|
|
|
31
39
|
return (
|
|
32
40
|
<Text
|
|
33
41
|
key={`${index}-${segment.text.slice(0, 8)}`}
|
|
34
|
-
style={
|
|
42
|
+
style={textStyle}
|
|
35
43
|
>
|
|
36
44
|
{segment.text}
|
|
37
45
|
</Text>
|
|
@@ -89,7 +89,10 @@ export const RichTextInput: React.FC<RichTextInputProps> = ({
|
|
|
89
89
|
// Input style
|
|
90
90
|
const inputStyle = [
|
|
91
91
|
styles.textInput,
|
|
92
|
+
resolvedTheme.baseTextStyle ?? DEFAULT_THEME.baseTextStyle,
|
|
92
93
|
resolvedTheme.inputStyle ?? DEFAULT_THEME.inputStyle,
|
|
94
|
+
textInputProps?.style,
|
|
95
|
+
styles.hiddenInputText,
|
|
93
96
|
];
|
|
94
97
|
|
|
95
98
|
// Toolbar component
|
|
@@ -141,11 +144,12 @@ export const RichTextInput: React.FC<RichTextInputProps> = ({
|
|
|
141
144
|
editable={editable}
|
|
142
145
|
maxLength={maxLength}
|
|
143
146
|
autoFocus={autoFocus}
|
|
147
|
+
underlineColorAndroid="transparent"
|
|
144
148
|
selectionColor={
|
|
145
149
|
resolvedTheme.colors?.cursor ?? DEFAULT_THEME.colors?.cursor
|
|
146
150
|
}
|
|
147
151
|
textAlignVertical="top"
|
|
148
|
-
scrollEnabled={
|
|
152
|
+
scrollEnabled={typeof maxHeight === 'number'}
|
|
149
153
|
/>
|
|
150
154
|
</View>
|
|
151
155
|
|
|
@@ -164,11 +168,13 @@ const styles = StyleSheet.create({
|
|
|
164
168
|
position: 'relative',
|
|
165
169
|
},
|
|
166
170
|
textInput: {
|
|
167
|
-
// The TextInput must be transparent so the overlay text shows through.
|
|
168
|
-
// Only the caret/cursor and selection highlight are visible.
|
|
169
|
-
color: 'transparent',
|
|
170
|
-
// Ensure it matches the overlay text positioning exactly.
|
|
171
171
|
position: 'relative',
|
|
172
172
|
zIndex: 1,
|
|
173
173
|
},
|
|
174
|
+
hiddenInputText: {
|
|
175
|
+
// Keep the editable layer invisible while preserving the caret.
|
|
176
|
+
color: 'transparent',
|
|
177
|
+
backgroundColor: 'transparent',
|
|
178
|
+
textShadowColor: 'transparent',
|
|
179
|
+
},
|
|
174
180
|
});
|
|
@@ -20,17 +20,17 @@ export const Toolbar: React.FC<ToolbarProps> = React.memo(
|
|
|
20
20
|
|
|
21
21
|
// Compute active state for each item
|
|
22
22
|
const enrichedItems: ToolbarItem[] = useMemo(() => {
|
|
23
|
+
const selectionStyle = actions.getSelectionStyle();
|
|
24
|
+
|
|
23
25
|
return toolbarItems.map((item) => {
|
|
24
26
|
let isActive = false;
|
|
25
27
|
|
|
26
28
|
if (item.format) {
|
|
27
|
-
|
|
28
|
-
const { activeStyles } = state;
|
|
29
|
-
isActive = !!activeStyles[item.format];
|
|
29
|
+
isActive = actions.isFormatActive(item.format);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
if (item.heading) {
|
|
33
|
-
isActive =
|
|
33
|
+
isActive = selectionStyle.heading === item.heading;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
return {
|
|
@@ -38,7 +38,7 @@ export const Toolbar: React.FC<ToolbarProps> = React.memo(
|
|
|
38
38
|
active: item.active ?? isActive,
|
|
39
39
|
};
|
|
40
40
|
});
|
|
41
|
-
}, [
|
|
41
|
+
}, [actions, toolbarItems]);
|
|
42
42
|
|
|
43
43
|
// Custom render
|
|
44
44
|
if (renderToolbar) {
|
package/src/hooks/useRichText.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { useState, useCallback, useRef
|
|
1
|
+
import { useState, useCallback, useRef } from 'react';
|
|
2
2
|
import type {
|
|
3
3
|
StyledSegment,
|
|
4
|
-
FormatType,
|
|
5
4
|
FormatStyle,
|
|
6
|
-
HeadingLevel,
|
|
7
5
|
SelectionRange,
|
|
8
6
|
RichTextState,
|
|
9
7
|
RichTextActions,
|
|
@@ -16,6 +14,7 @@ import {
|
|
|
16
14
|
reconcileTextChange,
|
|
17
15
|
findPositionInSegments,
|
|
18
16
|
} from '../utils/parser';
|
|
17
|
+
import { getSelectionStyle } from '../utils/formatter';
|
|
19
18
|
import { useSelection } from '../hooks/useSelection';
|
|
20
19
|
import { useFormatting } from '../hooks/useFormatting';
|
|
21
20
|
|
|
@@ -57,6 +56,8 @@ export function useRichText(
|
|
|
57
56
|
// Refs for stable access in callbacks
|
|
58
57
|
const segmentsRef = useRef(segments);
|
|
59
58
|
segmentsRef.current = segments;
|
|
59
|
+
const selectionRef = useRef(selection);
|
|
60
|
+
selectionRef.current = selection;
|
|
60
61
|
const activeStylesRef = useRef(activeStyles);
|
|
61
62
|
activeStylesRef.current = activeStyles;
|
|
62
63
|
|
|
@@ -86,7 +87,11 @@ export function useRichText(
|
|
|
86
87
|
const handleTextChange = useCallback(
|
|
87
88
|
(newText: string) => {
|
|
88
89
|
const currentSegments = segmentsRef.current;
|
|
89
|
-
const
|
|
90
|
+
const currentSelection = selectionRef.current;
|
|
91
|
+
const currentActiveStyles =
|
|
92
|
+
currentSelection.start === currentSelection.end
|
|
93
|
+
? activeStylesRef.current
|
|
94
|
+
: getSelectionStyle(currentSegments, currentSelection);
|
|
90
95
|
|
|
91
96
|
const newSegments = reconcileTextChange(
|
|
92
97
|
currentSegments,
|
|
@@ -161,6 +166,8 @@ export function useRichText(
|
|
|
161
166
|
setFontSize: formatting.setFontSize,
|
|
162
167
|
handleTextChange,
|
|
163
168
|
handleSelectionChange: onSelectionChange,
|
|
169
|
+
isFormatActive: formatting.isFormatActive,
|
|
170
|
+
getSelectionStyle: formatting.currentSelectionStyle,
|
|
164
171
|
getPlainText,
|
|
165
172
|
exportJSON,
|
|
166
173
|
importJSON,
|
package/src/index.d.ts
CHANGED
|
@@ -12,4 +12,4 @@ export { createSegment, segmentsToPlainText, getTotalLength, mergeAdjacentSegmen
|
|
|
12
12
|
export { toggleFormatOnSelection, setStyleOnSelection, setHeadingOnLine, isFormatActiveInSelection, getSelectionStyle, } from './utils/formatter';
|
|
13
13
|
export { formatStyleToTextStyle, segmentToTextStyle, segmentsToTextStyles, } from './utils/styleMapper';
|
|
14
14
|
export { DEFAULT_COLORS, DEFAULT_THEME, DEFAULT_TOOLBAR_ITEMS, DEFAULT_BASE_TEXT_STYLE, HEADING_FONT_SIZES, EMPTY_FORMAT_STYLE, } from './constants/defaultStyles';
|
|
15
|
-
export type { FormatType, HeadingLevel, ListType, FormatStyle, StyledSegment, SelectionRange, RichTextState, RichTextActions, UseRichTextReturn, RichTextTheme, ToolbarItem, OverlayTextProps, ToolbarButtonProps, ToolbarProps, RichTextInputProps, } from './types';
|
|
15
|
+
export type { FormatType, HeadingLevel, ListType, FormatStyle, StyledSegment, SelectionRange, RichTextState, RichTextActions, UseRichTextReturn, RichTextTheme, ToolbarItem, ToolbarButtonRenderProps, ToolbarRenderProps, OverlayTextProps, ToolbarButtonProps, ToolbarProps, RichTextInputProps, } from './types';
|
package/src/index.ts
CHANGED
package/src/types/index.d.ts
CHANGED
|
@@ -75,6 +75,10 @@ export interface RichTextActions {
|
|
|
75
75
|
handleTextChange: (text: string) => void;
|
|
76
76
|
/** Handle selection change from TextInput. */
|
|
77
77
|
handleSelectionChange: (selection: SelectionRange) => void;
|
|
78
|
+
/** Check whether a format is active at the current cursor/selection. */
|
|
79
|
+
isFormatActive: (format: FormatType) => boolean;
|
|
80
|
+
/** Get the effective shared style at the current cursor/selection. */
|
|
81
|
+
getSelectionStyle: () => FormatStyle;
|
|
78
82
|
/** Get the full plain text content. */
|
|
79
83
|
getPlainText: () => string;
|
|
80
84
|
/** Export the segments as a serializable JSON array. */
|
|
@@ -150,11 +154,23 @@ export interface ToolbarItem {
|
|
|
150
154
|
/** Whether this item is currently active. */
|
|
151
155
|
active?: boolean;
|
|
152
156
|
/** Custom render function for the button. */
|
|
153
|
-
renderButton?: (props:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
157
|
+
renderButton?: (props: ToolbarButtonRenderProps) => React.ReactElement | null;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Props passed to a custom toolbar button renderer.
|
|
161
|
+
*/
|
|
162
|
+
export interface ToolbarButtonRenderProps {
|
|
163
|
+
active: boolean;
|
|
164
|
+
onPress: () => void;
|
|
165
|
+
label: string;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Props passed to a custom toolbar renderer.
|
|
169
|
+
*/
|
|
170
|
+
export interface ToolbarRenderProps {
|
|
171
|
+
items: ToolbarItem[];
|
|
172
|
+
state: RichTextState;
|
|
173
|
+
actions: RichTextActions;
|
|
158
174
|
}
|
|
159
175
|
/**
|
|
160
176
|
* Props for the OverlayText component.
|
|
@@ -197,11 +213,7 @@ export interface ToolbarProps {
|
|
|
197
213
|
/** Whether to show the toolbar. */
|
|
198
214
|
visible?: boolean;
|
|
199
215
|
/** Custom render function for the entire toolbar. */
|
|
200
|
-
renderToolbar?: (props:
|
|
201
|
-
items: ToolbarItem[];
|
|
202
|
-
state: RichTextState;
|
|
203
|
-
actions: RichTextActions;
|
|
204
|
-
}) => React.ReactElement;
|
|
216
|
+
renderToolbar?: (props: ToolbarRenderProps) => React.ReactElement | null;
|
|
205
217
|
}
|
|
206
218
|
/**
|
|
207
219
|
* Props for the main RichTextInput component.
|
package/src/types/index.ts
CHANGED
|
@@ -101,6 +101,10 @@ export interface RichTextActions {
|
|
|
101
101
|
handleTextChange: (text: string) => void;
|
|
102
102
|
/** Handle selection change from TextInput. */
|
|
103
103
|
handleSelectionChange: (selection: SelectionRange) => void;
|
|
104
|
+
/** Check whether a format is active at the current cursor/selection. */
|
|
105
|
+
isFormatActive: (format: FormatType) => boolean;
|
|
106
|
+
/** Get the effective shared style at the current cursor/selection. */
|
|
107
|
+
getSelectionStyle: () => FormatStyle;
|
|
104
108
|
/** Get the full plain text content. */
|
|
105
109
|
getPlainText: () => string;
|
|
106
110
|
/** Export the segments as a serializable JSON array. */
|
|
@@ -185,11 +189,25 @@ export interface ToolbarItem {
|
|
|
185
189
|
/** Whether this item is currently active. */
|
|
186
190
|
active?: boolean;
|
|
187
191
|
/** Custom render function for the button. */
|
|
188
|
-
renderButton?: (props:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
192
|
+
renderButton?: (props: ToolbarButtonRenderProps) => React.ReactElement | null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Props passed to a custom toolbar button renderer.
|
|
197
|
+
*/
|
|
198
|
+
export interface ToolbarButtonRenderProps {
|
|
199
|
+
active: boolean;
|
|
200
|
+
onPress: () => void;
|
|
201
|
+
label: string;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Props passed to a custom toolbar renderer.
|
|
206
|
+
*/
|
|
207
|
+
export interface ToolbarRenderProps {
|
|
208
|
+
items: ToolbarItem[];
|
|
209
|
+
state: RichTextState;
|
|
210
|
+
actions: RichTextActions;
|
|
193
211
|
}
|
|
194
212
|
|
|
195
213
|
// ─── Component Props ─────────────────────────────────────────────────────────
|
|
@@ -237,11 +255,7 @@ export interface ToolbarProps {
|
|
|
237
255
|
/** Whether to show the toolbar. */
|
|
238
256
|
visible?: boolean;
|
|
239
257
|
/** Custom render function for the entire toolbar. */
|
|
240
|
-
renderToolbar?: (props:
|
|
241
|
-
items: ToolbarItem[];
|
|
242
|
-
state: RichTextState;
|
|
243
|
-
actions: RichTextActions;
|
|
244
|
-
}) => React.ReactElement;
|
|
258
|
+
renderToolbar?: (props: ToolbarRenderProps) => React.ReactElement | null;
|
|
245
259
|
}
|
|
246
260
|
|
|
247
261
|
/**
|