selectable text experiment

This commit is contained in:
Hailey
2024-01-11 00:38:47 -08:00
parent c75771047c
commit c6d2e54923
18 changed files with 787 additions and 2 deletions
@@ -0,0 +1,52 @@
import React from 'react'
import { ColorValue } from 'react-native'
interface ExpoProTextViewCommonProps {
selectable?: boolean
}
export interface ExpoProTextViewProps extends ExpoProTextViewCommonProps {
children: React.ReactNode
style?: ExpoProTextStyle
onPress?: () => void
onLongPress?: () => void
}
export interface ExpoProTextNativeViewProps extends ExpoProTextViewCommonProps {
segments: string
textStyle?: ExpoProTextStyle
onTextPress?: (event: ExpoProTextPressEvent) => void
onTextLongPress?: (event: ExpoProTextPressEvent) => void
onTextLayout?: (event: ExpoProTextLayoutEvent) => void
disableLongPress: boolean
}
interface ExpoProTextStyle {
color: ColorValue
fontSize?: number
fontStyle?: 'normal' | 'italic'
fontWeight?: 'normal' | 'bold'
letterSpacing?: number
textAlign?: 'auto' | 'left' | 'right' | 'center' | 'justify'
lineHeight?: number
}
export interface ExpoProTextLayoutEvent {
nativeEvent: {
height: number
}
}
export interface ExpoProTextPressEvent {
nativeEvent: {
index: number
}
}
export interface ExpoProTextSegment {
index: number
text: string
style?: ExpoProTextStyle
handlePress?: boolean
handleLongPress?: boolean
}
@@ -0,0 +1,5 @@
import { requireNativeModule } from 'expo-modules-core';
// It loads the native module object from the JSI or falls back to
// the bridge module (from NativeModulesProxy) if the remote debugger is on.
export default requireNativeModule('ExpoProText');
@@ -0,0 +1,13 @@
import { EventEmitter } from 'expo-modules-core';
const emitter = new EventEmitter({} as any);
export default {
PI: Math.PI,
async setValueAsync(value: string): Promise<void> {
emitter.emit('onChange', { value });
},
hello() {
return 'Hello world! 👋';
},
};
@@ -0,0 +1,109 @@
import { requireNativeViewManager } from 'expo-modules-core'
import * as React from 'react'
import {
ExpoProTextLayoutEvent,
ExpoProTextNativeViewProps,
ExpoProTextPressEvent,
ExpoProTextSegment,
ExpoProTextViewProps,
} from './ExpoProText.types'
import { Text, View } from 'react-native'
const NativeView: React.ComponentType<ExpoProTextNativeViewProps> =
requireNativeViewManager('ExpoProText')
export default function ExpoProTextView({
style,
children,
selectable = false,
onPress,
onLongPress,
}: ExpoProTextViewProps) {
const [dims, setDims] = React.useState({ height: 0 })
const segmentPressCallbacks = React.useRef<
Array<{ index: number; onPress: () => void }>
>([])
const segmentLongPressCallbacks = React.useRef<
Array<{ index: number, onLongPress: () => void, }>
>([])
const onTextLayout = React.useCallback((e: ExpoProTextLayoutEvent) => {
setDims({
height: e.nativeEvent.height,
})
}, [])
const onTextPress = React.useCallback((e: ExpoProTextPressEvent) => {
console.log(e.nativeEvent)
const onPressSegment = segmentPressCallbacks.current.find(
(s) => s.index === e.nativeEvent.index,
)
onPressSegment?.onPress()
}, [])
const textSegments = React.useMemo(() => {
const segments: ExpoProTextSegment[] = []
for (const [index, child] of React.Children.toArray(children).entries()) {
if (typeof child === 'string') {
segments.push({
index,
text: child,
style,
handlePress: onPress !== undefined,
handleLongPress: onLongPress !== undefined,
})
} else if (
React.isValidElement(child) &&
(child as React.ReactElement<any>).type === Text
) {
const { onPress, onLongPress, children, style } = child.props
segments.push({
index,
text: children,
style,
handlePress: onPress !== undefined,
handleLongPress: onLongPress !== undefined,
})
if (onPress !== undefined) {
segmentPressCallbacks.current.push({
index,
onPress,
})
}
if (onLongPress !== undefined) {
segmentLongPressCallbacks.current.push({
index,
onLongPress,
})
}
}
}
return segments
}, [children])
const segmentsJson = React.useMemo(() => {
const json = JSON.stringify({ segments: textSegments })
console.log(json)
return json
}, [textSegments])
return (
<View style={dims}>
<NativeView
textStyle={style}
segments={segmentsJson}
selectable={selectable}
onTextPress={onTextPress}
onTextLongPress={onLongPress}
onTextLayout={onTextLayout}
disableLongPress={onLongPress !== undefined}
/>
</View>
)
}
@@ -0,0 +1,11 @@
import * as React from 'react'
import { ExpoProTextViewProps } from './ExpoProText.types'
export default function ExpoProTextView(props: ExpoProTextViewProps) {
return (
<div>
<span>{props.name}</span>
</div>
)
}