1- import React , { ComponentType , ReactElement } from 'react' ;
2- import { StyleProp , StyleSheet , Text , TextProps , TextStyle } from 'react-native' ;
3-
4- type TextComponent = ComponentType < TextProps & any > ;
5-
1+ import type { ComponentType , ReactElement } from 'react' ;
2+ import React from 'react' ;
3+ import type { StyleProp , TextProps , TextStyle } from 'react-native' ;
4+ import { StyleSheet , Text , View } from 'react-native' ;
5+
6+ type TextComponent = ComponentType < TextProps > ;
7+ type Config = {
8+ additionalStyle ?: StyleProp < TextStyle > ;
9+ outerTextStyle ?: StyleProp < TextStyle > ;
10+ } ;
11+ type InnerConfig = {
12+ appendedStyle ?: StyleProp < TextStyle > ;
13+ outerStyle ?: StyleProp < TextStyle > ;
14+ } ;
615export default class SpannableBuilder {
716 static getInstanceWithComponent (
817 baseComponent ?: TextComponent ,
9- config ?: { additionalStyle ?: StyleProp < TextStyle > ; outerTextStyle ?: StyleProp < TextStyle > } ,
18+ config ?: Config
1019 ) : SpannableBuilder {
11- const BaseText = baseComponent || Text ;
20+ const BaseText : TextComponent = baseComponent || Text ;
1221
13- const Wrapped = ( props ) : ReactElement => {
22+ const Wrapped : TextComponent = (
23+ props : TextProps & InnerConfig
24+ ) : ReactElement => {
1425 const { style, children, appendedStyle, outerStyle } = props ;
1526
16- const flattenStyle = StyleSheet . flatten ( [ config ?. additionalStyle , style , outerStyle , appendedStyle ] ) ;
27+ const flattenStyle = StyleSheet . flatten ( [
28+ config ?. additionalStyle ,
29+ style ,
30+ outerStyle ,
31+ appendedStyle ,
32+ ] ) ;
1733
1834 return (
1935 < BaseText style = { flattenStyle } { ...props } >
@@ -25,21 +41,30 @@ export default class SpannableBuilder {
2541 return new SpannableBuilder ( Wrapped , config ?. outerTextStyle ) ;
2642 }
2743
28- static getInstance ( additionalStyle ?: StyleProp < TextStyle > , outerTextStyle ?: StyleProp < TextStyle > ) : SpannableBuilder {
44+ static getInstance (
45+ additionalStyle ?: StyleProp < TextStyle > ,
46+ outerTextStyle ?: StyleProp < TextStyle >
47+ ) : SpannableBuilder {
2948 if ( ! additionalStyle ) return new SpannableBuilder ( Text ) ;
3049
31- return SpannableBuilder . getInstanceWithComponent ( Text , { additionalStyle, outerTextStyle } ) ;
50+ return SpannableBuilder . getInstanceWithComponent ( Text , {
51+ additionalStyle,
52+ outerTextStyle,
53+ } ) ;
3254 }
3355
34- readonly #TextComponent: TextComponent ;
56+ readonly #TextComponent: ComponentType < TextProps & InnerConfig > ;
3557
3658 #order = '' ;
37- readonly #textList: string [ ] = [ ] ;
59+ readonly #textList: ( string | ReactElement ) [ ] = [ ] ;
3860 readonly #customStyleList: StyleProp < TextStyle > [ ] = [ ] ;
3961
4062 readonly outerTextStyle ?: StyleProp < TextStyle > ;
4163
42- constructor ( textComponent : TextComponent , outerTextStyle ?: StyleProp < TextStyle > ) {
64+ constructor (
65+ textComponent : TextComponent ,
66+ outerTextStyle ?: StyleProp < TextStyle >
67+ ) {
4368 this . #TextComponent = textComponent ;
4469 this . outerTextStyle = outerTextStyle ;
4570 }
@@ -93,6 +118,13 @@ export default class SpannableBuilder {
93118 return this ;
94119 }
95120
121+ appendCustomComponent ( component : ReactElement ) : this {
122+ this . #textList. push ( component ) ;
123+ this . #order += 'C' ;
124+
125+ return this ;
126+ }
127+
96128 private appendWithDelimiter ( {
97129 appender,
98130 delimiter,
@@ -115,7 +147,11 @@ export default class SpannableBuilder {
115147 return this ;
116148 }
117149
118- appendCustomWithDelimiter ( text : string , style : StyleProp < TextStyle > , delimiter = '$' ) : this {
150+ appendCustomWithDelimiter (
151+ text : string ,
152+ style : StyleProp < TextStyle > ,
153+ delimiter = '$'
154+ ) : this {
119155 return this . appendWithDelimiter ( {
120156 text,
121157 delimiter,
@@ -145,7 +181,11 @@ export default class SpannableBuilder {
145181 } ) ;
146182 }
147183
148- appendColoredWithDelimiter ( text : string , color : string , delimiter = '$' ) : this {
184+ appendColoredWithDelimiter (
185+ text : string ,
186+ color : string ,
187+ delimiter = '$'
188+ ) : this {
149189 return this . appendWithDelimiter ( {
150190 text,
151191 delimiter,
@@ -156,23 +196,30 @@ export default class SpannableBuilder {
156196 }
157197
158198 build ( ) : ReactElement {
159- const BaseText : TextComponent = this . #TextComponent;
199+ const BaseText = this . #TextComponent;
160200
161201 let idx = 0 ;
162202 let customStyleIdx = 0 ;
163203
164204 const result = (
165205 < BaseText outerStyle = { this . outerTextStyle } >
166206 { [ ...this . #order] . map ( ( order , index ) => {
207+ const key = `${ order } ${ index } ` ;
208+
167209 switch ( order ) {
168210 case 'S' :
169211 return (
170- < BaseText key = { order + index } appendedStyle = { this . #customStyleList[ customStyleIdx ++ ] } >
212+ < BaseText
213+ key = { key }
214+ appendedStyle = { this . #customStyleList[ customStyleIdx ++ ] }
215+ >
171216 { this . #textList[ idx ++ ] }
172217 </ BaseText >
173218 ) ;
219+ case 'C' :
220+ return < View key = { key } > { this . #textList[ idx ++ ] } </ View > ;
174221 default :
175- return < BaseText key = { order + index } > { this . #textList[ idx ++ ] } </ BaseText > ;
222+ return < BaseText key = { key } > { this . #textList[ idx ++ ] } </ BaseText > ;
176223 }
177224 } ) }
178225 </ BaseText >
0 commit comments