Add `selectable` to new text components (#2899)
* Make new text selectable (broken) * Fixes * Fix bad conflict resolution * Remove consolezio/stable
parent
7390863a10
commit
943acd16aa
|
@ -1,9 +1,5 @@
|
|||
import React from 'react'
|
||||
import {
|
||||
GestureResponderEvent,
|
||||
Linking,
|
||||
TouchableWithoutFeedback,
|
||||
} from 'react-native'
|
||||
import {GestureResponderEvent, Linking} from 'react-native'
|
||||
import {
|
||||
useLinkProps,
|
||||
useNavigation,
|
||||
|
@ -23,7 +19,7 @@ import {
|
|||
} from '#/lib/strings/url-helpers'
|
||||
import {useModalControls} from '#/state/modals'
|
||||
import {router} from '#/routes'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {Text, TextProps} from '#/components/Typography'
|
||||
|
||||
/**
|
||||
* Only available within a `Link`, since that inherits from `Button`.
|
||||
|
@ -217,7 +213,7 @@ export function Link({
|
|||
}
|
||||
|
||||
export type InlineLinkProps = React.PropsWithChildren<
|
||||
BaseLinkProps & TextStyleProp
|
||||
BaseLinkProps & TextStyleProp & Pick<TextProps, 'selectable'>
|
||||
>
|
||||
|
||||
export function InlineLink({
|
||||
|
@ -228,6 +224,7 @@ export function InlineLink({
|
|||
style,
|
||||
onPress: outerOnPress,
|
||||
download,
|
||||
selectable,
|
||||
...rest
|
||||
}: InlineLinkProps) {
|
||||
const t = useTheme()
|
||||
|
@ -253,14 +250,8 @@ export function InlineLink({
|
|||
const flattenedStyle = flatten(style)
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback
|
||||
accessibilityRole="button"
|
||||
onPress={download ? undefined : onPress}
|
||||
onPressIn={onPressIn}
|
||||
onPressOut={onPressOut}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}>
|
||||
<Text
|
||||
selectable={selectable}
|
||||
label={href}
|
||||
{...rest}
|
||||
style={[
|
||||
|
@ -273,6 +264,11 @@ export function InlineLink({
|
|||
flattenedStyle,
|
||||
]}
|
||||
role="link"
|
||||
onPress={download ? undefined : onPress}
|
||||
onPressIn={onPressIn}
|
||||
onPressOut={onPressOut}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
onMouseEnter={onHoverIn}
|
||||
onMouseLeave={onHoverOut}
|
||||
accessibilityRole="link"
|
||||
|
@ -290,6 +286,5 @@ export function InlineLink({
|
|||
})}>
|
||||
{children}
|
||||
</Text>
|
||||
</TouchableWithoutFeedback>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api'
|
|||
|
||||
import {atoms as a, TextStyleProp} from '#/alf'
|
||||
import {InlineLink} from '#/components/Link'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {Text, TextProps} from '#/components/Typography'
|
||||
import {toShortUrl} from 'lib/strings/url-helpers'
|
||||
import {getAgent} from '#/state/session'
|
||||
|
||||
|
@ -16,13 +16,15 @@ export function RichText({
|
|||
numberOfLines,
|
||||
disableLinks,
|
||||
resolveFacets = false,
|
||||
}: TextStyleProp & {
|
||||
selectable,
|
||||
}: TextStyleProp &
|
||||
Pick<TextProps, 'selectable'> & {
|
||||
value: RichTextAPI | string
|
||||
testID?: string
|
||||
numberOfLines?: number
|
||||
disableLinks?: boolean
|
||||
resolveFacets?: boolean
|
||||
}) {
|
||||
}) {
|
||||
const detected = React.useRef(false)
|
||||
const [richText, setRichText] = React.useState<RichTextAPI>(() =>
|
||||
value instanceof RichTextAPI ? value : new RichTextAPI({text: value}),
|
||||
|
@ -50,6 +52,7 @@ export function RichText({
|
|||
if (text.length <= 5 && /^\p{Extended_Pictographic}+$/u.test(text)) {
|
||||
return (
|
||||
<Text
|
||||
selectable={selectable}
|
||||
testID={testID}
|
||||
style={[
|
||||
{
|
||||
|
@ -65,6 +68,7 @@ export function RichText({
|
|||
}
|
||||
return (
|
||||
<Text
|
||||
selectable={selectable}
|
||||
testID={testID}
|
||||
style={styles}
|
||||
numberOfLines={numberOfLines}
|
||||
|
@ -88,6 +92,7 @@ export function RichText({
|
|||
) {
|
||||
els.push(
|
||||
<InlineLink
|
||||
selectable={selectable}
|
||||
key={key}
|
||||
to={`/profile/${mention.did}`}
|
||||
style={[...styles, {pointerEvents: 'auto'}]}
|
||||
|
@ -102,6 +107,7 @@ export function RichText({
|
|||
} else {
|
||||
els.push(
|
||||
<InlineLink
|
||||
selectable={selectable}
|
||||
key={key}
|
||||
to={link.uri}
|
||||
style={[...styles, {pointerEvents: 'auto'}]}
|
||||
|
@ -120,6 +126,7 @@ export function RichText({
|
|||
|
||||
return (
|
||||
<Text
|
||||
selectable={selectable}
|
||||
testID={testID}
|
||||
style={styles}
|
||||
numberOfLines={numberOfLines}
|
||||
|
|
|
@ -1,7 +1,16 @@
|
|||
import React from 'react'
|
||||
import {Text as RNText, TextStyle, TextProps} from 'react-native'
|
||||
import {Text as RNText, TextStyle, TextProps as RNTextProps} from 'react-native'
|
||||
import {UITextView} from 'react-native-ui-text-view'
|
||||
|
||||
import {useTheme, atoms, web, flatten} from '#/alf'
|
||||
import {isIOS} from '#/platform/detection'
|
||||
|
||||
export type TextProps = RNTextProps & {
|
||||
/**
|
||||
* Lets the user select text, to use the native copy and paste functionality.
|
||||
*/
|
||||
selectable?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Util to calculate lineHeight from a text size atom and a leading atom
|
||||
|
@ -44,27 +53,24 @@ function normalizeTextStyles(styles: TextStyle[]) {
|
|||
/**
|
||||
* Our main text component. Use this most of the time.
|
||||
*/
|
||||
export function Text({style, ...rest}: TextProps) {
|
||||
export function Text({style, selectable, ...rest}: TextProps) {
|
||||
const t = useTheme()
|
||||
const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)])
|
||||
return <RNText style={s} {...rest} />
|
||||
return selectable && isIOS ? (
|
||||
<UITextView style={s} {...rest} />
|
||||
) : (
|
||||
<RNText selectable={selectable} style={s} {...rest} />
|
||||
)
|
||||
}
|
||||
|
||||
export function createHeadingElement({level}: {level: number}) {
|
||||
return function HeadingElement({style, ...rest}: TextProps) {
|
||||
const t = useTheme()
|
||||
const attr =
|
||||
web({
|
||||
role: 'heading',
|
||||
'aria-level': level,
|
||||
}) || {}
|
||||
return (
|
||||
<RNText
|
||||
{...attr}
|
||||
{...rest}
|
||||
style={normalizeTextStyles([t.atoms.text, flatten(style)])}
|
||||
/>
|
||||
)
|
||||
return <Text {...attr} {...rest} style={style} />
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,21 +84,15 @@ export const H4 = createHeadingElement({level: 4})
|
|||
export const H5 = createHeadingElement({level: 5})
|
||||
export const H6 = createHeadingElement({level: 6})
|
||||
export function P({style, ...rest}: TextProps) {
|
||||
const t = useTheme()
|
||||
const attr =
|
||||
web({
|
||||
role: 'paragraph',
|
||||
}) || {}
|
||||
return (
|
||||
<RNText
|
||||
<Text
|
||||
{...attr}
|
||||
{...rest}
|
||||
style={normalizeTextStyles([
|
||||
atoms.text_md,
|
||||
atoms.leading_normal,
|
||||
t.atoms.text,
|
||||
flatten(style),
|
||||
])}
|
||||
style={[atoms.text_md, atoms.leading_normal, flatten(style)]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,9 @@ import {RichText} from '#/components/RichText'
|
|||
export function Typography() {
|
||||
return (
|
||||
<View style={[a.gap_md]}>
|
||||
<Text style={[a.text_5xl]}>atoms.text_5xl</Text>
|
||||
<Text selectable style={[a.text_5xl]}>
|
||||
atoms.text_5xl
|
||||
</Text>
|
||||
<Text style={[a.text_4xl]}>atoms.text_4xl</Text>
|
||||
<Text style={[a.text_3xl]}>atoms.text_3xl</Text>
|
||||
<Text style={[a.text_2xl]}>atoms.text_2xl</Text>
|
||||
|
@ -24,6 +26,7 @@ export function Typography() {
|
|||
value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`}
|
||||
/>
|
||||
<RichText
|
||||
selectable
|
||||
resolveFacets
|
||||
value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`}
|
||||
style={[a.text_xl]}
|
||||
|
|
Loading…
Reference in New Issue