react-native-richify 1.0.1 → 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.
Files changed (159) hide show
  1. package/lib/commonjs/components/OverlayText.d.js +6 -0
  2. package/lib/commonjs/components/OverlayText.d.js.map +1 -0
  3. package/lib/commonjs/components/OverlayText.js +51 -0
  4. package/lib/commonjs/components/OverlayText.js.map +1 -0
  5. package/lib/commonjs/components/RichTextInput.d.js +6 -0
  6. package/lib/commonjs/components/RichTextInput.d.js.map +1 -0
  7. package/lib/commonjs/components/RichTextInput.js +163 -0
  8. package/lib/commonjs/components/RichTextInput.js.map +1 -0
  9. package/lib/commonjs/components/Toolbar.d.js +6 -0
  10. package/lib/commonjs/components/Toolbar.d.js.map +1 -0
  11. package/lib/commonjs/components/Toolbar.js +96 -0
  12. package/lib/commonjs/components/Toolbar.js.map +1 -0
  13. package/lib/commonjs/components/ToolbarButton.d.js +6 -0
  14. package/lib/commonjs/components/ToolbarButton.d.js.map +1 -0
  15. package/lib/commonjs/components/ToolbarButton.js +63 -0
  16. package/lib/commonjs/components/ToolbarButton.js.map +1 -0
  17. package/lib/commonjs/constants/defaultStyles.d.js +6 -0
  18. package/lib/commonjs/constants/defaultStyles.d.js.map +1 -0
  19. package/lib/commonjs/constants/defaultStyles.js +172 -0
  20. package/lib/commonjs/constants/defaultStyles.js.map +1 -0
  21. package/lib/commonjs/context/RichTextContext.d.js +6 -0
  22. package/lib/commonjs/context/RichTextContext.d.js.map +1 -0
  23. package/lib/commonjs/context/RichTextContext.js +61 -0
  24. package/lib/commonjs/context/RichTextContext.js.map +1 -0
  25. package/lib/commonjs/hooks/useFormatting.d.js +6 -0
  26. package/lib/commonjs/hooks/useFormatting.d.js.map +1 -0
  27. package/lib/commonjs/hooks/useFormatting.js +82 -0
  28. package/lib/commonjs/hooks/useFormatting.js.map +1 -0
  29. package/lib/commonjs/hooks/useRichText.d.js +6 -0
  30. package/lib/commonjs/hooks/useRichText.d.js.map +1 -0
  31. package/lib/commonjs/hooks/useRichText.js +142 -0
  32. package/lib/commonjs/hooks/useRichText.js.map +1 -0
  33. package/lib/commonjs/hooks/useSelection.d.js +6 -0
  34. package/lib/commonjs/hooks/useSelection.d.js.map +1 -0
  35. package/lib/commonjs/hooks/useSelection.js +39 -0
  36. package/lib/commonjs/hooks/useSelection.js.map +1 -0
  37. package/lib/commonjs/index.d.js +186 -0
  38. package/lib/commonjs/index.d.js.map +1 -0
  39. package/lib/commonjs/index.js +186 -0
  40. package/lib/commonjs/index.js.map +1 -0
  41. package/lib/commonjs/package.json +1 -0
  42. package/lib/commonjs/types/index.d.js +6 -0
  43. package/lib/commonjs/types/index.d.js.map +1 -0
  44. package/lib/commonjs/types/index.js +6 -0
  45. package/lib/commonjs/types/index.js.map +1 -0
  46. package/lib/commonjs/utils/formatter.d.js +13 -0
  47. package/lib/commonjs/utils/formatter.d.js.map +1 -0
  48. package/lib/commonjs/utils/formatter.js +229 -0
  49. package/lib/commonjs/utils/formatter.js.map +1 -0
  50. package/lib/commonjs/utils/parser.d.js +6 -0
  51. package/lib/commonjs/utils/parser.d.js.map +1 -0
  52. package/lib/commonjs/utils/parser.js +221 -0
  53. package/lib/commonjs/utils/parser.js.map +1 -0
  54. package/lib/commonjs/utils/styleMapper.d.js +6 -0
  55. package/lib/commonjs/utils/styleMapper.d.js.map +1 -0
  56. package/lib/commonjs/utils/styleMapper.js +87 -0
  57. package/lib/commonjs/utils/styleMapper.js.map +1 -0
  58. package/lib/module/components/OverlayText.d.js +4 -0
  59. package/lib/module/components/OverlayText.d.js.map +1 -0
  60. package/lib/module/components/OverlayText.js +47 -0
  61. package/lib/module/components/OverlayText.js.map +1 -0
  62. package/lib/module/components/RichTextInput.d.js +4 -0
  63. package/lib/module/components/RichTextInput.d.js.map +1 -0
  64. package/lib/module/components/RichTextInput.js +158 -0
  65. package/lib/module/components/RichTextInput.js.map +1 -0
  66. package/lib/module/components/Toolbar.d.js +4 -0
  67. package/lib/module/components/Toolbar.d.js.map +1 -0
  68. package/lib/module/components/Toolbar.js +92 -0
  69. package/lib/module/components/Toolbar.js.map +1 -0
  70. package/lib/module/components/ToolbarButton.d.js +4 -0
  71. package/lib/module/components/ToolbarButton.d.js.map +1 -0
  72. package/lib/module/components/ToolbarButton.js +59 -0
  73. package/lib/module/components/ToolbarButton.js.map +1 -0
  74. package/lib/module/constants/defaultStyles.d.js +4 -0
  75. package/lib/module/constants/defaultStyles.d.js.map +1 -0
  76. package/lib/module/constants/defaultStyles.js +168 -0
  77. package/lib/module/constants/defaultStyles.js.map +1 -0
  78. package/lib/module/context/RichTextContext.d.js +4 -0
  79. package/lib/module/context/RichTextContext.d.js.map +1 -0
  80. package/lib/module/context/RichTextContext.js +55 -0
  81. package/lib/module/context/RichTextContext.js.map +1 -0
  82. package/lib/module/hooks/useFormatting.d.js +11 -0
  83. package/lib/module/hooks/useFormatting.d.js.map +1 -0
  84. package/lib/module/hooks/useFormatting.js +78 -0
  85. package/lib/module/hooks/useFormatting.js.map +1 -0
  86. package/lib/module/hooks/useRichText.d.js +4 -0
  87. package/lib/module/hooks/useRichText.d.js.map +1 -0
  88. package/lib/module/hooks/useRichText.js +138 -0
  89. package/lib/module/hooks/useRichText.js.map +1 -0
  90. package/lib/module/hooks/useSelection.d.js +4 -0
  91. package/lib/module/hooks/useSelection.d.js.map +1 -0
  92. package/lib/module/hooks/useSelection.js +35 -0
  93. package/lib/module/hooks/useSelection.js.map +1 -0
  94. package/lib/module/index.d.js +15 -0
  95. package/lib/module/index.d.js.map +1 -0
  96. package/lib/module/index.js +25 -0
  97. package/lib/module/index.js.map +1 -0
  98. package/lib/module/types/index.d.js +4 -0
  99. package/lib/module/types/index.d.js.map +1 -0
  100. package/lib/module/types/index.js +4 -0
  101. package/lib/module/types/index.js.map +1 -0
  102. package/lib/module/utils/formatter.d.js +30 -0
  103. package/lib/module/utils/formatter.d.js.map +1 -0
  104. package/lib/module/utils/formatter.js +217 -0
  105. package/lib/module/utils/formatter.js.map +1 -0
  106. package/lib/module/utils/parser.d.js +4 -0
  107. package/lib/module/utils/parser.d.js.map +1 -0
  108. package/lib/module/utils/parser.js +211 -0
  109. package/lib/module/utils/parser.js.map +1 -0
  110. package/lib/module/utils/styleMapper.d.js +4 -0
  111. package/lib/module/utils/styleMapper.d.js.map +1 -0
  112. package/lib/module/utils/styleMapper.js +82 -0
  113. package/lib/module/utils/styleMapper.js.map +1 -0
  114. package/lib/typescript/src/components/OverlayText.d.ts +11 -0
  115. package/lib/typescript/src/components/OverlayText.d.ts.map +1 -0
  116. package/lib/typescript/src/components/RichTextInput.d.ts +21 -0
  117. package/lib/typescript/src/components/RichTextInput.d.ts.map +1 -0
  118. package/lib/typescript/src/components/Toolbar.d.ts +13 -0
  119. package/lib/typescript/src/components/Toolbar.d.ts.map +1 -0
  120. package/lib/typescript/src/components/ToolbarButton.d.ts +8 -0
  121. package/lib/typescript/src/components/ToolbarButton.d.ts.map +1 -0
  122. package/lib/typescript/src/constants/defaultStyles.d.ts +46 -0
  123. package/lib/typescript/src/constants/defaultStyles.d.ts.map +1 -0
  124. package/lib/typescript/src/context/RichTextContext.d.ts +31 -0
  125. package/lib/typescript/src/context/RichTextContext.d.ts.map +1 -0
  126. package/lib/typescript/src/hooks/useFormatting.d.ts +26 -0
  127. package/lib/typescript/src/hooks/useFormatting.d.ts.map +1 -0
  128. package/lib/typescript/src/hooks/useRichText.d.ts +17 -0
  129. package/lib/typescript/src/hooks/useRichText.d.ts.map +1 -0
  130. package/lib/typescript/src/hooks/useSelection.d.ts +14 -0
  131. package/lib/typescript/src/hooks/useSelection.d.ts.map +1 -0
  132. package/lib/typescript/src/index.d.ts +16 -0
  133. package/lib/typescript/src/index.d.ts.map +1 -0
  134. package/lib/typescript/src/types/index.d.ts +257 -0
  135. package/lib/typescript/src/types/index.d.ts.map +1 -0
  136. package/lib/typescript/src/utils/formatter.d.ts +29 -0
  137. package/lib/typescript/src/utils/formatter.d.ts.map +1 -0
  138. package/lib/typescript/src/utils/parser.d.ts +46 -0
  139. package/lib/typescript/src/utils/parser.d.ts.map +1 -0
  140. package/lib/typescript/src/utils/styleMapper.d.ts +16 -0
  141. package/lib/typescript/src/utils/styleMapper.d.ts.map +1 -0
  142. package/package.json +1 -1
  143. package/src/components/OverlayText.tsx +11 -3
  144. package/src/components/RichTextInput.tsx +11 -5
  145. package/src/components/Toolbar.d.ts +1 -1
  146. package/src/components/Toolbar.tsx +5 -5
  147. package/src/components/ToolbarButton.d.ts +1 -1
  148. package/src/constants/defaultStyles.d.ts +1 -1
  149. package/src/hooks/useRichText.ts +11 -4
  150. package/src/index.d.ts +1 -1
  151. package/src/index.ts +2 -0
  152. package/src/types/index.d.ts +22 -10
  153. package/src/types/index.ts +24 -10
  154. package/src/utils/formatter.d.ts +2 -2
  155. package/src/utils/formatter.ts +4 -4
  156. package/src/utils/parser.d.ts +1 -1
  157. package/src/utils/parser.ts +2 -2
  158. package/src/utils/styleMapper.d.ts +1 -1
  159. package/src/utils/styleMapper.ts +2 -2
@@ -0,0 +1,257 @@
1
+ import type { TextStyle, ViewStyle, TextInputProps, ColorValue } from 'react-native';
2
+ /**
3
+ * Supported inline formatting types.
4
+ */
5
+ export type FormatType = 'bold' | 'italic' | 'underline' | 'strikethrough' | 'code';
6
+ /**
7
+ * Heading level presets.
8
+ */
9
+ export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'none';
10
+ /**
11
+ * List type for a line/paragraph.
12
+ */
13
+ export type ListType = 'bullet' | 'ordered' | 'none';
14
+ /**
15
+ * Inline formatting styles attached to a text segment.
16
+ */
17
+ export interface FormatStyle {
18
+ bold?: boolean;
19
+ italic?: boolean;
20
+ underline?: boolean;
21
+ strikethrough?: boolean;
22
+ code?: boolean;
23
+ color?: string;
24
+ backgroundColor?: string;
25
+ fontSize?: number;
26
+ heading?: HeadingLevel;
27
+ }
28
+ /**
29
+ * A segment of text with uniform formatting.
30
+ * The entire rich text content is an ordered array of these segments.
31
+ */
32
+ export interface StyledSegment {
33
+ /** The text content of this segment. */
34
+ text: string;
35
+ /** The formatting styles applied to this segment. */
36
+ styles: FormatStyle;
37
+ }
38
+ /**
39
+ * Represents a text selection range within the input.
40
+ */
41
+ export interface SelectionRange {
42
+ /** Start index (inclusive). */
43
+ start: number;
44
+ /** End index (exclusive). */
45
+ end: number;
46
+ }
47
+ /**
48
+ * The complete state of the rich text input.
49
+ */
50
+ export interface RichTextState {
51
+ /** Ordered array of styled text segments. */
52
+ segments: StyledSegment[];
53
+ /** Current selection range. */
54
+ selection: SelectionRange;
55
+ /** The current active styles that will be applied to newly typed text. */
56
+ activeStyles: FormatStyle;
57
+ }
58
+ /**
59
+ * Actions returned by the useRichText hook for controlling the editor.
60
+ */
61
+ export interface RichTextActions {
62
+ /** Toggle an inline format on the current selection or active styles. */
63
+ toggleFormat: (format: FormatType) => void;
64
+ /** Set a specific style property on the current selection. */
65
+ setStyleProperty: <K extends keyof FormatStyle>(key: K, value: FormatStyle[K]) => void;
66
+ /** Apply a heading level to the current line. */
67
+ setHeading: (level: HeadingLevel) => void;
68
+ /** Set the text color for the current selection. */
69
+ setColor: (color: string) => void;
70
+ /** Set the background color for the current selection. */
71
+ setBackgroundColor: (color: string) => void;
72
+ /** Set the font size for the current selection. */
73
+ setFontSize: (size: number) => void;
74
+ /** Handle text change from TextInput. */
75
+ handleTextChange: (text: string) => void;
76
+ /** Handle selection change from TextInput. */
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;
82
+ /** Get the full plain text content. */
83
+ getPlainText: () => string;
84
+ /** Export the segments as a serializable JSON array. */
85
+ exportJSON: () => StyledSegment[];
86
+ /** Import segments from a JSON array, replacing current content. */
87
+ importJSON: (segments: StyledSegment[]) => void;
88
+ /** Clear all content. */
89
+ clear: () => void;
90
+ }
91
+ /**
92
+ * Return value of the useRichText hook.
93
+ */
94
+ export interface UseRichTextReturn {
95
+ state: RichTextState;
96
+ actions: RichTextActions;
97
+ }
98
+ /**
99
+ * Theme configuration for the RichTextInput component.
100
+ */
101
+ export interface RichTextTheme {
102
+ /** Style for the outer container. */
103
+ containerStyle?: ViewStyle;
104
+ /** Style for the TextInput. */
105
+ inputStyle?: TextStyle;
106
+ /** Style for the overlay text container. */
107
+ overlayContainerStyle?: ViewStyle;
108
+ /** Base text style applied to all segments before formatting. */
109
+ baseTextStyle?: TextStyle;
110
+ /** Style for the toolbar container. */
111
+ toolbarStyle?: ViewStyle;
112
+ /** Style for toolbar buttons. */
113
+ toolbarButtonStyle?: ViewStyle;
114
+ /** Style for active toolbar buttons. */
115
+ toolbarButtonActiveStyle?: ViewStyle;
116
+ /** Text style for toolbar button labels. */
117
+ toolbarButtonTextStyle?: TextStyle;
118
+ /** Text style for active toolbar button labels. */
119
+ toolbarButtonActiveTextStyle?: TextStyle;
120
+ /** Style for the code format. */
121
+ codeStyle?: TextStyle;
122
+ /** Colors */
123
+ colors?: {
124
+ /** Primary accent color. */
125
+ primary?: string;
126
+ /** Background color of the editor. */
127
+ background?: string;
128
+ /** Text color. */
129
+ text?: string;
130
+ /** Placeholder text color. */
131
+ placeholder?: string;
132
+ /** Toolbar background. */
133
+ toolbarBackground?: string;
134
+ /** Toolbar border color. */
135
+ toolbarBorder?: string;
136
+ /** Cursor / caret color. */
137
+ cursor?: ColorValue;
138
+ };
139
+ }
140
+ /**
141
+ * A toolbar item configuration.
142
+ */
143
+ export interface ToolbarItem {
144
+ /** Unique identifier. */
145
+ id: string;
146
+ /** Display label or icon text. */
147
+ label: string;
148
+ /** The format type this button toggles (for inline formats). */
149
+ format?: FormatType;
150
+ /** The heading level this button sets. */
151
+ heading?: HeadingLevel;
152
+ /** Custom action handler (overrides default behavior). */
153
+ onPress?: () => void;
154
+ /** Whether this item is currently active. */
155
+ active?: boolean;
156
+ /** Custom render function for the button. */
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;
174
+ }
175
+ /**
176
+ * Props for the OverlayText component.
177
+ */
178
+ export interface OverlayTextProps {
179
+ /** The styled segments to render. */
180
+ segments: StyledSegment[];
181
+ /** Base text style. */
182
+ baseTextStyle?: TextStyle;
183
+ /** Theme overrides. */
184
+ theme?: RichTextTheme;
185
+ }
186
+ /**
187
+ * Props for the ToolbarButton component.
188
+ */
189
+ export interface ToolbarButtonProps {
190
+ /** Button label text. */
191
+ label: string;
192
+ /** Whether the button is currently active. */
193
+ active: boolean;
194
+ /** Press handler. */
195
+ onPress: () => void;
196
+ /** Theme overrides. */
197
+ theme?: RichTextTheme;
198
+ /** Custom render function. */
199
+ renderButton?: ToolbarItem['renderButton'];
200
+ }
201
+ /**
202
+ * Props for the Toolbar component.
203
+ */
204
+ export interface ToolbarProps {
205
+ /** The current rich text actions. */
206
+ actions: RichTextActions;
207
+ /** The current rich text state. */
208
+ state: RichTextState;
209
+ /** Custom toolbar items (overrides defaults). */
210
+ items?: ToolbarItem[];
211
+ /** Theme overrides. */
212
+ theme?: RichTextTheme;
213
+ /** Whether to show the toolbar. */
214
+ visible?: boolean;
215
+ /** Custom render function for the entire toolbar. */
216
+ renderToolbar?: (props: ToolbarRenderProps) => React.ReactElement | null;
217
+ }
218
+ /**
219
+ * Props for the main RichTextInput component.
220
+ */
221
+ export interface RichTextInputProps {
222
+ /** Initial segments to populate the editor with. */
223
+ initialSegments?: StyledSegment[];
224
+ /** Callback when the content changes. */
225
+ onChangeSegments?: (segments: StyledSegment[]) => void;
226
+ /** Callback when the plain text changes. */
227
+ onChangeText?: (text: string) => void;
228
+ /** Placeholder text. */
229
+ placeholder?: string;
230
+ /** Whether the input is editable. */
231
+ editable?: boolean;
232
+ /** Maximum character length. */
233
+ maxLength?: number;
234
+ /** Whether to show the toolbar. */
235
+ showToolbar?: boolean;
236
+ /** Toolbar position relative to the input. */
237
+ toolbarPosition?: 'top' | 'bottom';
238
+ /** Custom toolbar items. */
239
+ toolbarItems?: ToolbarItem[];
240
+ /** Theme configuration. */
241
+ theme?: RichTextTheme;
242
+ /** Whether multiline input is enabled. */
243
+ multiline?: boolean;
244
+ /** Minimum height for the input area. */
245
+ minHeight?: number;
246
+ /** Maximum height for the input area. */
247
+ maxHeight?: number;
248
+ /** Auto-focus the input on mount. */
249
+ autoFocus?: boolean;
250
+ /** Additional TextInput props. */
251
+ textInputProps?: Omit<TextInputProps, 'value' | 'onChangeText' | 'onSelectionChange' | 'multiline' | 'placeholder' | 'editable' | 'maxLength' | 'autoFocus'>;
252
+ /** Custom toolbar render function. */
253
+ renderToolbar?: ToolbarProps['renderToolbar'];
254
+ /** Ref callback to access actions. */
255
+ onReady?: (actions: RichTextActions) => void;
256
+ }
257
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { StyledSegment, FormatType, FormatStyle, HeadingLevel, SelectionRange } from '../types';
2
+ /**
3
+ * Toggle an inline format (bold, italic, etc.) on the selected range.
4
+ *
5
+ * If the entire selection already has the format, it is removed.
6
+ * Otherwise, it is applied to the entire selection.
7
+ *
8
+ * Returns the new segments array.
9
+ */
10
+ export declare function toggleFormatOnSelection(segments: StyledSegment[], selection: SelectionRange, format: FormatType): StyledSegment[];
11
+ /**
12
+ * Set a specific style property on the selected range.
13
+ */
14
+ export declare function setStyleOnSelection<K extends keyof FormatStyle>(segments: StyledSegment[], selection: SelectionRange, key: K, value: FormatStyle[K]): StyledSegment[];
15
+ /**
16
+ * Apply a heading level to the line containing the cursor/selection.
17
+ */
18
+ export declare function setHeadingOnLine(segments: StyledSegment[], selection: SelectionRange, level: HeadingLevel): StyledSegment[];
19
+ /**
20
+ * Checks whether the given format is active across the entire selection.
21
+ */
22
+ export declare function isFormatActiveInSelection(segments: StyledSegment[], selection: SelectionRange, format: FormatType): boolean;
23
+ /**
24
+ * Gets the format style that is common across the entire selection.
25
+ * For properties where segments disagree, the value is undefined.
26
+ */
27
+ export declare function getSelectionStyle(segments: StyledSegment[], selection: SelectionRange): FormatStyle;
28
+ export { createSegment } from '../utils/parser';
29
+ //# sourceMappingURL=formatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatter.d.ts","sourceRoot":"","sources":["../../../../src/utils/formatter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,WAAW,EACX,YAAY,EACZ,cAAc,EACf,MAAM,UAAU,CAAC;AAUlB;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,aAAa,EAAE,EACzB,SAAS,EAAE,cAAc,EACzB,MAAM,EAAE,UAAU,GACjB,aAAa,EAAE,CAgBjB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,MAAM,WAAW,EAC7D,QAAQ,EAAE,aAAa,EAAE,EACzB,SAAS,EAAE,cAAc,EACzB,GAAG,EAAE,CAAC,EACN,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,GACpB,aAAa,EAAE,CAOjB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,aAAa,EAAE,EACzB,SAAS,EAAE,cAAc,EACzB,KAAK,EAAE,YAAY,GAClB,aAAa,EAAE,CAWjB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,aAAa,EAAE,EACzB,SAAS,EAAE,cAAc,EACzB,MAAM,EAAE,UAAU,GACjB,OAAO,CAQT;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EAAE,EACzB,SAAS,EAAE,cAAc,GACxB,WAAW,CAwCb;AAgID,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { StyledSegment, FormatStyle } from '../types';
2
+ /**
3
+ * Creates a new segment with the given text and optional styles.
4
+ */
5
+ export declare function createSegment(text: string, styles?: FormatStyle): StyledSegment;
6
+ /**
7
+ * Computes the total character length across all segments.
8
+ */
9
+ export declare function getTotalLength(segments: StyledSegment[]): number;
10
+ /**
11
+ * Converts an array of segments to plain text.
12
+ */
13
+ export declare function segmentsToPlainText(segments: StyledSegment[]): string;
14
+ /**
15
+ * Finds which segment and character offset a global position corresponds to.
16
+ * Returns { segmentIndex, offsetInSegment }.
17
+ */
18
+ export declare function findPositionInSegments(segments: StyledSegment[], globalPosition: number): {
19
+ segmentIndex: number;
20
+ offsetInSegment: number;
21
+ };
22
+ /**
23
+ * Splits a segment at the given offset, returning [before, after].
24
+ * If offset is 0 or at end, one side will have empty text.
25
+ */
26
+ export declare function splitSegment(segment: StyledSegment, offset: number): [StyledSegment, StyledSegment];
27
+ /**
28
+ * Checks if two FormatStyle objects are deeply equal.
29
+ */
30
+ export declare function areStylesEqual(a: FormatStyle, b: FormatStyle): boolean;
31
+ /**
32
+ * Merges adjacent segments that have identical styles.
33
+ * Returns a new array (does not mutate input).
34
+ */
35
+ export declare function mergeAdjacentSegments(segments: StyledSegment[]): StyledSegment[];
36
+ /**
37
+ * Given the old segments and new plain text (from TextInput onChange),
38
+ * reconcile the segments to preserve formatting while reflecting the text change.
39
+ *
40
+ * Strategy:
41
+ * 1. Find the diff region between old plain text and new plain text
42
+ * 2. Replace that region in the segment array
43
+ * 3. New text inserted at the diff point inherits the `activeStyles`
44
+ */
45
+ export declare function reconcileTextChange(oldSegments: StyledSegment[], newText: string, activeStyles: FormatStyle): StyledSegment[];
46
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../../src/utils/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG3D;;GAEG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,WAAuC,GAC9C,aAAa,CAEf;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,CAEhE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM,CAErE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,aAAa,EAAE,EACzB,cAAc,EAAE,MAAM,GACrB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAiBnD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,MAAM,GACb,CAAC,aAAa,EAAE,aAAa,CAAC,CAIhC;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,OAAO,CAYtE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,aAAa,EAAE,GACxB,aAAa,EAAE,CA8BjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,aAAa,EAAE,EAC5B,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,WAAW,GACxB,aAAa,EAAE,CAqHjB"}
@@ -0,0 +1,16 @@
1
+ import type { TextStyle } from 'react-native';
2
+ import type { FormatStyle, RichTextTheme, StyledSegment } from '../types';
3
+ /**
4
+ * Maps a FormatStyle to a React Native TextStyle.
5
+ * Applies formatting properties based on the segment's style.
6
+ */
7
+ export declare function formatStyleToTextStyle(formatStyle: FormatStyle, theme?: RichTextTheme): TextStyle;
8
+ /**
9
+ * Maps an entire segment to its computed TextStyle (base + format).
10
+ */
11
+ export declare function segmentToTextStyle(segment: StyledSegment, theme?: RichTextTheme): TextStyle;
12
+ /**
13
+ * Batch-maps an array of segments to an array of TextStyles.
14
+ */
15
+ export declare function segmentsToTextStyles(segments: StyledSegment[], theme?: RichTextTheme): TextStyle[];
16
+ //# sourceMappingURL=styleMapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styleMapper.d.ts","sourceRoot":"","sources":["../../../../src/utils/styleMapper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAG1E;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,WAAW,EACxB,KAAK,CAAC,EAAE,aAAa,GACpB,SAAS,CAsDX;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,aAAa,EACtB,KAAK,CAAC,EAAE,aAAa,GACpB,SAAS,CAQX;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,aAAa,EAAE,EACzB,KAAK,CAAC,EAAE,aAAa,GACpB,SAAS,EAAE,CAEb"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-richify",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "A production-grade, fully customizable React Native Rich Text Input using the Overlay Technique — no WebView required.",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -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, resolvedTheme);
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={[baseTextStyle, textStyle]}
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={true}
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
  });
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { ToolbarProps } from '@/types';
2
+ import type { ToolbarProps } from '../types';
3
3
  /**
4
4
  * Formatting toolbar for the rich text editor.
5
5
  *
@@ -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
- // Check if the format is currently active
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 = state.activeStyles.heading === item.heading;
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
- }, [toolbarItems, state]);
41
+ }, [actions, toolbarItems]);
42
42
 
43
43
  // Custom render
44
44
  if (renderToolbar) {
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import type { ToolbarButtonProps } from '@/types';
2
+ import type { ToolbarButtonProps } from '../types';
3
3
  /**
4
4
  * A single toolbar button that toggles a formatting option.
5
5
  * Supports custom rendering via the `renderButton` prop.
@@ -1,4 +1,4 @@
1
- import type { RichTextTheme, FormatStyle, ToolbarItem } from '@/types';
1
+ import type { RichTextTheme, FormatStyle, ToolbarItem } from '../types';
2
2
  /**
3
3
  * Default color palette used throughout the editor.
4
4
  */
@@ -1,9 +1,7 @@
1
- import { useState, useCallback, useRef, useEffect } from 'react';
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 currentActiveStyles = activeStylesRef.current;
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
@@ -61,6 +61,8 @@ export type {
61
61
  UseRichTextReturn,
62
62
  RichTextTheme,
63
63
  ToolbarItem,
64
+ ToolbarButtonRenderProps,
65
+ ToolbarRenderProps,
64
66
  OverlayTextProps,
65
67
  ToolbarButtonProps,
66
68
  ToolbarProps,
@@ -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
- active: boolean;
155
- onPress: () => void;
156
- label: string;
157
- }) => React.ReactElement;
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.