11#import " EnrichedTextInputView.h"
22#import " AlignmentUtils.h"
33#import " AttachmentLayoutUtils.h"
4+ #import " ColorExtension.h"
45#import " CoreText/CoreText.h"
56#import " DotReplacementUtils.h"
67#import " HtmlParser.h"
1718#import " WordsUtils.h"
1819#import " ZeroWidthSpaceUtils.h"
1920#import < React/RCTConversions.h>
21+ #import < React/RCTConvert.h>
2022#import < ReactNativeEnriched/EnrichedTextInputViewComponentDescriptor.h>
2123#import < ReactNativeEnriched/EventEmitters.h>
2224#import < ReactNativeEnriched/Props.h>
@@ -60,6 +62,7 @@ @implementation EnrichedTextInputView {
6062 NSString *_submitBehavior;
6163 NSDictionary <NSAttributedStringKey , id > *_capturedAttributesBeforeChange;
6264 NSString *_recentlyEmittedAlignment;
65+ CustomStyleData *_recentlyEmittedCustomStyle;
6366}
6467
6568@synthesize blockEmitting = blockEmitting;
@@ -1092,13 +1095,23 @@ - (void)tryUpdatingActiveStyles {
10921095 updateNeeded = YES ;
10931096 }
10941097
1098+ // detect custom style change
1099+ CustomStyle *customStyle = stylesDict[@([CustomStyle getType ])];
1100+ CustomStyleData *currentCustomStyle =
1101+ [customStyle getCustomStyleDataAt: textView.selectedRange.location];
1102+ if (currentCustomStyle != _recentlyEmittedCustomStyle &&
1103+ ![currentCustomStyle isEqual: _recentlyEmittedCustomStyle]) {
1104+ updateNeeded = YES ;
1105+ }
1106+
10951107 if (updateNeeded) {
10961108 auto emitter = [self getEventEmitter ];
10971109 if (emitter != nullptr ) {
10981110 // update activeStyles and blockedStyles only if emitter is available
10991111 _activeStyles = newActiveStyles;
11001112 _blockedStyles = newBlockedStyles;
11011113 _recentlyEmittedAlignment = currentAlignment;
1114+ _recentlyEmittedCustomStyle = currentCustomStyle;
11021115
11031116 emitter->onChangeState (
11041117 {.bold = GET_STYLE_STATE ([BoldStyle getType ]),
@@ -1120,7 +1133,14 @@ - (void)tryUpdatingActiveStyles {
11201133 .codeBlock = GET_STYLE_STATE ([CodeBlockStyle getType ]),
11211134 .image = GET_STYLE_STATE ([ImageStyle getType ]),
11221135 .checkboxList = GET_STYLE_STATE ([CheckboxListStyle getType ]),
1123- .alignment = [currentAlignment UTF8String ]});
1136+ .alignment = [currentAlignment UTF8String ],
1137+ .customStyle = {
1138+ .foregroundColor =
1139+ [[currentCustomStyle.foregroundColor rgbaString ] UTF8String ]
1140+ ?: " " ,
1141+ .backgroundColor =
1142+ [[currentCustomStyle.backgroundColor rgbaString ] UTF8String ]
1143+ ?: " " }});
11241144 }
11251145 }
11261146
@@ -1268,6 +1288,9 @@ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
12681288 if (!_placeholderLabel.isHidden ) {
12691289 [self refreshPlaceholderLabelStyles ];
12701290 }
1291+ } else if ([commandName isEqualToString: @" setStyle" ]) {
1292+ NSString *styleJSON = (NSString *)args[0 ];
1293+ [self setStyle: styleJSON];
12711294 }
12721295}
12731296
@@ -1475,6 +1498,52 @@ - (void)toggleRegularStyle:(StyleType)type {
14751498 }
14761499}
14771500
1501+ - (void )setStyle : (NSString *)styleJSON {
1502+ NSData *jsonData = [styleJSON dataUsingEncoding: NSUTF8StringEncoding];
1503+ if (jsonData == nil )
1504+ return ;
1505+ NSDictionary *dict = [NSJSONSerialization JSONObjectWithData: jsonData
1506+ options: 0
1507+ error: nil ];
1508+ if (dict == nil )
1509+ return ;
1510+
1511+ NSRange selectedRange = textView.selectedRange ;
1512+ CustomStyle *customStyleClass =
1513+ (CustomStyle *)stylesDict[@([CustomStyle getType ])];
1514+ if (customStyleClass == nil )
1515+ return ;
1516+
1517+ if (![StyleUtils handleStyleBlocksAndConflicts: [CustomStyle getType ]
1518+ range: selectedRange
1519+ forHost: self ]) {
1520+ return ;
1521+ }
1522+
1523+ // Convert raw JSON values (NSNumber ARGB integers from processColor) to
1524+ // UIColor. NSNull is passed through as-is so mergeFromDict: can clear
1525+ // the color when the caller explicitly passes null.
1526+ NSMutableDictionary *processedDict = [NSMutableDictionary new ];
1527+
1528+ id fgRaw = dict[@" foregroundColor" ];
1529+ if (fgRaw != nil ) {
1530+ processedDict[@" foregroundColor" ] = [fgRaw isKindOfClass: [NSNull class ]]
1531+ ? [NSNull null ]
1532+ : [RCTConvert UIColor: fgRaw];
1533+ }
1534+
1535+ id bgRaw = dict[@" backgroundColor" ];
1536+ if (bgRaw != nil ) {
1537+ processedDict[@" backgroundColor" ] = [bgRaw isKindOfClass: [NSNull class ]]
1538+ ? [NSNull null ]
1539+ : [RCTConvert UIColor: bgRaw];
1540+ }
1541+
1542+ [customStyleClass applyStyleFromDict: processedDict
1543+ selectedRange: selectedRange];
1544+ [self anyTextMayHaveBeenModified ];
1545+ }
1546+
14781547- (void )toggleCheckboxList : (BOOL )checked {
14791548 CheckboxListStyle *style =
14801549 (CheckboxListStyle *)stylesDict[@([CheckboxListStyle getType ])];
@@ -1827,6 +1896,10 @@ - (void)emitOnContextMenuItemPressEvent:(NSString *)itemText {
18271896 AlignmentStyle *alignmentStyle = stylesDict[@([AlignmentStyle getType ])];
18281897 NSString *currentAlignment = [alignmentStyle getStyleState ];
18291898
1899+ CustomStyle *customStyle = stylesDict[@([CustomStyle getType ])];
1900+ CustomStyleData *contextCustomStyleData =
1901+ [customStyle getCustomStyleDataAt: textView.selectedRange.location];
1902+
18301903 emitter->onContextMenuItemPress (
18311904 {.itemText = [itemText toCppString ],
18321905 .selectedText = [selectedText toCppString ],
@@ -1853,7 +1926,16 @@ - (void)emitOnContextMenuItemPressEvent:(NSString *)itemText {
18531926 .image = GET_STYLE_STATE ([ImageStyle getType ]),
18541927 .mention = GET_STYLE_STATE ([MentionStyle getType ]),
18551928 .checkboxList = GET_STYLE_STATE ([CheckboxListStyle getType ]),
1856- .alignment = [currentAlignment UTF8String ]}});
1929+ .alignment = [currentAlignment UTF8String ],
1930+ .customStyle = {
1931+ .foregroundColor =
1932+ [[contextCustomStyleData.foregroundColor rgbaString ]
1933+ UTF8String ]
1934+ ?: " " ,
1935+ .backgroundColor =
1936+ [[contextCustomStyleData.backgroundColor rgbaString ]
1937+ UTF8String ]
1938+ ?: " " }}});
18571939 }
18581940}
18591941
0 commit comments