@nanogiants/react-native-render-html 1.0.0

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/dist/index.mjs ADDED
@@ -0,0 +1,1068 @@
1
+ // src/RenderHTML.tsx
2
+ import { parseDocument } from "htmlparser2";
3
+ import { useMemo as useMemo2 } from "react";
4
+ import {
5
+ View as View6
6
+ } from "react-native";
7
+
8
+ // src/context/HtmlProvider.tsx
9
+ import { createContext, useCallback, useContext, useMemo } from "react";
10
+ import { Image, Text } from "react-native";
11
+ import { jsx } from "react/jsx-runtime";
12
+ var HtmlContext = createContext(null);
13
+ var HtmlProvider = (options) => {
14
+ const baseStyle = useMemo(() => {
15
+ return {
16
+ ...options.baseStyle
17
+ };
18
+ }, [options.baseStyle]);
19
+ const tagsStyles = useMemo(() => {
20
+ return {
21
+ // BLOCK RENDERER TAGS
22
+ thead: {
23
+ text: {
24
+ ...baseStyle,
25
+ ...options.tagStyles?.thead?.text
26
+ },
27
+ block: {
28
+ ...options.tagStyles?.thead?.block
29
+ }
30
+ },
31
+ tbody: {
32
+ text: {
33
+ ...baseStyle,
34
+ ...options.tagStyles?.tbody?.text
35
+ },
36
+ block: {
37
+ ...options.tagStyles?.tbody?.block
38
+ }
39
+ },
40
+ tfoot: {
41
+ text: {
42
+ ...baseStyle,
43
+ ...options.tagStyles?.tfoot?.text
44
+ },
45
+ block: {
46
+ ...options.tagStyles?.tfoot?.block
47
+ }
48
+ },
49
+ blockquote: {
50
+ text: {
51
+ ...baseStyle,
52
+ fontStyle: "italic",
53
+ ...options.tagStyles?.blockquote?.text
54
+ },
55
+ block: {
56
+ borderLeftWidth: 4,
57
+ borderLeftColor: "black",
58
+ marginLeft: 8,
59
+ paddingLeft: 12,
60
+ backgroundColor: "#cccccc",
61
+ ...options.tagStyles?.blockquote?.block
62
+ }
63
+ },
64
+ ul: {
65
+ text: {
66
+ ...baseStyle,
67
+ ...options.tagStyles?.ul?.text
68
+ },
69
+ block: {
70
+ paddingLeft: 12,
71
+ paddingVertical: 0,
72
+ gap: options.listGap ?? 0,
73
+ ...options.tagStyles?.ul?.block
74
+ }
75
+ },
76
+ ol: {
77
+ text: {
78
+ ...baseStyle,
79
+ ...options.tagStyles?.ol?.text
80
+ },
81
+ block: {
82
+ paddingLeft: 12,
83
+ paddingVertical: 0,
84
+ gap: options.listGap ?? 0,
85
+ ...options.tagStyles?.ol?.block
86
+ }
87
+ },
88
+ dl: {
89
+ text: {
90
+ ...baseStyle,
91
+ ...options.tagStyles?.dl?.text
92
+ },
93
+ block: {
94
+ ...options.tagStyles?.dl?.block
95
+ }
96
+ },
97
+ div: {
98
+ text: {
99
+ ...baseStyle,
100
+ ...options.tagStyles?.div?.text
101
+ },
102
+ block: {
103
+ ...options.tagStyles?.div?.block
104
+ }
105
+ },
106
+ main: {
107
+ text: {
108
+ ...baseStyle,
109
+ ...options.tagStyles?.main?.text
110
+ },
111
+ block: {
112
+ ...options.tagStyles?.main?.block
113
+ }
114
+ },
115
+ section: {
116
+ text: {
117
+ ...baseStyle,
118
+ ...options.tagStyles?.section?.text
119
+ },
120
+ block: {
121
+ marginVertical: 8,
122
+ ...options.tagStyles?.section?.block
123
+ }
124
+ },
125
+ article: {
126
+ text: {
127
+ ...baseStyle,
128
+ ...options.tagStyles?.article?.text
129
+ },
130
+ block: {
131
+ marginVertical: 8,
132
+ ...options.tagStyles?.article?.block
133
+ }
134
+ },
135
+ aside: {
136
+ text: {
137
+ ...baseStyle,
138
+ ...options.tagStyles?.aside?.text
139
+ },
140
+ block: {
141
+ ...options.tagStyles?.aside?.block
142
+ }
143
+ },
144
+ nav: {
145
+ text: {
146
+ ...baseStyle,
147
+ ...options.tagStyles?.nav?.text
148
+ },
149
+ block: {
150
+ ...options.tagStyles?.nav?.block
151
+ }
152
+ },
153
+ header: {
154
+ text: {
155
+ ...baseStyle,
156
+ ...options.tagStyles?.header?.text
157
+ },
158
+ block: {
159
+ marginBottom: 8,
160
+ ...options.tagStyles?.header?.block
161
+ }
162
+ },
163
+ footer: {
164
+ text: {
165
+ ...baseStyle,
166
+ ...options.tagStyles?.footer?.text
167
+ },
168
+ block: {
169
+ marginTop: 8,
170
+ ...options.tagStyles?.footer?.block
171
+ }
172
+ },
173
+ hr: {
174
+ text: {
175
+ ...options.tagStyles?.hr?.text
176
+ },
177
+ block: {
178
+ borderBottomWidth: 1,
179
+ borderBottomColor: "black",
180
+ marginVertical: 12,
181
+ ...options.tagStyles?.hr?.block
182
+ }
183
+ },
184
+ br: {
185
+ text: {
186
+ ...options.tagStyles?.br?.text
187
+ },
188
+ block: {
189
+ height: 16,
190
+ ...options.tagStyles?.br?.block
191
+ }
192
+ },
193
+ img: {
194
+ text: {
195
+ ...options.tagStyles?.img?.text
196
+ },
197
+ block: {
198
+ marginVertical: 8,
199
+ flex: 1,
200
+ width: void 0,
201
+ height: 100,
202
+ ...options.tagStyles?.img?.block
203
+ }
204
+ },
205
+ table: {
206
+ text: {
207
+ ...options.tagStyles?.table?.text
208
+ },
209
+ block: {
210
+ marginVertical: 8,
211
+ ...options.tagStyles?.table?.block
212
+ }
213
+ },
214
+ tr: {
215
+ text: {
216
+ ...baseStyle,
217
+ ...options.tagStyles?.tr?.text
218
+ },
219
+ block: {
220
+ ...options.tagStyles?.tr?.block
221
+ }
222
+ },
223
+ // TEXT RENDERER TAGS
224
+ h1: {
225
+ text: {
226
+ ...baseStyle,
227
+ fontSize: 32,
228
+ lineHeight: 38.4,
229
+ ...options.tagStyles?.h1?.text
230
+ },
231
+ block: {
232
+ marginTop: 12,
233
+ marginBottom: 8,
234
+ ...options.tagStyles?.h1?.block
235
+ }
236
+ },
237
+ h2: {
238
+ text: {
239
+ ...baseStyle,
240
+ fontSize: 24,
241
+ lineHeight: 28.8,
242
+ ...options.tagStyles?.h2?.text
243
+ },
244
+ block: {
245
+ marginTop: 10,
246
+ marginBottom: 6,
247
+ ...options.tagStyles?.h2?.block
248
+ }
249
+ },
250
+ h3: {
251
+ text: {
252
+ ...baseStyle,
253
+ fontSize: 18.72,
254
+ lineHeight: 22.46,
255
+ ...options.tagStyles?.h3?.text
256
+ },
257
+ block: {
258
+ marginTop: 8,
259
+ marginBottom: 6,
260
+ ...options.tagStyles?.h3?.block
261
+ }
262
+ },
263
+ h4: {
264
+ text: {
265
+ ...baseStyle,
266
+ fontSize: 16,
267
+ lineHeight: 19.2,
268
+ ...options.tagStyles?.h4?.text
269
+ },
270
+ block: {
271
+ marginTop: 8,
272
+ marginBottom: 4,
273
+ ...options.tagStyles?.h4?.block
274
+ }
275
+ },
276
+ h5: {
277
+ text: {
278
+ ...baseStyle,
279
+ fontSize: 13.28,
280
+ lineHeight: 15.94,
281
+ ...options.tagStyles?.h5?.text
282
+ },
283
+ block: {
284
+ marginTop: 6,
285
+ marginBottom: 4,
286
+ ...options.tagStyles?.h5?.block
287
+ }
288
+ },
289
+ h6: {
290
+ text: {
291
+ ...baseStyle,
292
+ fontSize: 10.72,
293
+ lineHeight: 12.86,
294
+ ...options.tagStyles?.h6?.text
295
+ },
296
+ block: {
297
+ marginTop: 6,
298
+ marginBottom: 4,
299
+ ...options.tagStyles?.h6?.block
300
+ }
301
+ },
302
+ p: {
303
+ text: {
304
+ ...baseStyle,
305
+ ...options.tagStyles?.p?.text
306
+ },
307
+ block: {
308
+ marginTop: 8,
309
+ marginBottom: 8,
310
+ ...options.tagStyles?.p?.block
311
+ }
312
+ },
313
+ a: {
314
+ text: {
315
+ ...baseStyle,
316
+ textDecorationLine: "underline",
317
+ ...options.tagStyles?.a?.text
318
+ }
319
+ },
320
+ li: {
321
+ text: {
322
+ ...baseStyle,
323
+ ...options.tagStyles?.li?.text
324
+ },
325
+ block: {
326
+ paddingLeft: 0,
327
+ ...options.tagStyles?.li?.block
328
+ }
329
+ },
330
+ pre: {
331
+ text: {
332
+ ...baseStyle,
333
+ fontFamily: "monospace",
334
+ backgroundColor: "#bbbbbb",
335
+ ...options.tagStyles?.pre?.text
336
+ }
337
+ },
338
+ code: {
339
+ text: {
340
+ ...baseStyle,
341
+ fontFamily: "monospace",
342
+ backgroundColor: "#bbbbbb",
343
+ ...options.tagStyles?.code?.text
344
+ }
345
+ },
346
+ // inline text styles
347
+ b: {
348
+ text: {
349
+ fontWeight: "bold",
350
+ ...options.tagStyles?.b?.text
351
+ }
352
+ },
353
+ strong: {
354
+ text: {
355
+ fontWeight: "bold",
356
+ ...options.tagStyles?.strong?.text
357
+ }
358
+ },
359
+ i: {
360
+ text: {
361
+ fontStyle: "italic",
362
+ ...options.tagStyles?.i?.text
363
+ }
364
+ },
365
+ em: {
366
+ text: {
367
+ fontStyle: "italic",
368
+ ...options.tagStyles?.em?.text
369
+ }
370
+ },
371
+ u: {
372
+ text: {
373
+ textDecorationLine: "underline",
374
+ ...options.tagStyles?.u?.text
375
+ }
376
+ },
377
+ mark: {
378
+ text: {
379
+ ...baseStyle,
380
+ backgroundColor: "#fff59d",
381
+ ...options.tagStyles?.mark?.text
382
+ }
383
+ },
384
+ small: {
385
+ text: {
386
+ ...baseStyle,
387
+ fontSize: Math.max(baseStyle.fontSize - 2, 12),
388
+ ...options.tagStyles?.small?.text
389
+ }
390
+ },
391
+ s: {
392
+ text: {
393
+ ...baseStyle,
394
+ textDecorationLine: "line-through",
395
+ ...options.tagStyles?.s?.text
396
+ }
397
+ },
398
+ del: {
399
+ text: {
400
+ ...baseStyle,
401
+ textDecorationLine: "line-through",
402
+ ...options.tagStyles?.del?.text
403
+ }
404
+ },
405
+ sup: {
406
+ text: {
407
+ ...baseStyle,
408
+ textAlignVertical: "top",
409
+ fontSize: Math.max(baseStyle.fontSize - 4, 10),
410
+ ...options.tagStyles?.sup?.text
411
+ }
412
+ },
413
+ sub: {
414
+ text: {
415
+ ...baseStyle,
416
+ textAlignVertical: "bottom",
417
+ fontSize: Math.max(baseStyle.fontSize - 4, 10),
418
+ ...options.tagStyles?.sub?.text
419
+ }
420
+ },
421
+ th: {
422
+ text: {
423
+ ...baseStyle,
424
+ fontWeight: "bold",
425
+ padding: 8,
426
+ borderWidth: 1,
427
+ borderColor: "black",
428
+ ...options.tagStyles?.th?.text
429
+ }
430
+ },
431
+ td: {
432
+ text: {
433
+ ...baseStyle,
434
+ padding: 8,
435
+ ...options.tagStyles?.td?.text
436
+ }
437
+ },
438
+ dt: {
439
+ text: {
440
+ ...baseStyle,
441
+ ...options.tagStyles?.dt?.text
442
+ }
443
+ },
444
+ dd: {
445
+ text: {
446
+ ...baseStyle,
447
+ marginLeft: 20,
448
+ ...options.tagStyles?.dd?.text
449
+ }
450
+ },
451
+ span: {
452
+ text: {
453
+ ...baseStyle,
454
+ ...options.tagStyles?.span?.text
455
+ }
456
+ }
457
+ };
458
+ }, [baseStyle, options.tagStyles, options.listGap]);
459
+ const getStyle = useCallback(
460
+ (node) => {
461
+ if (node.type === "tag") {
462
+ const classStyle = node.attribs.class ? options.classesStyles?.[node.attribs.class] || {} : {};
463
+ return {
464
+ text: {
465
+ ...tagsStyles[node.tagName].text,
466
+ ...classStyle.text
467
+ },
468
+ block: {
469
+ ...tagsStyles[node.tagName].block ?? {},
470
+ ...classStyle.block
471
+ }
472
+ };
473
+ }
474
+ return {
475
+ text: {},
476
+ block: {}
477
+ };
478
+ },
479
+ [tagsStyles, options.classesStyles]
480
+ );
481
+ const renderImage = useMemo(() => {
482
+ if (options.renderImage) {
483
+ return options.renderImage;
484
+ }
485
+ return (props) => /* @__PURE__ */ jsx(Image, { ...props });
486
+ }, [options.renderImage]);
487
+ const renderText = useMemo(() => {
488
+ if (options.renderTextComponent) {
489
+ return options.renderTextComponent;
490
+ }
491
+ return (props) => /* @__PURE__ */ jsx(Text, { ...props });
492
+ }, [options.renderTextComponent]);
493
+ const providerValue = useMemo(
494
+ () => ({
495
+ getStyle,
496
+ baseStyle,
497
+ tagsStyles,
498
+ overrideExternalLinkTintColor: options.overrideExternalLinkTintColor,
499
+ onLinkPress: options.onLinkPress,
500
+ markerColor: options.markerColor,
501
+ renderImage,
502
+ renderText
503
+ }),
504
+ [
505
+ getStyle,
506
+ baseStyle,
507
+ tagsStyles,
508
+ options.overrideExternalLinkTintColor,
509
+ options.onLinkPress,
510
+ options.markerColor,
511
+ renderImage,
512
+ renderText
513
+ ]
514
+ );
515
+ return /* @__PURE__ */ jsx(HtmlContext.Provider, { value: providerValue, children: options.children });
516
+ };
517
+ var useHtmlContext = () => {
518
+ const context = useContext(HtmlContext);
519
+ if (!context) {
520
+ throw new Error("useHtml must be used within a HtmlProvider");
521
+ }
522
+ return context;
523
+ };
524
+
525
+ // src/HTMLValidator.ts
526
+ var cleanUlNodesUtil = (nodes) => {
527
+ return nodes.map((node) => {
528
+ if (node.type !== "tag") {
529
+ return node;
530
+ }
531
+ if (node.name === "ul" || node.name === "ol") {
532
+ if (!node.children) {
533
+ return node;
534
+ }
535
+ node.children = node.children.filter((child) => {
536
+ return child.type !== "text" || child.type === "text" && child.data.trim() !== "";
537
+ });
538
+ }
539
+ if (node.children) {
540
+ node.children = cleanUlNodesUtil(node.children);
541
+ return node;
542
+ }
543
+ return node;
544
+ });
545
+ };
546
+ var HTMLValidator = class {
547
+ constructor(nodes) {
548
+ this.nodes = nodes;
549
+ }
550
+ cleanup() {
551
+ this.nodes = cleanUlNodesUtil(this.nodes);
552
+ return this.nodes;
553
+ }
554
+ };
555
+
556
+ // src/renderers/_NodeRenderer.tsx
557
+ import { Text as Text3, View as View5 } from "react-native";
558
+
559
+ // src/context/AlignedWidthItem.tsx
560
+ import { View } from "react-native";
561
+
562
+ // src/context/AlignedWidthProvider.tsx
563
+ import { createContext as createContext2, useContext as useContext2, useState } from "react";
564
+ import { jsx as jsx2 } from "react/jsx-runtime";
565
+ var AlignedWidthContext = createContext2(void 0);
566
+ var AlignedWidthProvider = ({ children }) => {
567
+ const [colWidths, setColWidths] = useState({});
568
+ const getLayoutHandlerForIndex = (colIdx) => {
569
+ return (event) => {
570
+ const { width } = event.nativeEvent.layout;
571
+ setColWidths((prev) => {
572
+ const previousWidth = prev[colIdx] || 0;
573
+ return {
574
+ ...prev,
575
+ [colIdx]: Math.max(previousWidth, width)
576
+ };
577
+ });
578
+ };
579
+ };
580
+ const getWidthStyle = (colIdx) => {
581
+ return {
582
+ minWidth: colWidths[colIdx] || void 0,
583
+ width: colWidths[colIdx] || void 0
584
+ };
585
+ };
586
+ return /* @__PURE__ */ jsx2(
587
+ AlignedWidthContext.Provider,
588
+ {
589
+ value: {
590
+ getLayoutHandlerForIndex,
591
+ getWidthStyle
592
+ },
593
+ children
594
+ }
595
+ );
596
+ };
597
+ var useAlignedWidth = () => {
598
+ const context = useContext2(AlignedWidthContext);
599
+ if (!context) {
600
+ throw new Error("useAlignedWidth must be used within a AlignedWidthProvider");
601
+ }
602
+ return context;
603
+ };
604
+
605
+ // src/context/AlignedWidthItem.tsx
606
+ import { jsx as jsx3 } from "react/jsx-runtime";
607
+ var AlignedWidthItem = ({ children, index, style }) => {
608
+ const { getLayoutHandlerForIndex, getWidthStyle } = useAlignedWidth();
609
+ return /* @__PURE__ */ jsx3(View, { style: [getWidthStyle(index), style], onLayout: getLayoutHandlerForIndex(index), children });
610
+ };
611
+
612
+ // src/types.ts
613
+ var rendererTypeMap = {
614
+ // Elements rendered with a View (block)
615
+ thead: "block",
616
+ tbody: "block",
617
+ tfoot: "block",
618
+ blockquote: "block",
619
+ ul: "block",
620
+ ol: "block",
621
+ dl: "block",
622
+ li: "block",
623
+ div: "block",
624
+ hr: "block",
625
+ br: "block",
626
+ pre: "block",
627
+ code: "block",
628
+ img: "block",
629
+ table: "block",
630
+ tr: "block",
631
+ dt: "block",
632
+ dd: "block",
633
+ p: "block",
634
+ h1: "block",
635
+ h2: "block",
636
+ h3: "block",
637
+ h4: "block",
638
+ h5: "block",
639
+ h6: "block",
640
+ main: "block",
641
+ section: "block",
642
+ article: "block",
643
+ aside: "block",
644
+ nav: "block",
645
+ header: "block",
646
+ footer: "block",
647
+ // All other elements are text
648
+ b: "text",
649
+ strong: "text",
650
+ i: "text",
651
+ em: "text",
652
+ u: "text",
653
+ mark: "text",
654
+ small: "text",
655
+ s: "text",
656
+ del: "text",
657
+ sup: "text",
658
+ sub: "text",
659
+ span: "text",
660
+ a: "text",
661
+ th: "text",
662
+ td: "text"
663
+ };
664
+
665
+ // src/utils.ts
666
+ var isTextRenderer = (node) => {
667
+ if (!node) {
668
+ return false;
669
+ }
670
+ if (node.type === "tag") {
671
+ return rendererTypeMap[node.tagName] === "text";
672
+ }
673
+ return node.type === "text";
674
+ };
675
+ var concatTextNodes = (children) => {
676
+ let result = "";
677
+ for (const child of children) {
678
+ if (child.type === "text") {
679
+ result += child.data;
680
+ }
681
+ if (child.type === "tag" && Array.isArray(child.children)) {
682
+ result += concatTextNodes(child.children);
683
+ }
684
+ }
685
+ return result;
686
+ };
687
+ var isList = (child) => {
688
+ return child.type === "tag" && (child.tagName === "ol" || child.tagName === "ul");
689
+ };
690
+ var isExternalURL = (url) => {
691
+ if (!url) {
692
+ return false;
693
+ }
694
+ return url.startsWith("https://") || url.startsWith("http://");
695
+ };
696
+
697
+ // src/renderers/_DefaultBlockRenderer.tsx
698
+ import { Text as Text2, View as View2 } from "react-native";
699
+ import { jsx as jsx4 } from "react/jsx-runtime";
700
+ import { createElement } from "react";
701
+ var DefaultBlockRenderer = ({
702
+ node,
703
+ viewProps = {},
704
+ textProps = {}
705
+ }) => {
706
+ const { getStyle } = useHtmlContext();
707
+ const { style: viewStyle, ...restViewProps } = viewProps;
708
+ const { style: textStyle, ...restTextProps } = textProps;
709
+ const groupedChildren = groupByInlineBlock(node.children);
710
+ return /* @__PURE__ */ jsx4(View2, { ...restViewProps, style: [getStyle(node).block, viewStyle], children: groupedChildren.map((childrenGroup, index) => {
711
+ if (childrenGroup.type === "text") {
712
+ return /* @__PURE__ */ createElement(
713
+ Text2,
714
+ {
715
+ ...restTextProps,
716
+ key: `${node.type}-${index}`,
717
+ style: [getStyle(node).text, textStyle]
718
+ },
719
+ /* @__PURE__ */ jsx4(NodesRenderer, { nodes: childrenGroup.nodes })
720
+ );
721
+ }
722
+ return /* @__PURE__ */ jsx4(NodesRenderer, { nodes: childrenGroup.nodes }, `${node.type}-${index}`);
723
+ }) });
724
+ };
725
+ function groupByInlineBlock(arr) {
726
+ const result = [];
727
+ let currentGroup = [];
728
+ for (const item of arr) {
729
+ const isTextNode = item.type === "text";
730
+ const isTextTag = item.type === "tag" && rendererTypeMap[item.name] === "text";
731
+ if (isTextNode || isTextTag) {
732
+ currentGroup.push(item);
733
+ } else {
734
+ if (currentGroup.length) {
735
+ result.push({
736
+ type: "text",
737
+ nodes: currentGroup
738
+ });
739
+ currentGroup = [];
740
+ }
741
+ result.push({
742
+ type: "block",
743
+ nodes: [item]
744
+ });
745
+ }
746
+ }
747
+ if (currentGroup.length) {
748
+ result.push({
749
+ type: "text",
750
+ nodes: currentGroup
751
+ });
752
+ }
753
+ return result;
754
+ }
755
+
756
+ // src/renderers/_DefaultTextRenderer.tsx
757
+ import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
758
+ var DefaultTextRenderer = ({ node, textProps = {} }) => {
759
+ const { style, ...restProps } = textProps;
760
+ const { getStyle, renderText } = useHtmlContext();
761
+ return /* @__PURE__ */ jsx5(Fragment, { children: renderText({
762
+ ...restProps,
763
+ style: [getStyle(node).text, style],
764
+ children: /* @__PURE__ */ jsx5(NodesRenderer, { nodes: node.children })
765
+ }) });
766
+ };
767
+
768
+ // src/assets.ts
769
+ var EXTERNAL_LINK_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IB2cksfwAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+kJCwwjMljq2h8AAAD4SURBVEjH7dY9SgNBGMbxn7vpIngXwc5SbG39KjxCLDyCnY0nUFGIH0Uu4CU8gXZ2iYU2IsRmiiCz+u4sBpQ8sLwMDPPf53mWYVnoP2sH44bn8evmXgfQM+5n1jW20lz6LXc9DDHFByYRR2foBw4/xkM64xLbuMYy1iNvN8H7N/mPMcg4Gab1KOeoCTQK7KuTkyluZ9LJgqrCTmqcYx932E3dtNJPjnJxwWqamzjoCqpxlYlrEIw7BGqChHqtWnRygb3STqKgkwS5KS0+egWdpnlUAmkDesJhlzuqMifNDdQU3Vr6hKPql4BesIKNFqBXvC3+Bf62PgFLzkKE8ZczlwAAAABJRU5ErkJggg==";
770
+ var UL_MARKER_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAwCAYAAAAsJjtLAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAACKADAAQAAAABAAAAMAAAAAAE1YgVAAAAZklEQVQ4EWNgGAWjITC4QoDxiYRE/H9GxgkM//8LIDuNkYHhAZDfAFJw/z8DgwKyJJzNyPiBCackSBXQVCa4ahwMaigAOgSH6QwgnzD9//+/EOolVHVAjf8YGBpRBUd5oyEw8CEAAGO5FkZ9+flOAAAAAElFTkSuQmCC";
771
+
772
+ // src/renderers/ATagRenderer.tsx
773
+ import { Fragment as Fragment2, jsx as jsx6, jsxs } from "react/jsx-runtime";
774
+ var ATagRenderer = ({ node }) => {
775
+ const { overrideExternalLinkTintColor, onLinkPress, getStyle, renderImage, renderText } = useHtmlContext();
776
+ const text = concatTextNodes(node.children);
777
+ if (!text) {
778
+ return null;
779
+ }
780
+ const style = getStyle(node);
781
+ const fontSize = style.text?.fontSize ?? 18;
782
+ const isExternal = node.attribs.href ? isExternalURL(node.attribs.href) : false;
783
+ return /* @__PURE__ */ jsx6(Fragment2, { children: renderText({
784
+ accessible: true,
785
+ onPress: () => {
786
+ onLinkPress?.({
787
+ url: node.attribs.href,
788
+ title: text
789
+ });
790
+ },
791
+ accessibilityRole: "link",
792
+ accessibilityHint: isExternal ? "External link" : void 0,
793
+ importantForAccessibility: "yes",
794
+ accessibilityElementsHidden: false,
795
+ style: style.text,
796
+ children: /* @__PURE__ */ jsxs(Fragment2, { children: [
797
+ text,
798
+ isExternal ? renderImage({
799
+ source: { uri: EXTERNAL_LINK_URI },
800
+ style: {
801
+ tintColor: overrideExternalLinkTintColor ?? style.text.color ?? "black",
802
+ height: fontSize,
803
+ width: fontSize,
804
+ paddingTop: 5
805
+ }
806
+ }) : null
807
+ ] })
808
+ }) });
809
+ };
810
+
811
+ // src/renderers/ImgTagRenderer.tsx
812
+ import { useEffect, useState as useState2 } from "react";
813
+ import { Image as Image2, View as View3 } from "react-native";
814
+ import { jsx as jsx7 } from "react/jsx-runtime";
815
+ var ImgTagRenderer = ({ node }) => {
816
+ const { getStyle, renderImage } = useHtmlContext();
817
+ const [size, setSize] = useState2(null);
818
+ const [containerWidth, setContainerWidth] = useState2(0);
819
+ const { src, alt } = node.attribs || {};
820
+ useEffect(() => {
821
+ if (!src) {
822
+ return;
823
+ }
824
+ Image2.getSize(
825
+ src,
826
+ (width, height) => {
827
+ setSize({ w: width, h: height });
828
+ },
829
+ () => {
830
+ }
831
+ );
832
+ }, [src]);
833
+ if (!size) return null;
834
+ const { w, h } = size;
835
+ const finalWidth = Math.min(w, containerWidth);
836
+ const ratio = h / w;
837
+ const imageStyle = {
838
+ ...getStyle(node).block,
839
+ width: finalWidth,
840
+ height: finalWidth * ratio
841
+ };
842
+ return /* @__PURE__ */ jsx7(
843
+ View3,
844
+ {
845
+ onLayout: (e) => {
846
+ const width = e.nativeEvent.layout.width;
847
+ setContainerWidth(width);
848
+ },
849
+ style: { width: "100%" },
850
+ children: renderImage({
851
+ source: { uri: src },
852
+ style: imageStyle,
853
+ alt
854
+ })
855
+ }
856
+ );
857
+ };
858
+
859
+ // src/renderers/LiTagRenderer.tsx
860
+ import { View as View4 } from "react-native";
861
+ import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
862
+ var LiTagRenderer = ({ node }) => {
863
+ const { getStyle, markerColor, renderImage, renderText } = useHtmlContext();
864
+ const isParentOl = node.parent?.type === "tag" && node.parent?.tagName === "ol";
865
+ const parentLiTags = node.parent?.children || [];
866
+ const currentIndex = parentLiTags.indexOf(node) + 1;
867
+ const items = node.children;
868
+ const style = getStyle(node);
869
+ const { lineHeight = 20, fontSize = 16 } = style.text;
870
+ const markerHeight = 5;
871
+ const markerColorResult = markerColor ?? style.text.color ?? "pink";
872
+ const renderMarker = () => {
873
+ if (isParentOl) {
874
+ return renderText({ style: [style.text], children: `${currentIndex}.` });
875
+ } else {
876
+ return renderImage({
877
+ source: { uri: UL_MARKER_URI },
878
+ style: {
879
+ width: markerHeight,
880
+ height: lineHeight,
881
+ tintColor: markerColorResult
882
+ }
883
+ });
884
+ }
885
+ };
886
+ const nestedLists = items.filter((child) => isList(child));
887
+ const mainContent = items.filter((child) => !isList(child));
888
+ return /* @__PURE__ */ jsxs2(View4, { children: [
889
+ /* @__PURE__ */ jsxs2(
890
+ View4,
891
+ {
892
+ style: {
893
+ flexDirection: "row"
894
+ },
895
+ children: [
896
+ /* @__PURE__ */ jsx8(
897
+ View4,
898
+ {
899
+ style: {
900
+ marginRight: 8,
901
+ marginBottom: lineHeight - fontSize
902
+ },
903
+ children: renderMarker()
904
+ }
905
+ ),
906
+ renderText({
907
+ accessible: true,
908
+ accessibilityLabel: `List item ${currentIndex} of ${parentLiTags.length}: ${concatTextNodes(mainContent)}`,
909
+ style: [style.text, { flex: 1 }],
910
+ children: /* @__PURE__ */ jsx8(NodesRenderer, { nodes: mainContent })
911
+ })
912
+ ]
913
+ }
914
+ ),
915
+ nestedLists.length > 0 && /* @__PURE__ */ jsx8(View4, { style: { width: "100%" }, children: /* @__PURE__ */ jsx8(NodesRenderer, { nodes: nestedLists }) })
916
+ ] });
917
+ };
918
+
919
+ // src/renderers/PTagRenderer.tsx
920
+ import { jsx as jsx9 } from "react/jsx-runtime";
921
+ var PTagRenderer = ({ node }) => {
922
+ const { getStyle } = useHtmlContext();
923
+ const isFirstChild = !node.previousSibling;
924
+ const isLastChild = !node.nextSibling;
925
+ const style = getStyle(node);
926
+ const marginTop = isFirstChild ? 0 : style.block.marginTop;
927
+ const marginBottom = isLastChild ? 0 : style.block.marginBottom;
928
+ return /* @__PURE__ */ jsx9(
929
+ DefaultBlockRenderer,
930
+ {
931
+ node,
932
+ viewProps: {
933
+ style: {
934
+ marginTop,
935
+ marginBottom
936
+ }
937
+ }
938
+ }
939
+ );
940
+ };
941
+
942
+ // src/renderers/_NodeRenderer.tsx
943
+ import { jsx as jsx10 } from "react/jsx-runtime";
944
+ var NodeRenderer = ({ node }) => {
945
+ const { getStyle } = useHtmlContext();
946
+ if (node.type === "tag") {
947
+ switch (node.tagName) {
948
+ case "thead":
949
+ case "tbody":
950
+ case "tfoot":
951
+ case "blockquote":
952
+ case "ul":
953
+ case "ol":
954
+ case "dl":
955
+ case "dt":
956
+ case "dd":
957
+ case "div":
958
+ case "main":
959
+ case "section":
960
+ case "article":
961
+ case "aside":
962
+ case "nav":
963
+ case "header":
964
+ case "footer":
965
+ case "tr":
966
+ return /* @__PURE__ */ jsx10(DefaultBlockRenderer, { node });
967
+ case "h1":
968
+ case "h2":
969
+ case "h3":
970
+ case "h4":
971
+ case "h5":
972
+ case "h6":
973
+ return /* @__PURE__ */ jsx10(
974
+ DefaultBlockRenderer,
975
+ {
976
+ node,
977
+ textProps: {
978
+ accessible: true,
979
+ accessibilityRole: "header"
980
+ }
981
+ }
982
+ );
983
+ case "p":
984
+ return /* @__PURE__ */ jsx10(PTagRenderer, { node });
985
+ case "li":
986
+ return /* @__PURE__ */ jsx10(LiTagRenderer, { node });
987
+ case "hr":
988
+ return /* @__PURE__ */ jsx10(View5, { style: [getStyle(node).block] });
989
+ case "br":
990
+ if (isTextRenderer(node.prev)) {
991
+ return null;
992
+ }
993
+ return /* @__PURE__ */ jsx10(View5, { style: [getStyle(node).block] });
994
+ case "table":
995
+ return /* @__PURE__ */ jsx10(AlignedWidthProvider, { children: /* @__PURE__ */ jsx10(DefaultBlockRenderer, { node }) });
996
+ case "img":
997
+ return /* @__PURE__ */ jsx10(ImgTagRenderer, { node });
998
+ // text renderers
999
+ case "b":
1000
+ case "strong":
1001
+ case "i":
1002
+ case "em":
1003
+ case "u":
1004
+ case "mark":
1005
+ case "small":
1006
+ case "s":
1007
+ case "del":
1008
+ case "sup":
1009
+ case "sub":
1010
+ case "span":
1011
+ case "pre":
1012
+ case "code":
1013
+ return /* @__PURE__ */ jsx10(DefaultTextRenderer, { node });
1014
+ case "a":
1015
+ return /* @__PURE__ */ jsx10(ATagRenderer, { node });
1016
+ case "th":
1017
+ case "td": {
1018
+ const currentIndex = node.parent?.children.indexOf(node) || 0;
1019
+ return /* @__PURE__ */ jsx10(AlignedWidthItem, { index: currentIndex, children: /* @__PURE__ */ jsx10(DefaultTextRenderer, { node }) });
1020
+ }
1021
+ default:
1022
+ return null;
1023
+ }
1024
+ }
1025
+ if (node.type === "text") {
1026
+ const text = node.data.replace(/\s+/g, " ").replace(/ /g, " ");
1027
+ return /* @__PURE__ */ jsx10(Text3, { children: text });
1028
+ }
1029
+ return null;
1030
+ };
1031
+
1032
+ // src/renderers/_NodesRenderer.tsx
1033
+ import { jsx as jsx11 } from "react/jsx-runtime";
1034
+ var NodesRenderer = ({ nodes }) => {
1035
+ return nodes.map((node, index) => /* @__PURE__ */ jsx11(NodeRenderer, { node }, `${node.type}-${index}`));
1036
+ };
1037
+
1038
+ // src/RenderHTML.tsx
1039
+ import { jsx as jsx12 } from "react/jsx-runtime";
1040
+ var RenderHTML = (props) => {
1041
+ const nodes = useMemo2(() => {
1042
+ if (!props.html) {
1043
+ return [];
1044
+ }
1045
+ const cleaned = props.html.replace(/\n/g, "");
1046
+ const n = parseDocument(cleaned);
1047
+ return new HTMLValidator(n.children).cleanup();
1048
+ }, [props.html]);
1049
+ return /* @__PURE__ */ jsx12(View6, { children: /* @__PURE__ */ jsx12(
1050
+ HtmlProvider,
1051
+ {
1052
+ tagStyles: props.tagStyles,
1053
+ baseStyle: props.baseStyle,
1054
+ classesStyles: props.classesStyles,
1055
+ listGap: props.listGap,
1056
+ overrideExternalLinkTintColor: props.overrideExternalLinkTintColor,
1057
+ markerColor: props.markerColor,
1058
+ onLinkPress: props.onLinkPress,
1059
+ renderImage: props.renderImage,
1060
+ renderTextComponent: props.renderTextComponent,
1061
+ children: /* @__PURE__ */ jsx12(NodesRenderer, { nodes })
1062
+ }
1063
+ ) });
1064
+ };
1065
+ export {
1066
+ RenderHTML
1067
+ };
1068
+ //# sourceMappingURL=index.mjs.map