@domenico-esposito/react-native-markdown-editor 0.1.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.
Files changed (76) hide show
  1. package/.eslintrc.js +5 -0
  2. package/README.md +265 -0
  3. package/build/MarkdownRenderer.d.ts +12 -0
  4. package/build/MarkdownRenderer.d.ts.map +1 -0
  5. package/build/MarkdownRenderer.js +165 -0
  6. package/build/MarkdownRenderer.js.map +1 -0
  7. package/build/MarkdownTextInput.d.ts +10 -0
  8. package/build/MarkdownTextInput.d.ts.map +1 -0
  9. package/build/MarkdownTextInput.js +233 -0
  10. package/build/MarkdownTextInput.js.map +1 -0
  11. package/build/MarkdownToolbar.d.ts +11 -0
  12. package/build/MarkdownToolbar.d.ts.map +1 -0
  13. package/build/MarkdownToolbar.js +98 -0
  14. package/build/MarkdownToolbar.js.map +1 -0
  15. package/build/index.d.ts +14 -0
  16. package/build/index.d.ts.map +1 -0
  17. package/build/index.js +11 -0
  18. package/build/index.js.map +1 -0
  19. package/build/markdownCore.types.d.ts +321 -0
  20. package/build/markdownCore.types.d.ts.map +1 -0
  21. package/build/markdownCore.types.js +2 -0
  22. package/build/markdownCore.types.js.map +1 -0
  23. package/build/markdownHighlight.d.ts +31 -0
  24. package/build/markdownHighlight.d.ts.map +1 -0
  25. package/build/markdownHighlight.js +378 -0
  26. package/build/markdownHighlight.js.map +1 -0
  27. package/build/markdownHighlight.types.d.ts +48 -0
  28. package/build/markdownHighlight.types.d.ts.map +1 -0
  29. package/build/markdownHighlight.types.js +9 -0
  30. package/build/markdownHighlight.types.js.map +1 -0
  31. package/build/markdownParser.d.ts +16 -0
  32. package/build/markdownParser.d.ts.map +1 -0
  33. package/build/markdownParser.js +309 -0
  34. package/build/markdownParser.js.map +1 -0
  35. package/build/markdownRendererDefaults.d.ts +113 -0
  36. package/build/markdownRendererDefaults.d.ts.map +1 -0
  37. package/build/markdownRendererDefaults.js +174 -0
  38. package/build/markdownRendererDefaults.js.map +1 -0
  39. package/build/markdownSegment.types.d.ts +22 -0
  40. package/build/markdownSegment.types.d.ts.map +1 -0
  41. package/build/markdownSegment.types.js +2 -0
  42. package/build/markdownSegment.types.js.map +1 -0
  43. package/build/markdownSegmentDefaults.d.ts +43 -0
  44. package/build/markdownSegmentDefaults.d.ts.map +1 -0
  45. package/build/markdownSegmentDefaults.js +176 -0
  46. package/build/markdownSegmentDefaults.js.map +1 -0
  47. package/build/markdownSyntaxUtils.d.ts +58 -0
  48. package/build/markdownSyntaxUtils.d.ts.map +1 -0
  49. package/build/markdownSyntaxUtils.js +98 -0
  50. package/build/markdownSyntaxUtils.js.map +1 -0
  51. package/build/markdownToolbarActions.d.ts +12 -0
  52. package/build/markdownToolbarActions.d.ts.map +1 -0
  53. package/build/markdownToolbarActions.js +212 -0
  54. package/build/markdownToolbarActions.js.map +1 -0
  55. package/build/useMarkdownEditor.d.ts +10 -0
  56. package/build/useMarkdownEditor.d.ts.map +1 -0
  57. package/build/useMarkdownEditor.js +219 -0
  58. package/build/useMarkdownEditor.js.map +1 -0
  59. package/jest.config.js +10 -0
  60. package/package.json +45 -0
  61. package/src/MarkdownRenderer.tsx +240 -0
  62. package/src/MarkdownTextInput.tsx +263 -0
  63. package/src/MarkdownToolbar.tsx +126 -0
  64. package/src/index.ts +31 -0
  65. package/src/markdownCore.types.ts +405 -0
  66. package/src/markdownHighlight.ts +413 -0
  67. package/src/markdownHighlight.types.ts +75 -0
  68. package/src/markdownParser.ts +345 -0
  69. package/src/markdownRendererDefaults.tsx +207 -0
  70. package/src/markdownSegment.types.ts +24 -0
  71. package/src/markdownSegmentDefaults.tsx +208 -0
  72. package/src/markdownSyntaxUtils.ts +139 -0
  73. package/src/markdownToolbarActions.ts +296 -0
  74. package/src/useMarkdownEditor.ts +265 -0
  75. package/tsconfig.json +9 -0
  76. package/tsconfig.test.json +8 -0
@@ -0,0 +1,405 @@
1
+ import type React from 'react';
2
+ import type { ComponentType, ReactNode } from 'react';
3
+ import type { StyleProp, TextInputProps, TextStyle, ViewStyle } from 'react-native';
4
+ import type { HighlightSegment } from './markdownHighlight.types';
5
+ import type { SegmentComponentMap } from './markdownSegment.types';
6
+
7
+ export type MarkdownTextNode = {
8
+ type: 'text';
9
+ content: string;
10
+ };
11
+
12
+ export type MarkdownBoldNode = {
13
+ type: 'bold';
14
+ children: MarkdownInlineNode[];
15
+ };
16
+
17
+ export type MarkdownItalicNode = {
18
+ type: 'italic';
19
+ children: MarkdownInlineNode[];
20
+ };
21
+
22
+ export type MarkdownInlineCodeNode = {
23
+ type: 'code';
24
+ content: string;
25
+ };
26
+
27
+ export type MarkdownLinkNode = {
28
+ type: 'link';
29
+ href: string;
30
+ children: MarkdownInlineNode[];
31
+ };
32
+
33
+ export type MarkdownStrikethroughNode = {
34
+ type: 'strikethrough';
35
+ children: MarkdownInlineNode[];
36
+ };
37
+
38
+ export type MarkdownImageNode = {
39
+ type: 'image';
40
+ src: string;
41
+ alt: string;
42
+ title?: string;
43
+ };
44
+
45
+ export type MarkdownInlineNode = MarkdownTextNode | MarkdownBoldNode | MarkdownItalicNode | MarkdownStrikethroughNode | MarkdownInlineCodeNode | MarkdownLinkNode | MarkdownImageNode;
46
+
47
+ export type MarkdownParagraphNode = {
48
+ type: 'paragraph';
49
+ children: MarkdownInlineNode[];
50
+ };
51
+
52
+ export type MarkdownHeadingNode = {
53
+ type: 'heading';
54
+ level: 1 | 2 | 3 | 4 | 5 | 6;
55
+ children: MarkdownInlineNode[];
56
+ };
57
+
58
+ export type MarkdownCodeBlockNode = {
59
+ type: 'codeBlock';
60
+ language?: string;
61
+ content: string;
62
+ };
63
+
64
+ export type MarkdownBlockquoteNode = {
65
+ type: 'blockquote';
66
+ children: MarkdownInlineNode[];
67
+ };
68
+
69
+ export type MarkdownHorizontalRuleNode = {
70
+ type: 'horizontalRule';
71
+ };
72
+
73
+ export type MarkdownSpacerNode = {
74
+ type: 'spacer';
75
+ };
76
+
77
+ export type MarkdownListNode = {
78
+ type: 'list';
79
+ ordered: boolean;
80
+ items: MarkdownInlineNode[][];
81
+ };
82
+
83
+ export type MarkdownListItemNode = {
84
+ type: 'listItem';
85
+ ordered: boolean;
86
+ index: number;
87
+ children: MarkdownInlineNode[];
88
+ };
89
+
90
+ export type MarkdownBlockNode = MarkdownParagraphNode | MarkdownHeadingNode | MarkdownCodeBlockNode | MarkdownBlockquoteNode | MarkdownHorizontalRuleNode | MarkdownSpacerNode | MarkdownListNode;
91
+
92
+ export type MarkdownRootNode = {
93
+ type: 'root';
94
+ children: MarkdownBlockNode[];
95
+ };
96
+
97
+ export type MarkdownNode = MarkdownInlineNode | MarkdownBlockNode | MarkdownListItemNode | MarkdownRootNode;
98
+
99
+ export type MarkdownTag =
100
+ | 'root'
101
+ | 'paragraph'
102
+ | 'text'
103
+ | 'heading1'
104
+ | 'heading2'
105
+ | 'heading3'
106
+ | 'heading4'
107
+ | 'heading5'
108
+ | 'heading6'
109
+ | 'bold'
110
+ | 'italic'
111
+ | 'strikethrough'
112
+ | 'inlineCode'
113
+ | 'codeBlock'
114
+ | 'blockquote'
115
+ | 'horizontalRule'
116
+ | 'unorderedList'
117
+ | 'orderedList'
118
+ | 'listItem'
119
+ | 'link'
120
+ | 'image'
121
+ | 'spacer';
122
+
123
+ /**
124
+ * Common props shared by every custom renderer component.
125
+ * Unlike segment components (editor), renderer components are NOT constrained
126
+ * to Text — they can use View, Image, or any React Native component.
127
+ */
128
+ export type RendererBaseProps = {
129
+ children?: ReactNode;
130
+ };
131
+
132
+ export type RootRendererProps = RendererBaseProps & {
133
+ type: 'root';
134
+ style?: StyleProp<ViewStyle>;
135
+ };
136
+
137
+ export type ParagraphRendererProps = RendererBaseProps & {
138
+ type: 'paragraph';
139
+ };
140
+
141
+ export type TextRendererProps = RendererBaseProps & {
142
+ type: 'text';
143
+ text: string;
144
+ };
145
+
146
+ export type HeadingRendererProps<L extends 1 | 2 | 3 | 4 | 5 | 6> = RendererBaseProps & {
147
+ type: `heading${L}`;
148
+ level: L;
149
+ };
150
+
151
+ export type BoldRendererProps = RendererBaseProps & {
152
+ type: 'bold';
153
+ };
154
+
155
+ export type ItalicRendererProps = RendererBaseProps & {
156
+ type: 'italic';
157
+ };
158
+
159
+ export type StrikethroughRendererProps = RendererBaseProps & {
160
+ type: 'strikethrough';
161
+ };
162
+
163
+ export type InlineCodeRendererProps = RendererBaseProps & {
164
+ type: 'inlineCode';
165
+ text: string;
166
+ };
167
+
168
+ export type CodeBlockRendererProps = RendererBaseProps & {
169
+ type: 'codeBlock';
170
+ text: string;
171
+ language?: string;
172
+ };
173
+
174
+ export type BlockquoteRendererProps = RendererBaseProps & {
175
+ type: 'blockquote';
176
+ };
177
+
178
+ export type HorizontalRuleRendererProps = RendererBaseProps & {
179
+ type: 'horizontalRule';
180
+ };
181
+
182
+ export type UnorderedListRendererProps = RendererBaseProps & {
183
+ type: 'unorderedList';
184
+ ordered: false;
185
+ };
186
+
187
+ export type OrderedListRendererProps = RendererBaseProps & {
188
+ type: 'orderedList';
189
+ ordered: true;
190
+ };
191
+
192
+ export type ListItemRendererProps = RendererBaseProps & {
193
+ type: 'listItem';
194
+ ordered: boolean;
195
+ index: number;
196
+ };
197
+
198
+ export type LinkRendererProps = RendererBaseProps & {
199
+ type: 'link';
200
+ href: string;
201
+ };
202
+
203
+ export type ImageRendererProps = RendererBaseProps & {
204
+ type: 'image';
205
+ src: string;
206
+ alt: string;
207
+ title?: string;
208
+ };
209
+
210
+ export type SpacerRendererProps = RendererBaseProps & {
211
+ type: 'spacer';
212
+ };
213
+
214
+ export type RendererPropsByTag = {
215
+ root: RootRendererProps;
216
+ paragraph: ParagraphRendererProps;
217
+ text: TextRendererProps;
218
+ heading1: HeadingRendererProps<1>;
219
+ heading2: HeadingRendererProps<2>;
220
+ heading3: HeadingRendererProps<3>;
221
+ heading4: HeadingRendererProps<4>;
222
+ heading5: HeadingRendererProps<5>;
223
+ heading6: HeadingRendererProps<6>;
224
+ bold: BoldRendererProps;
225
+ italic: ItalicRendererProps;
226
+ strikethrough: StrikethroughRendererProps;
227
+ inlineCode: InlineCodeRendererProps;
228
+ codeBlock: CodeBlockRendererProps;
229
+ blockquote: BlockquoteRendererProps;
230
+ horizontalRule: HorizontalRuleRendererProps;
231
+ unorderedList: UnorderedListRendererProps;
232
+ orderedList: OrderedListRendererProps;
233
+ listItem: ListItemRendererProps;
234
+ link: LinkRendererProps;
235
+ image: ImageRendererProps;
236
+ spacer: SpacerRendererProps;
237
+ };
238
+
239
+ export type RendererComponentProps = RendererPropsByTag[MarkdownTag];
240
+
241
+ /** Complete map of renderer components (one per tag). Used internally. */
242
+ export type MarkdownComponentMap = {
243
+ [K in MarkdownTag]: ComponentType<RendererPropsByTag[K]>;
244
+ };
245
+
246
+ /**
247
+ * Map of custom renderer components keyed by tag.
248
+ * Only the tags you want to override need to be specified;
249
+ * unspecified tags fall back to defaults in `markdownRendererDefaults.tsx`.
250
+ */
251
+ export type RendererComponentMap = Partial<{ [K in MarkdownTag]: ComponentType<RendererPropsByTag[K]> }>;
252
+
253
+ export type MarkdownSelection = {
254
+ start: number;
255
+ end: number;
256
+ };
257
+
258
+ export type MarkdownInlineToolbarAction = 'bold' | 'italic' | 'strikethrough' | 'code';
259
+
260
+ export type MarkdownBlockToolbarAction =
261
+ | 'heading'
262
+ | 'heading1'
263
+ | 'heading2'
264
+ | 'heading3'
265
+ | 'heading4'
266
+ | 'heading5'
267
+ | 'heading6'
268
+ | 'quote'
269
+ | 'unorderedList'
270
+ | 'orderedList'
271
+ | 'divider'
272
+ | 'codeBlock';
273
+
274
+ export type MarkdownToolbarAction = MarkdownInlineToolbarAction | MarkdownBlockToolbarAction | 'image';
275
+
276
+ export type MarkdownToolbarActionResult = {
277
+ text: string;
278
+ selection: MarkdownSelection;
279
+ activeInlineActions: MarkdownInlineToolbarAction[];
280
+ };
281
+
282
+ export type MarkdownRendererProps = {
283
+ markdown: string;
284
+ components?: RendererComponentMap;
285
+ style?: StyleProp<ViewStyle>;
286
+ /**
287
+ * Enabled markdown features.
288
+ * When provided, only the corresponding markdown features are rendered;
289
+ * disabled syntax is treated as plain text.
290
+ */
291
+ features?: MarkdownToolbarAction[];
292
+ };
293
+
294
+ // ---------------------------------------------------------------------------
295
+ // useMarkdownEditor hook
296
+ // ---------------------------------------------------------------------------
297
+
298
+ export type UseMarkdownEditorOptions = {
299
+ /** Current markdown text (controlled). */
300
+ value: string;
301
+ /** Called when the text changes. */
302
+ onChangeText: (nextValue: string) => void;
303
+ /** Called when the cursor/selection changes. */
304
+ onSelectionChange?: (selection: MarkdownSelection) => void;
305
+ /** Called after a toolbar action is applied. */
306
+ onToolbarAction?: (action: MarkdownToolbarAction, result: MarkdownToolbarActionResult) => void;
307
+ /**
308
+ * Enabled markdown features.
309
+ * Controls which toolbar buttons appear AND which syntax is highlighted
310
+ * in MarkdownTextInput. Defaults to all features when omitted.
311
+ */
312
+ features?: MarkdownToolbarAction[];
313
+ };
314
+
315
+ /**
316
+ * Shared editor state returned by `useMarkdownEditor`.
317
+ * Pass as `editor` prop to both `MarkdownTextInput` and `MarkdownToolbar`.
318
+ */
319
+ export type MarkdownEditorHandle = {
320
+ /** Enabled markdown features. */
321
+ features: MarkdownToolbarAction[];
322
+ /** Current text value. */
323
+ value: string;
324
+ /** Current cursor/selection range. */
325
+ selection: MarkdownSelection;
326
+ /** Currently active inline formatting actions. */
327
+ activeInlineActions: MarkdownInlineToolbarAction[];
328
+ /** Image info when cursor is on an image segment, null otherwise. */
329
+ activeImageInfo: MarkdownImageInfo | null;
330
+ /** Image info set by the toolbar when the user taps the image button (modal state). */
331
+ imageInfo: MarkdownImageInfo | null;
332
+ /** Sets `imageInfo` to the current `activeImageInfo`. Called internally by the toolbar. */
333
+ openImageInfo: () => void;
334
+ /** Clears `imageInfo` (closes the modal). */
335
+ dismissImageInfo: () => void;
336
+ /** Highlighted segments for live preview rendering. */
337
+ highlightedSegments: HighlightSegment[];
338
+ /** Ref to the underlying TextInput. */
339
+ inputRef: React.RefObject<import('react-native').TextInput | null>;
340
+ /** Internal handler - consumed by MarkdownTextInput. */
341
+ handleChangeText: (nextValue: string) => void;
342
+ /** Internal handler - consumed by MarkdownTextInput. */
343
+ handleSelectionChange: (event: { nativeEvent: { selection: MarkdownSelection } }) => void;
344
+ /** Handler for toolbar action presses - consumed by MarkdownToolbar. */
345
+ handleToolbarAction: (action: MarkdownToolbarAction) => void;
346
+ /** Removes the image markdown at the current cursor position (if any). */
347
+ deleteActiveImage: () => void;
348
+ };
349
+
350
+ // ---------------------------------------------------------------------------
351
+ // Component props
352
+ // ---------------------------------------------------------------------------
353
+
354
+ export type MarkdownToolbarButtonState = {
355
+ action: MarkdownToolbarAction;
356
+ active: boolean;
357
+ };
358
+
359
+ export type MarkdownToolbarProps = {
360
+ /** Shared editor state from useMarkdownEditor. */
361
+ editor?: MarkdownEditorHandle;
362
+ features?: MarkdownToolbarAction[];
363
+ activeInlineActions?: MarkdownInlineToolbarAction[];
364
+ onPressAction?: (action: MarkdownToolbarAction) => void;
365
+ /** Style for the toolbar container. */
366
+ style?: StyleProp<ViewStyle>;
367
+ /** Static style applied to every button, or a function receiving button state for per-button styling. */
368
+ buttonStyle?: StyleProp<ViewStyle> | ((state: MarkdownToolbarButtonState) => StyleProp<ViewStyle>);
369
+ /** Static text style applied to every button, or a function receiving button state for per-button styling. */
370
+ buttonTextStyle?: StyleProp<TextStyle> | ((state: MarkdownToolbarButtonState) => StyleProp<TextStyle>);
371
+ /** Additional style merged when a button is active. */
372
+ activeButtonStyle?: StyleProp<ViewStyle>;
373
+ /** Additional text style merged when a button is active. */
374
+ activeButtonTextStyle?: StyleProp<TextStyle>;
375
+ /** Additional style merged when a button is inactive. */
376
+ inactiveButtonStyle?: StyleProp<ViewStyle>;
377
+ /** Additional text style merged when a button is inactive. */
378
+ inactiveButtonTextStyle?: StyleProp<TextStyle>;
379
+ renderButton?: (params: { action: MarkdownToolbarAction; label: string; active: boolean; onPress: () => void }) => ReactNode;
380
+ };
381
+
382
+ export type MarkdownImageInfo = {
383
+ src: string;
384
+ alt: string;
385
+ title?: string;
386
+ /** Start character offset of the image markdown in the text. */
387
+ start: number;
388
+ /** End character offset of the image markdown in the text. */
389
+ end: number;
390
+ };
391
+
392
+ export type MarkdownTextInputProps = Omit<TextInputProps, 'value' | 'onChangeText' | 'onSelectionChange' | 'multiline'> & {
393
+ /** Shared editor state from useMarkdownEditor. */
394
+ editor: MarkdownEditorHandle;
395
+ /** Style for the outer container View. */
396
+ style?: StyleProp<ViewStyle>;
397
+ /** Style for the inner TextInput. */
398
+ textInputStyle?: StyleProp<TextStyle>;
399
+ /**
400
+ * Custom components to render specific segment types.
401
+ * Each component must be or extend React Native's Text.
402
+ * Unspecified types fall back to the default Text renderer.
403
+ */
404
+ segmentComponents?: SegmentComponentMap;
405
+ };