1import React, { type ComponentRef, type Ref } from "react";
2import { Platform, StyleSheet, Text, type TextProps, type TextStyle } from "react-native";
3import T3SourceTextRunNativeComponent from "./T3SourceTextRunNativeComponent";
4import T3SourceTextNativeComponent from "./T3SourceTextNativeComponent";
5import { flattenStyles } from "./util";
6
7const TextAncestorContext = React.createContext<[boolean, TextStyle]>([false, {}]);
8
9export type SourceTextProps = Omit<TextProps, "onTextLayout" | "onPress" | "onLongPress"> & {
10 nativeTextRef?: Ref<ComponentRef<typeof Text>>;
11 onSelectionChange?: (event: {
12 nativeEvent: { target: number; start: number; end: number };
13 }) => void;
14};
15
16/** One selectable document on iOS, with nested runs carrying syntax colors. */
17export function SourceText({ style, children, nativeTextRef, ...props }: SourceTextProps) {
18 const [isAncestor, rootStyle] = React.useContext(TextAncestorContext);
19 const flattenedStyle = React.useMemo(() => flattenStyles(rootStyle, style), [rootStyle, style]);
20 const contextValue = React.useMemo<[boolean, TextStyle]>(
21 () => [true, StyleSheet.flatten([rootStyle, style]) ?? {}],
22 [rootStyle, style],
23 );
24 if (Platform.OS !== "ios") {
25 const { onSelectionChange: _onSelectionChange, ...textProps } = props;
26 return (
27 <Text ref={nativeTextRef} style={style} {...textProps}>
28 {children}
29 </Text>
30 );
31 }
32 const runs = React.Children.toArray(children).map((child, index) => {
33 if (React.isValidElement(child)) return child;
34 if (typeof child !== "string" && typeof child !== "number") return null;
35 return (
36 <T3SourceTextRunNativeComponent
37 key={index}
38 style={flattenedStyle}
39 {...flattenedStyle}
40 text={String(child)}
41 />
42 );
43 });
44 if (isAncestor) {
45 return <TextAncestorContext.Provider value={contextValue}>{runs}</TextAncestorContext.Provider>;
46 }
47 return (
48 <TextAncestorContext.Provider value={contextValue}>
49 <T3SourceTextNativeComponent allowFontScaling selectable {...props} style={flattenedStyle}>
50 {runs}
51 </T3SourceTextNativeComponent>
52 </TextAncestorContext.Provider>
53 );
54}