react-native-enriched-markdown-scaffold 1.0.2-scaffold.2 → 1.0.2-scaffold.4

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 (135) hide show
  1. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +3 -0
  2. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +1 -0
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +3 -0
  4. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +1 -0
  5. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +72 -0
  6. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +50 -0
  7. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +10 -0
  8. package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +134 -0
  9. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +62 -7
  10. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +4 -0
  11. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +13 -0
  12. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +42 -0
  13. package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +12 -0
  14. package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyRequestEvent.kt +28 -0
  15. package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLayoutEvent.kt +36 -0
  16. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +37 -7
  17. package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +5 -1
  18. package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +13 -99
  19. package/android/src/main/java/com/swmansion/enriched/markdown/spans/InlineChipPainter.kt +140 -0
  20. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkChipSpans.kt +177 -0
  21. package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +1 -6
  22. package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +36 -1
  23. package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkVariantEntry.kt +7 -3
  24. package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +33 -0
  25. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/HTMLGenerator.kt +2 -1
  26. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/MarkdownExtractor.kt +6 -0
  27. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLayoutCollector.kt +100 -0
  28. package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +24 -2
  29. package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +27 -8
  30. package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +9 -1
  31. package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +18 -2
  32. package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +3 -1
  33. package/docs/API_REFERENCE.md +101 -0
  34. package/docs/COPY_OPTIONS.md +14 -0
  35. package/docs/MENTIONS.md +8 -1
  36. package/docs/STYLES.md +27 -0
  37. package/docs/TEXT.md +2 -0
  38. package/ios/EnrichedMarkdown.mm +91 -2
  39. package/ios/EnrichedMarkdownText.mm +64 -3
  40. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +72 -0
  41. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +50 -0
  42. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +10 -0
  43. package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +134 -0
  44. package/ios/internals/MeasurementCache.h +5 -0
  45. package/ios/renderer/LinkRenderer.m +47 -15
  46. package/ios/styles/StyleConfig.h +16 -0
  47. package/ios/styles/StyleConfig.mm +60 -0
  48. package/ios/utils/CodeBackground.m +3 -65
  49. package/ios/utils/ENRMBlockMenu.h +12 -3
  50. package/ios/utils/ENRMChipDrawing.h +24 -0
  51. package/ios/utils/ENRMChipDrawing.m +83 -0
  52. package/ios/utils/ENRMLinkChip.h +70 -0
  53. package/ios/utils/ENRMLinkChip.m +194 -0
  54. package/ios/utils/ENRMLinkLayout.h +50 -0
  55. package/ios/utils/ENRMLinkLayout.m +45 -0
  56. package/ios/utils/EditMenuUtils+macOS.m +4 -1
  57. package/ios/utils/EditMenuUtils.h +10 -1
  58. package/ios/utils/EditMenuUtils.m +22 -5
  59. package/ios/utils/HTMLGenerator.m +2 -1
  60. package/ios/utils/MarkdownAccessibilityElementBuilder.m +4 -1
  61. package/ios/utils/MarkdownExtractor.m +2 -1
  62. package/ios/utils/RTFExportUtils.m +2 -0
  63. package/ios/utils/StylePropsUtils.h +37 -1
  64. package/ios/utils/TextViewLayoutManager.mm +3 -0
  65. package/ios/views/ENRMCodeBlockContainerView.h +4 -0
  66. package/ios/views/ENRMCodeBlockContainerView.m +8 -1
  67. package/ios/views/ENRMMathContainerView.h +4 -0
  68. package/ios/views/ENRMMathContainerView.m +8 -1
  69. package/ios/views/TableContainerView.h +7 -0
  70. package/ios/views/TableContainerView.m +30 -3
  71. package/lib/module/EnrichedMarkdownNativeComponent.ts +57 -0
  72. package/lib/module/EnrichedMarkdownTextNativeComponent.ts +57 -0
  73. package/lib/module/index.js.map +1 -1
  74. package/lib/module/native/EnrichedMarkdownText.js +119 -9
  75. package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
  76. package/lib/module/normalizeMarkdownStyle.js +13 -2
  77. package/lib/module/normalizeMarkdownStyle.js.map +1 -1
  78. package/lib/module/normalizeMarkdownStyle.web.js +13 -2
  79. package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
  80. package/lib/module/web/EnrichedMarkdownText.js +6 -3
  81. package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
  82. package/lib/module/web/renderers/InlineRenderers.js +96 -3
  83. package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
  84. package/lib/module/web/styles.js +18 -0
  85. package/lib/module/web/styles.js.map +1 -1
  86. package/lib/module/web/utils.js +13 -0
  87. package/lib/module/web/utils.js.map +1 -1
  88. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +53 -0
  89. package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
  90. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +53 -0
  91. package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
  92. package/lib/typescript/src/index.d.ts +2 -2
  93. package/lib/typescript/src/index.d.ts.map +1 -1
  94. package/lib/typescript/src/index.web.d.ts +1 -1
  95. package/lib/typescript/src/index.web.d.ts.map +1 -1
  96. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +5 -5
  97. package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
  98. package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
  99. package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
  100. package/lib/typescript/src/types/MarkdownStyle.d.ts +28 -5
  101. package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
  102. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +10 -0
  103. package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
  104. package/lib/typescript/src/types/MarkdownTextProps.d.ts +54 -1
  105. package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
  106. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +41 -1
  107. package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
  108. package/lib/typescript/src/types/events.d.ts +22 -0
  109. package/lib/typescript/src/types/events.d.ts.map +1 -1
  110. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
  111. package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
  112. package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
  113. package/lib/typescript/src/web/styles.d.ts.map +1 -1
  114. package/lib/typescript/src/web/types.d.ts +4 -0
  115. package/lib/typescript/src/web/types.d.ts.map +1 -1
  116. package/lib/typescript/src/web/utils.d.ts +4 -0
  117. package/lib/typescript/src/web/utils.d.ts.map +1 -1
  118. package/package.json +1 -1
  119. package/src/EnrichedMarkdownNativeComponent.ts +57 -0
  120. package/src/EnrichedMarkdownTextNativeComponent.ts +57 -0
  121. package/src/index.tsx +3 -0
  122. package/src/index.web.tsx +5 -1
  123. package/src/native/EnrichedMarkdownText.tsx +127 -7
  124. package/src/normalizeMarkdownStyle.ts +13 -0
  125. package/src/normalizeMarkdownStyle.web.ts +11 -0
  126. package/src/types/MarkdownStyle.ts +29 -5
  127. package/src/types/MarkdownStyleInternal.ts +10 -0
  128. package/src/types/MarkdownTextProps.ts +56 -0
  129. package/src/types/MarkdownTextProps.web.ts +43 -1
  130. package/src/types/events.ts +23 -0
  131. package/src/web/EnrichedMarkdownText.tsx +10 -3
  132. package/src/web/renderers/InlineRenderers.tsx +111 -3
  133. package/src/web/styles.ts +25 -0
  134. package/src/web/types.ts +4 -0
  135. package/src/web/utils.ts +8 -0
@@ -1,4 +1,5 @@
1
1
  #import "LinkRenderer.h"
2
+ #import "ENRMLinkChip.h"
2
3
  #import "FontUtils.h"
3
4
  #import "RenderContext.h"
4
5
  #import "RendererFactory.h"
@@ -13,31 +14,49 @@
13
14
  into:(NSMutableAttributedString *)output
14
15
  context:(RenderContext *)context
15
16
  {
16
- NSUInteger start = output.length;
17
-
18
- // 1. Render children first to establish base attributes
19
- [_rendererFactory renderChildrenOfNode:node into:output context:context];
20
-
21
- NSRange range = NSMakeRange(start, output.length - start);
22
- if (range.length == 0)
23
- return;
24
-
25
- // 2. Extract configuration
17
+ // 1. Extract configuration
26
18
  NSString *url = node.attributes[@"url"] ?: @"";
27
19
  LinkVariantConfig *variant = [_config effectiveLinkVariantForURL:url];
28
20
 
29
21
  RCTUIColor *linkColor = variant.color ?: [_config linkColor];
30
22
  BOOL linkUnderline = variant ? variant.underline : [_config linkUnderline];
31
23
  NSString *linkFontFamily = [_config linkFontFamily];
32
- RCTUIColor *backgroundColor = variant ? variant.backgroundColor : [_config linkBackgroundColor];
24
+ // Chip ("box") properties never fall back to the base link style.
25
+ ENRMLinkChip *chip = [[ENRMLinkChip alloc] init];
26
+ chip.backgroundColor = variant ? variant.backgroundColor : [_config linkBackgroundColor];
27
+ chip.borderColor = variant ? variant.borderColor : [_config linkBorderColor];
28
+ chip.borderWidth = variant ? variant.borderWidth : [_config linkBorderWidth];
29
+ chip.borderRadius = variant ? variant.borderRadius : [_config linkBorderRadius];
30
+ chip.paddingHorizontal = variant ? variant.paddingHorizontal : [_config linkPaddingHorizontal];
31
+ chip.paddingVertical = variant ? variant.paddingVertical : [_config linkPaddingVertical];
32
+ BOOL hasSpacers = chip.paddingHorizontal > 0;
33
33
 
34
34
  NSNumber *underlineStyle = @(linkUnderline ? NSUnderlineStyleSingle : NSUnderlineStyleNone);
35
35
 
36
+ // 2. Render children to establish base attributes. With horizontal padding, a
37
+ // leading spacer goes in first so ranges the children register stay valid;
38
+ // its attributes are fixed up once the first child glyph exists.
39
+ NSUInteger start = output.length;
40
+ if (hasSpacers) {
41
+ [output appendAttributedString:ENRMLinkSpacerString([context getTextAttributes], chip.paddingHorizontal)];
42
+ }
43
+ NSUInteger contentStart = output.length;
44
+
45
+ [_rendererFactory renderChildrenOfNode:node into:output context:context];
46
+
47
+ NSRange contentRange = NSMakeRange(contentStart, output.length - contentStart);
48
+ if (contentRange.length == 0) {
49
+ if (hasSpacers) {
50
+ [output deleteCharactersInRange:NSMakeRange(start, contentStart - start)];
51
+ }
52
+ return;
53
+ }
54
+
36
55
  // 3. Apply core link functionality (non-destructive)
37
- [output addAttribute:NSLinkAttributeName value:url range:range];
56
+ [output addAttribute:NSLinkAttributeName value:url range:contentRange];
38
57
 
39
58
  // 4. Optimize visual attributes via enumeration to avoid redundant updates
40
- [output enumerateAttributesInRange:range
59
+ [output enumerateAttributesInRange:contentRange
41
60
  options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
42
61
  usingBlock:^(NSDictionary<NSAttributedStringKey, id> *attrs, NSRange subrange, BOOL *stop) {
43
62
  NSMutableDictionary *newAttributes = [NSMutableDictionary dictionary];
@@ -74,10 +93,23 @@
74
93
  }
75
94
  }];
76
95
 
77
- if (backgroundColor) {
78
- [output addAttribute:NSBackgroundColorAttributeName value:backgroundColor range:range];
96
+ // 5. Reserve horizontal padding with a spacer at each outer edge (see
97
+ // ENRMLinkSpacerString). Spacers sit inside the link range, so tapping the
98
+ // padding hits the link.
99
+ NSRange range = contentRange;
100
+ if (hasSpacers) {
101
+ NSDictionary *firstAttrs = [output attributesAtIndex:contentRange.location effectiveRange:NULL];
102
+ NSDictionary *lastAttrs = [output attributesAtIndex:NSMaxRange(contentRange) - 1 effectiveRange:NULL];
103
+ NSAttributedString *leading = ENRMLinkSpacerString(firstAttrs, chip.paddingHorizontal);
104
+ [output setAttributes:[leading attributesAtIndex:0 effectiveRange:NULL] range:NSMakeRange(start, 1)];
105
+ [output appendAttributedString:ENRMLinkSpacerString(lastAttrs, chip.paddingHorizontal)];
106
+ range = NSMakeRange(start, output.length - start);
79
107
  }
80
108
 
109
+ // 6. Chip background/border is drawn by the layout manager (see ENRMLinkChip).
110
+ // Attached even when invisible so the chip's padding is known to link-layout reporting.
111
+ [output addAttribute:ENRMLinkChipAttributeName value:chip range:range];
112
+
81
113
  [context registerLinkRange:range url:url];
82
114
  }
83
115
 
@@ -13,6 +13,12 @@ NS_ASSUME_NONNULL_BEGIN
13
13
  @property (nonatomic, strong) RCTUIColor *color;
14
14
  @property (nonatomic, assign) BOOL underline;
15
15
  @property (nonatomic, strong, nullable) RCTUIColor *backgroundColor;
16
+ // Chip ("box") properties — never inherited from the base link style.
17
+ @property (nonatomic, assign) CGFloat borderRadius;
18
+ @property (nonatomic, assign) CGFloat paddingHorizontal;
19
+ @property (nonatomic, assign) CGFloat paddingVertical;
20
+ @property (nonatomic, strong, nullable) RCTUIColor *borderColor;
21
+ @property (nonatomic, assign) CGFloat borderWidth;
16
22
  @end
17
23
 
18
24
  @interface StyleConfig : NSObject <NSCopying>
@@ -166,6 +172,16 @@ NS_ASSUME_NONNULL_BEGIN
166
172
  - (void)setLinkUnderline:(BOOL)newValue;
167
173
  - (nullable RCTUIColor *)linkBackgroundColor;
168
174
  - (void)setLinkBackgroundColor:(nullable RCTUIColor *)newValue;
175
+ - (CGFloat)linkBorderRadius;
176
+ - (void)setLinkBorderRadius:(CGFloat)newValue;
177
+ - (CGFloat)linkPaddingHorizontal;
178
+ - (void)setLinkPaddingHorizontal:(CGFloat)newValue;
179
+ - (CGFloat)linkPaddingVertical;
180
+ - (void)setLinkPaddingVertical:(CGFloat)newValue;
181
+ - (nullable RCTUIColor *)linkBorderColor;
182
+ - (void)setLinkBorderColor:(nullable RCTUIColor *)newValue;
183
+ - (CGFloat)linkBorderWidth;
184
+ - (void)setLinkBorderWidth:(CGFloat)newValue;
169
185
  - (NSArray<LinkVariantConfig *> *)linkVariants;
170
186
  - (void)setLinkVariants:(NSArray<LinkVariantConfig *> *)newValue;
171
187
  - (nullable LinkVariantConfig *)effectiveLinkVariantForURL:(NSString *)url;
@@ -113,6 +113,11 @@ static inline NSString *normalizedFontWeight(NSString *fontWeight)
113
113
  RCTUIColor *_linkColor;
114
114
  BOOL _linkUnderline;
115
115
  RCTUIColor *_linkBackgroundColor;
116
+ CGFloat _linkBorderRadius;
117
+ CGFloat _linkPaddingHorizontal;
118
+ CGFloat _linkPaddingVertical;
119
+ RCTUIColor *_linkBorderColor;
120
+ CGFloat _linkBorderWidth;
116
121
  NSArray<LinkVariantConfig *> *_linkVariants;
117
122
  NSArray<LinkVariantRegexEntry *> *_compiledVariantRegexes;
118
123
  // Strong properties
@@ -405,6 +410,11 @@ static inline NSString *normalizedFontWeight(NSString *fontWeight)
405
410
  copy->_linkColor = [_linkColor copy];
406
411
  copy->_linkUnderline = _linkUnderline;
407
412
  copy->_linkBackgroundColor = [_linkBackgroundColor copy];
413
+ copy->_linkBorderRadius = _linkBorderRadius;
414
+ copy->_linkPaddingHorizontal = _linkPaddingHorizontal;
415
+ copy->_linkPaddingVertical = _linkPaddingVertical;
416
+ copy->_linkBorderColor = [_linkBorderColor copy];
417
+ copy->_linkBorderWidth = _linkBorderWidth;
408
418
  copy->_linkVariants = [_linkVariants copy];
409
419
  copy->_compiledVariantRegexes = [_compiledVariantRegexes copy];
410
420
  copy->_strongFontFamily = [_strongFontFamily copy];
@@ -1334,6 +1344,56 @@ static inline NSString *normalizedFontWeight(NSString *fontWeight)
1334
1344
  _linkBackgroundColor = newValue;
1335
1345
  }
1336
1346
 
1347
+ - (CGFloat)linkBorderRadius
1348
+ {
1349
+ return _linkBorderRadius;
1350
+ }
1351
+
1352
+ - (void)setLinkBorderRadius:(CGFloat)newValue
1353
+ {
1354
+ _linkBorderRadius = newValue;
1355
+ }
1356
+
1357
+ - (CGFloat)linkPaddingHorizontal
1358
+ {
1359
+ return _linkPaddingHorizontal;
1360
+ }
1361
+
1362
+ - (void)setLinkPaddingHorizontal:(CGFloat)newValue
1363
+ {
1364
+ _linkPaddingHorizontal = newValue;
1365
+ }
1366
+
1367
+ - (CGFloat)linkPaddingVertical
1368
+ {
1369
+ return _linkPaddingVertical;
1370
+ }
1371
+
1372
+ - (void)setLinkPaddingVertical:(CGFloat)newValue
1373
+ {
1374
+ _linkPaddingVertical = newValue;
1375
+ }
1376
+
1377
+ - (RCTUIColor *)linkBorderColor
1378
+ {
1379
+ return _linkBorderColor;
1380
+ }
1381
+
1382
+ - (void)setLinkBorderColor:(RCTUIColor *)newValue
1383
+ {
1384
+ _linkBorderColor = newValue;
1385
+ }
1386
+
1387
+ - (CGFloat)linkBorderWidth
1388
+ {
1389
+ return _linkBorderWidth;
1390
+ }
1391
+
1392
+ - (void)setLinkBorderWidth:(CGFloat)newValue
1393
+ {
1394
+ _linkBorderWidth = newValue;
1395
+ }
1396
+
1337
1397
  - (NSArray<LinkVariantConfig *> *)linkVariants
1338
1398
  {
1339
1399
  return _linkVariants;
@@ -1,4 +1,5 @@
1
1
  #import "CodeBackground.h"
2
+ #import "ENRMChipDrawing.h"
2
3
  #import "ENRMUIKit.h"
3
4
  #import "RenderContext.h"
4
5
 
@@ -116,71 +117,8 @@ static const CGFloat kCodeBackgroundBorderWidth = 0.5;
116
117
  isFirst:(BOOL)isFirst
117
118
  isLast:(BOOL)isLast
118
119
  {
119
- UIBezierPath *path = [self pathForRect:rect isFirst:isFirst isLast:isLast];
120
- [backgroundColor setFill];
121
- [path fill];
122
-
123
- if (borderColor) {
124
- [borderColor setStroke];
125
- UIBezierPath *strokePath =
126
- (isFirst && isLast) ? path : [self openBorderPathForRect:rect isFirst:isFirst isLast:isLast];
127
- strokePath.lineWidth = kCodeBackgroundBorderWidth;
128
- BezierPathSetRoundStyle(strokePath);
129
- [strokePath stroke];
130
- }
131
- }
132
-
133
- - (UIBezierPath *)pathForRect:(CGRect)rect isFirst:(BOOL)isFirst isLast:(BOOL)isLast
134
- {
135
- #if !TARGET_OS_OSX
136
- UIRectCorner corners = 0;
137
- if (isFirst)
138
- corners |= (UIRectCornerTopLeft | UIRectCornerBottomLeft);
139
- if (isLast)
140
- corners |= (UIRectCornerTopRight | UIRectCornerBottomRight);
141
-
142
- return [UIBezierPath bezierPathWithRoundedRect:rect
143
- byRoundingCorners:corners
144
- cornerRadii:CGSizeMake(kCodeBackgroundCornerRadius, kCodeBackgroundCornerRadius)];
145
- #else
146
- // NSBezierPath doesn't support per-corner rounding — use uniform corner radius
147
- CGFloat cornerRadius = (isFirst || isLast) ? kCodeBackgroundCornerRadius : 0;
148
- return UIBezierPathWithRoundedRect(rect, cornerRadius);
149
- #endif
150
- }
151
-
152
- - (UIBezierPath *)openBorderPathForRect:(CGRect)rect isFirst:(BOOL)isFirst isLast:(BOOL)isLast
153
- {
154
- UIBezierPath *path = [UIBezierPath bezierPath];
155
- CGFloat r = kCodeBackgroundCornerRadius;
156
- CGFloat inset = kCodeBackgroundBorderWidth / 2.0;
157
- CGRect insetRect = CGRectInset(rect, inset, inset);
158
-
159
- if (isFirst) {
160
- [path moveToPoint:CGPointMake(CGRectGetMaxX(rect), insetRect.origin.y)];
161
- BezierPathAddLine(path, CGPointMake(insetRect.origin.x + r, insetRect.origin.y));
162
- BezierPathAddQuadCurve(path, CGPointMake(insetRect.origin.x, insetRect.origin.y + r),
163
- CGPointMake(insetRect.origin.x, insetRect.origin.y));
164
- BezierPathAddLine(path, CGPointMake(insetRect.origin.x, CGRectGetMaxY(insetRect) - r));
165
- BezierPathAddQuadCurve(path, CGPointMake(insetRect.origin.x + r, CGRectGetMaxY(insetRect)),
166
- CGPointMake(insetRect.origin.x, CGRectGetMaxY(insetRect)));
167
- BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(insetRect)));
168
- } else if (isLast) {
169
- [path moveToPoint:CGPointMake(rect.origin.x, insetRect.origin.y)];
170
- BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(insetRect) - r, insetRect.origin.y));
171
- BezierPathAddQuadCurve(path, CGPointMake(CGRectGetMaxX(insetRect), insetRect.origin.y + r),
172
- CGPointMake(CGRectGetMaxX(insetRect), insetRect.origin.y));
173
- BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(insetRect), CGRectGetMaxY(insetRect) - r));
174
- BezierPathAddQuadCurve(path, CGPointMake(CGRectGetMaxX(insetRect) - r, CGRectGetMaxY(insetRect)),
175
- CGPointMake(CGRectGetMaxX(insetRect), CGRectGetMaxY(insetRect)));
176
- BezierPathAddLine(path, CGPointMake(rect.origin.x, CGRectGetMaxY(insetRect)));
177
- } else {
178
- [path moveToPoint:CGPointMake(rect.origin.x, insetRect.origin.y)];
179
- BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(rect), insetRect.origin.y));
180
- [path moveToPoint:CGPointMake(rect.origin.x, CGRectGetMaxY(insetRect))];
181
- BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(insetRect)));
182
- }
183
- return path;
120
+ ENRMDrawChipFragment(rect, backgroundColor, borderColor, kCodeBackgroundBorderWidth, kCodeBackgroundCornerRadius,
121
+ isFirst, isLast, NO);
184
122
  }
185
123
 
186
124
  #pragma mark - Helpers
@@ -18,20 +18,29 @@ typedef struct {
18
18
  BOOL copyAsMarkdownHidden;
19
19
  // `selectionMenuConfig.codeBlockCopyButton.enabled == false`.
20
20
  BOOL copyButtonHidden;
21
+ // JS `onCopy` is set: the Copy item hands the block's content to the view's
22
+ // `onCopyRequest` instead of writing the pasteboard.
23
+ BOOL interceptCopy;
21
24
  } ENRMBlockMenuOptions;
22
25
 
26
+ /// Receives an intercepted block Copy: the block's Markdown and its raw content.
27
+ typedef void (^ENRMBlockCopyRequestBlock)(NSString *markdown, NSString *text);
28
+
23
29
  #if !TARGET_OS_OSX
24
30
  /// Builds the long-press menu shared by the table, math and code block views.
25
31
  /// Returns nil when no item is left — nil is how UIKit suppresses a context
26
- /// menu, whereas an empty menu would still lift the view.
32
+ /// menu, whereas an empty menu would still lift the view. `requestCopy` replaces
33
+ /// the Copy item's handler when `options.interceptCopy` is set.
27
34
  static inline UIContextMenuConfiguration *_Nullable ENRMBlockContextMenuConfiguration(
28
35
  ENRMBlockMenuOptions options, NSString *_Nullable copyLabel, NSString *_Nullable copyAsMarkdownLabel,
29
- void (^copyRaw)(void), void (^copyMarkdown)(void))
36
+ void (^copyRaw)(void), void (^copyMarkdown)(void), void (^_Nullable requestCopy)(void))
30
37
  {
31
38
  if (options.copyHidden && options.copyAsMarkdownHidden) {
32
39
  return nil;
33
40
  }
34
- void (^copyHandler)(void) = options.copyWritesMarkdown ? copyMarkdown : copyRaw;
41
+ void (^copyHandler)(void) = (options.interceptCopy && requestCopy) ? requestCopy
42
+ : options.copyWritesMarkdown ? copyMarkdown
43
+ : copyRaw;
35
44
  return [UIContextMenuConfiguration
36
45
  configurationWithIdentifier:nil
37
46
  previewProvider:nil
@@ -0,0 +1,24 @@
1
+ #pragma once
2
+ #import "ENRMUIKit.h"
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ #ifdef __cplusplus
7
+ extern "C" {
8
+ #endif
9
+
10
+ /// Fills (and optionally strokes) one line fragment of an inline "chip" — the
11
+ /// rounded background shared by inline code and link chips. For a chip that
12
+ /// wraps, only the outer ends are rounded (`isFirst` / `isLast`) and the border
13
+ /// stays open where the fragment is cut.
14
+ ///
15
+ /// `strokeInset` keeps a closed border inside `rect` (link chips); otherwise it
16
+ /// is centered on the edge (inline code, kept for pixel parity).
17
+ void ENRMDrawChipFragment(CGRect rect, RCTUIColor *_Nullable backgroundColor, RCTUIColor *_Nullable borderColor,
18
+ CGFloat borderWidth, CGFloat cornerRadius, BOOL isFirst, BOOL isLast, BOOL strokeInset);
19
+
20
+ #ifdef __cplusplus
21
+ }
22
+ #endif
23
+
24
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,83 @@
1
+ #import "ENRMChipDrawing.h"
2
+
3
+ static UIBezierPath *chipFillPath(CGRect rect, CGFloat r, BOOL isFirst, BOOL isLast)
4
+ {
5
+ #if !TARGET_OS_OSX
6
+ UIRectCorner corners = 0;
7
+ if (isFirst)
8
+ corners |= (UIRectCornerTopLeft | UIRectCornerBottomLeft);
9
+ if (isLast)
10
+ corners |= (UIRectCornerTopRight | UIRectCornerBottomRight);
11
+
12
+ return [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:CGSizeMake(r, r)];
13
+ #else
14
+ // NSBezierPath doesn't support per-corner rounding — use uniform corner radius
15
+ return UIBezierPathWithRoundedRect(rect, (isFirst || isLast) ? r : 0);
16
+ #endif
17
+ }
18
+
19
+ static UIBezierPath *chipOpenBorderPath(CGRect rect, CGFloat r, CGFloat borderWidth, BOOL isFirst, BOOL isLast)
20
+ {
21
+ UIBezierPath *path = [UIBezierPath bezierPath];
22
+ CGFloat inset = borderWidth / 2.0;
23
+ CGRect insetRect = CGRectInset(rect, inset, inset);
24
+
25
+ if (isFirst) {
26
+ [path moveToPoint:CGPointMake(CGRectGetMaxX(rect), insetRect.origin.y)];
27
+ BezierPathAddLine(path, CGPointMake(insetRect.origin.x + r, insetRect.origin.y));
28
+ BezierPathAddQuadCurve(path, CGPointMake(insetRect.origin.x, insetRect.origin.y + r),
29
+ CGPointMake(insetRect.origin.x, insetRect.origin.y));
30
+ BezierPathAddLine(path, CGPointMake(insetRect.origin.x, CGRectGetMaxY(insetRect) - r));
31
+ BezierPathAddQuadCurve(path, CGPointMake(insetRect.origin.x + r, CGRectGetMaxY(insetRect)),
32
+ CGPointMake(insetRect.origin.x, CGRectGetMaxY(insetRect)));
33
+ BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(insetRect)));
34
+ } else if (isLast) {
35
+ [path moveToPoint:CGPointMake(rect.origin.x, insetRect.origin.y)];
36
+ BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(insetRect) - r, insetRect.origin.y));
37
+ BezierPathAddQuadCurve(path, CGPointMake(CGRectGetMaxX(insetRect), insetRect.origin.y + r),
38
+ CGPointMake(CGRectGetMaxX(insetRect), insetRect.origin.y));
39
+ BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(insetRect), CGRectGetMaxY(insetRect) - r));
40
+ BezierPathAddQuadCurve(path, CGPointMake(CGRectGetMaxX(insetRect) - r, CGRectGetMaxY(insetRect)),
41
+ CGPointMake(CGRectGetMaxX(insetRect), CGRectGetMaxY(insetRect)));
42
+ BezierPathAddLine(path, CGPointMake(rect.origin.x, CGRectGetMaxY(insetRect)));
43
+ } else {
44
+ [path moveToPoint:CGPointMake(rect.origin.x, insetRect.origin.y)];
45
+ BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(rect), insetRect.origin.y));
46
+ [path moveToPoint:CGPointMake(rect.origin.x, CGRectGetMaxY(insetRect))];
47
+ BezierPathAddLine(path, CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(insetRect)));
48
+ }
49
+ return path;
50
+ }
51
+
52
+ void ENRMDrawChipFragment(CGRect rect, RCTUIColor *backgroundColor, RCTUIColor *borderColor, CGFloat borderWidth,
53
+ CGFloat cornerRadius, BOOL isFirst, BOOL isLast, BOOL strokeInset)
54
+ {
55
+ if (CGRectIsEmpty(rect))
56
+ return;
57
+
58
+ CGFloat r = MIN(MAX(cornerRadius, 0), MIN(rect.size.width, rect.size.height) / 2.0);
59
+
60
+ if (backgroundColor) {
61
+ [backgroundColor setFill];
62
+ [chipFillPath(rect, r, isFirst, isLast) fill];
63
+ }
64
+
65
+ if (!borderColor || borderWidth <= 0)
66
+ return;
67
+
68
+ [borderColor setStroke];
69
+ UIBezierPath *strokePath;
70
+ if (isFirst && isLast) {
71
+ if (strokeInset) {
72
+ CGFloat inset = borderWidth / 2.0;
73
+ strokePath = chipFillPath(CGRectInset(rect, inset, inset), MAX(r - inset, 0), YES, YES);
74
+ } else {
75
+ strokePath = chipFillPath(rect, r, YES, YES);
76
+ }
77
+ } else {
78
+ strokePath = chipOpenBorderPath(rect, r, borderWidth, isFirst, isLast);
79
+ }
80
+ strokePath.lineWidth = borderWidth;
81
+ BezierPathSetRoundStyle(strokePath);
82
+ [strokePath stroke];
83
+ }
@@ -0,0 +1,70 @@
1
+ #pragma once
2
+ #import "ENRMUIKit.h"
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ /// Marks a link range that carries chip styling. The value is one
7
+ /// `ENRMLinkChip` instance per link: instances compare by identity, so two
8
+ /// adjacent links never coalesce into one attribute run (one chip).
9
+ extern NSString *const ENRMLinkChipAttributeName;
10
+
11
+ /// Marks the spacer characters `LinkRenderer` inserts at both outer edges of a
12
+ /// link to reserve `paddingHorizontal` in the line. Spacers are layout-only:
13
+ /// every text exit point (copy, markdown/HTML export, decoration text) strips
14
+ /// them by this attribute — never by character, since U+00A0 can be content.
15
+ extern NSString *const ENRMLinkSpacerAttributeName;
16
+
17
+ @interface ENRMLinkChip : NSObject
18
+ @property (nonatomic, strong, nullable) RCTUIColor *backgroundColor;
19
+ @property (nonatomic, strong, nullable) RCTUIColor *borderColor;
20
+ @property (nonatomic, assign) CGFloat borderWidth;
21
+ @property (nonatomic, assign) CGFloat borderRadius;
22
+ @property (nonatomic, assign) CGFloat paddingHorizontal;
23
+ @property (nonatomic, assign) CGFloat paddingVertical;
24
+ /// YES when the chip paints something (a background or a visible border).
25
+ @property (nonatomic, readonly) BOOL isVisible;
26
+ @end
27
+
28
+ #ifdef __cplusplus
29
+ extern "C" {
30
+ #endif
31
+
32
+ /// A spacer character (U+00A0) whose advance is exactly `width`.
33
+ ///
34
+ /// U+00A0 is line-break class GL: a break is allowed before it after a space
35
+ /// but never after it, and never before it after a non-space — so a leading
36
+ /// spacer always travels with the link's first glyph and a trailing one with
37
+ /// its last glyph (no orphaned padding at a wrap). It uses a 1pt system font
38
+ /// so its own advance and line-height contribution are negligible whatever
39
+ /// the link font (a custom font may lack U+00A0), and NSKern makes up the rest.
40
+ NSAttributedString *ENRMLinkSpacerString(NSDictionary<NSAttributedStringKey, id> *baseAttributes, CGFloat width);
41
+
42
+ /// `string` without link spacers (returns `string` itself when it has none).
43
+ NSAttributedString *ENRMAttributedStringByRemovingLinkSpacers(NSAttributedString *string);
44
+
45
+ /// Plain text of `range` without link spacers.
46
+ NSString *ENRMPlainTextByRemovingLinkSpacers(NSAttributedString *string, NSRange range);
47
+
48
+ /// Falls back to a plain `NSBackgroundColorAttributeName` for every chip
49
+ /// background, for text drawn without `TextViewLayoutManager` (table cells,
50
+ /// RTF export). Radius, border and vertical padding are not represented.
51
+ void ENRMApplyLinkChipBackgroundsAsTextBackground(NSMutableAttributedString *string);
52
+
53
+ /// Enumerates the chip rect of every laid-out line fragment of the link at
54
+ /// `charRange`, in text-container coordinates. The rect spans the glyphs
55
+ /// (spacers included, so it already covers `paddingHorizontal`) horizontally
56
+ /// and the link font's ascender..descender ± `paddingVertical` vertically.
57
+ void ENRMEnumerateLinkChipFragments(NSLayoutManager *layoutManager, NSTextContainer *textContainer, NSRange charRange,
58
+ ENRMLinkChip *_Nullable chip,
59
+ void (^block)(CGRect rect, BOOL isFirst, BOOL isLast));
60
+
61
+ /// Draws the visible link chips intersecting `glyphsToShow`. Called from the
62
+ /// layout manager's background pass, before glyphs are drawn.
63
+ void ENRMDrawLinkChipsForGlyphRange(NSLayoutManager *layoutManager, NSTextContainer *textContainer,
64
+ NSRange glyphsToShow, CGPoint origin);
65
+
66
+ #ifdef __cplusplus
67
+ }
68
+ #endif
69
+
70
+ NS_ASSUME_NONNULL_END