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 React from 'react'
|
||||||
import {
|
import {GestureResponderEvent, Linking} from 'react-native'
|
||||||
GestureResponderEvent,
|
|
||||||
Linking,
|
|
||||||
TouchableWithoutFeedback,
|
|
||||||
} from 'react-native'
|
|
||||||
import {
|
import {
|
||||||
useLinkProps,
|
useLinkProps,
|
||||||
useNavigation,
|
useNavigation,
|
||||||
|
@ -23,7 +19,7 @@ import {
|
||||||
} from '#/lib/strings/url-helpers'
|
} from '#/lib/strings/url-helpers'
|
||||||
import {useModalControls} from '#/state/modals'
|
import {useModalControls} from '#/state/modals'
|
||||||
import {router} from '#/routes'
|
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`.
|
* Only available within a `Link`, since that inherits from `Button`.
|
||||||
|
@ -217,7 +213,7 @@ export function Link({
|
||||||
}
|
}
|
||||||
|
|
||||||
export type InlineLinkProps = React.PropsWithChildren<
|
export type InlineLinkProps = React.PropsWithChildren<
|
||||||
BaseLinkProps & TextStyleProp
|
BaseLinkProps & TextStyleProp & Pick<TextProps, 'selectable'>
|
||||||
>
|
>
|
||||||
|
|
||||||
export function InlineLink({
|
export function InlineLink({
|
||||||
|
@ -228,6 +224,7 @@ export function InlineLink({
|
||||||
style,
|
style,
|
||||||
onPress: outerOnPress,
|
onPress: outerOnPress,
|
||||||
download,
|
download,
|
||||||
|
selectable,
|
||||||
...rest
|
...rest
|
||||||
}: InlineLinkProps) {
|
}: InlineLinkProps) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
@ -253,14 +250,8 @@ export function InlineLink({
|
||||||
const flattenedStyle = flatten(style)
|
const flattenedStyle = flatten(style)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableWithoutFeedback
|
|
||||||
accessibilityRole="button"
|
|
||||||
onPress={download ? undefined : onPress}
|
|
||||||
onPressIn={onPressIn}
|
|
||||||
onPressOut={onPressOut}
|
|
||||||
onFocus={onFocus}
|
|
||||||
onBlur={onBlur}>
|
|
||||||
<Text
|
<Text
|
||||||
|
selectable={selectable}
|
||||||
label={href}
|
label={href}
|
||||||
{...rest}
|
{...rest}
|
||||||
style={[
|
style={[
|
||||||
|
@ -273,6 +264,11 @@ export function InlineLink({
|
||||||
flattenedStyle,
|
flattenedStyle,
|
||||||
]}
|
]}
|
||||||
role="link"
|
role="link"
|
||||||
|
onPress={download ? undefined : onPress}
|
||||||
|
onPressIn={onPressIn}
|
||||||
|
onPressOut={onPressOut}
|
||||||
|
onFocus={onFocus}
|
||||||
|
onBlur={onBlur}
|
||||||
onMouseEnter={onHoverIn}
|
onMouseEnter={onHoverIn}
|
||||||
onMouseLeave={onHoverOut}
|
onMouseLeave={onHoverOut}
|
||||||
accessibilityRole="link"
|
accessibilityRole="link"
|
||||||
|
@ -290,6 +286,5 @@ export function InlineLink({
|
||||||
})}>
|
})}>
|
||||||
{children}
|
{children}
|
||||||
</Text>
|
</Text>
|
||||||
</TouchableWithoutFeedback>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {RichText as RichTextAPI, AppBskyRichtextFacet} from '@atproto/api'
|
||||||
|
|
||||||
import {atoms as a, TextStyleProp} from '#/alf'
|
import {atoms as a, TextStyleProp} from '#/alf'
|
||||||
import {InlineLink} from '#/components/Link'
|
import {InlineLink} from '#/components/Link'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text, TextProps} from '#/components/Typography'
|
||||||
import {toShortUrl} from 'lib/strings/url-helpers'
|
import {toShortUrl} from 'lib/strings/url-helpers'
|
||||||
import {getAgent} from '#/state/session'
|
import {getAgent} from '#/state/session'
|
||||||
|
|
||||||
|
@ -16,7 +16,9 @@ export function RichText({
|
||||||
numberOfLines,
|
numberOfLines,
|
||||||
disableLinks,
|
disableLinks,
|
||||||
resolveFacets = false,
|
resolveFacets = false,
|
||||||
}: TextStyleProp & {
|
selectable,
|
||||||
|
}: TextStyleProp &
|
||||||
|
Pick<TextProps, 'selectable'> & {
|
||||||
value: RichTextAPI | string
|
value: RichTextAPI | string
|
||||||
testID?: string
|
testID?: string
|
||||||
numberOfLines?: number
|
numberOfLines?: number
|
||||||
|
@ -50,6 +52,7 @@ export function RichText({
|
||||||
if (text.length <= 5 && /^\p{Extended_Pictographic}+$/u.test(text)) {
|
if (text.length <= 5 && /^\p{Extended_Pictographic}+$/u.test(text)) {
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
|
selectable={selectable}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
|
@ -65,6 +68,7 @@ export function RichText({
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
|
selectable={selectable}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
style={styles}
|
style={styles}
|
||||||
numberOfLines={numberOfLines}
|
numberOfLines={numberOfLines}
|
||||||
|
@ -88,6 +92,7 @@ export function RichText({
|
||||||
) {
|
) {
|
||||||
els.push(
|
els.push(
|
||||||
<InlineLink
|
<InlineLink
|
||||||
|
selectable={selectable}
|
||||||
key={key}
|
key={key}
|
||||||
to={`/profile/${mention.did}`}
|
to={`/profile/${mention.did}`}
|
||||||
style={[...styles, {pointerEvents: 'auto'}]}
|
style={[...styles, {pointerEvents: 'auto'}]}
|
||||||
|
@ -102,6 +107,7 @@ export function RichText({
|
||||||
} else {
|
} else {
|
||||||
els.push(
|
els.push(
|
||||||
<InlineLink
|
<InlineLink
|
||||||
|
selectable={selectable}
|
||||||
key={key}
|
key={key}
|
||||||
to={link.uri}
|
to={link.uri}
|
||||||
style={[...styles, {pointerEvents: 'auto'}]}
|
style={[...styles, {pointerEvents: 'auto'}]}
|
||||||
|
@ -120,6 +126,7 @@ export function RichText({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
|
selectable={selectable}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
style={styles}
|
style={styles}
|
||||||
numberOfLines={numberOfLines}
|
numberOfLines={numberOfLines}
|
||||||
|
|
|
@ -1,7 +1,16 @@
|
||||||
import React from 'react'
|
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 {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
|
* 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.
|
* 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 t = useTheme()
|
||||||
const s = normalizeTextStyles([atoms.text_sm, t.atoms.text, flatten(style)])
|
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}) {
|
export function createHeadingElement({level}: {level: number}) {
|
||||||
return function HeadingElement({style, ...rest}: TextProps) {
|
return function HeadingElement({style, ...rest}: TextProps) {
|
||||||
const t = useTheme()
|
|
||||||
const attr =
|
const attr =
|
||||||
web({
|
web({
|
||||||
role: 'heading',
|
role: 'heading',
|
||||||
'aria-level': level,
|
'aria-level': level,
|
||||||
}) || {}
|
}) || {}
|
||||||
return (
|
return <Text {...attr} {...rest} style={style} />
|
||||||
<RNText
|
|
||||||
{...attr}
|
|
||||||
{...rest}
|
|
||||||
style={normalizeTextStyles([t.atoms.text, flatten(style)])}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,21 +84,15 @@ export const H4 = createHeadingElement({level: 4})
|
||||||
export const H5 = createHeadingElement({level: 5})
|
export const H5 = createHeadingElement({level: 5})
|
||||||
export const H6 = createHeadingElement({level: 6})
|
export const H6 = createHeadingElement({level: 6})
|
||||||
export function P({style, ...rest}: TextProps) {
|
export function P({style, ...rest}: TextProps) {
|
||||||
const t = useTheme()
|
|
||||||
const attr =
|
const attr =
|
||||||
web({
|
web({
|
||||||
role: 'paragraph',
|
role: 'paragraph',
|
||||||
}) || {}
|
}) || {}
|
||||||
return (
|
return (
|
||||||
<RNText
|
<Text
|
||||||
{...attr}
|
{...attr}
|
||||||
{...rest}
|
{...rest}
|
||||||
style={normalizeTextStyles([
|
style={[atoms.text_md, atoms.leading_normal, flatten(style)]}
|
||||||
atoms.text_md,
|
|
||||||
atoms.leading_normal,
|
|
||||||
t.atoms.text,
|
|
||||||
flatten(style),
|
|
||||||
])}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@ import {RichText} from '#/components/RichText'
|
||||||
export function Typography() {
|
export function Typography() {
|
||||||
return (
|
return (
|
||||||
<View style={[a.gap_md]}>
|
<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_4xl]}>atoms.text_4xl</Text>
|
||||||
<Text style={[a.text_3xl]}>atoms.text_3xl</Text>
|
<Text style={[a.text_3xl]}>atoms.text_3xl</Text>
|
||||||
<Text style={[a.text_2xl]}>atoms.text_2xl</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`}
|
value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`}
|
||||||
/>
|
/>
|
||||||
<RichText
|
<RichText
|
||||||
|
selectable
|
||||||
resolveFacets
|
resolveFacets
|
||||||
value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`}
|
value={`This is rich text. It can have mentions like @bsky.app or links like https://bsky.social`}
|
||||||
style={[a.text_xl]}
|
style={[a.text_xl]}
|
||||||
|
|
Loading…
Reference in New Issue