import React from "react"; import { Platform, StyleSheet, Text as RNText, type TextProps, type ViewStyle } from "react-native"; import T3MarkdownTextRunNativeComponent from "./T3MarkdownTextRunNativeComponent"; import T3MarkdownTextNativeComponent from "./T3MarkdownTextNativeComponent"; import { flattenStyles } from "./util"; const TextAncestorContext = React.createContext<[boolean, ViewStyle]>([ false, StyleSheet.create({}), ]); const textDefaults: TextProps = { allowFontScaling: true, selectable: true, }; const useTextAncestorContext = () => React.useContext(TextAncestorContext); /** * Event fired by `onSelectionChange`. `start`/`end` are 0-based UTF-16 indices * into the rendered string. `start === end` means the selection was cleared. */ export type SelectionChangeEvent = { nativeEvent: { target: number; start: number; end: number }; }; export type MarkdownTextPrimitiveProps = TextProps & { uiTextView?: boolean; /** * Fired when the native text selection changes. Only fires on iOS when * `uiTextView` is true. Note: fires on every selection-edge adjustment * (e.g. dragging a selection handle), so consumers driving expensive work * off this event should debounce. */ onSelectionChange?: (event: SelectionChangeEvent) => void; }; function MarkdownTextPrimitiveChild({ style, children, ...rest }: MarkdownTextPrimitiveProps) { const [isAncestor, rootStyle] = useTextAncestorContext(); // Flatten the styles, and apply the root styles when needed const flattenedStyle = React.useMemo(() => flattenStyles(rootStyle, style), [rootStyle, style]); const contextValue = React.useMemo<[boolean, ViewStyle]>( () => [true, flattenedStyle], [flattenedStyle], ); let childPosition = 0; const nativeChildren = React.Children.toArray(children).map((child) => { const position = childPosition; childPosition += 1; if (React.isValidElement(child)) { return child; } if (typeof child !== "string" && typeof child !== "number") { return null; } const text = child.toString(); return ( // @ts-expect-error The generated run props do not include inherited Text props. ); }); if (!isAncestor) { return ( {nativeChildren} ); } return <>{nativeChildren}; } function MarkdownTextPrimitiveInner(props: MarkdownTextPrimitiveProps) { const [isAncestor] = useTextAncestorContext(); // Even if the uiTextView prop is set, we can still default to using // normal selection (i.e. base RN text) if the text doesn't need to be // selectable if ((!props.selectable || !props.uiTextView) && !isAncestor) { return ; } return ; } export function MarkdownTextPrimitive(props: MarkdownTextPrimitiveProps) { if (Platform.OS !== "ios") { return ; } return ; }