diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/view/com/pager/Pager.tsx | 7 | ||||
-rw-r--r-- | src/view/com/util/Selector.tsx | 148 | ||||
-rw-r--r-- | src/view/com/util/anim/TriggerableAnimated.tsx | 74 | ||||
-rw-r--r-- | src/view/com/util/text/RichText.tsx | 201 |
4 files changed, 3 insertions, 427 deletions
diff --git a/src/view/com/pager/Pager.tsx b/src/view/com/pager/Pager.tsx index aca3245a7..de0409991 100644 --- a/src/view/com/pager/Pager.tsx +++ b/src/view/com/pager/Pager.tsx @@ -1,5 +1,5 @@ import React, {forwardRef} from 'react' -import {Animated, View} from 'react-native' +import {View} from 'react-native' import PagerView, { PagerViewOnPageScrollEvent, PagerViewOnPageSelectedEvent, @@ -10,7 +10,6 @@ import {LogEvents} from '#/lib/statsig/events' import {atoms as a, native} from '#/alf' export type PageSelectedEvent = PagerViewOnPageSelectedEvent -const AnimatedPagerView = Animated.createAnimatedComponent(PagerView) export interface PagerRef { setPage: ( @@ -138,7 +137,7 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>( selectedPage, onSelect: onTabBarSelect, })} - <AnimatedPagerView + <PagerView ref={pagerView} style={[a.flex_1]} initialPage={initialPage} @@ -146,7 +145,7 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>( onPageSelected={onPageSelectedInner} onPageScroll={onPageScroll}> {children} - </AnimatedPagerView> + </PagerView> </View> ) }, diff --git a/src/view/com/util/Selector.tsx b/src/view/com/util/Selector.tsx deleted file mode 100644 index 86bd322e3..000000000 --- a/src/view/com/util/Selector.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import {createRef, useMemo, useRef, useState} from 'react' -import {Animated, Pressable, StyleSheet, View} from 'react-native' -import {msg} from '@lingui/macro' -import {useLingui} from '@lingui/react' - -import {usePalette} from '#/lib/hooks/usePalette' -import {Text} from './text/Text' - -interface Layout { - x: number - width: number -} - -export function Selector({ - selectedIndex, - items, - panX, - onSelect, -}: { - selectedIndex: number - items: string[] - panX: Animated.Value - onSelect?: (index: number) => void -}) { - const {_} = useLingui() - const containerRef = useRef<View>(null) - const pal = usePalette('default') - const [itemLayouts, setItemLayouts] = useState<undefined | Layout[]>( - undefined, - ) - const itemRefs = useMemo( - () => Array.from({length: items.length}).map(() => createRef<View>()), - [items.length], - ) - - const currentLayouts = useMemo(() => { - const left = itemLayouts?.[selectedIndex - 1] || {x: 0, width: 0} - const middle = itemLayouts?.[selectedIndex] || {x: 0, width: 0} - const right = itemLayouts?.[selectedIndex + 1] || { - x: middle.x + 20, - width: middle.width, - } - return [left, middle, right] - }, [selectedIndex, itemLayouts]) - - const underlineStyle = { - backgroundColor: pal.colors.text, - left: panX.interpolate({ - inputRange: [-1, 0, 1], - outputRange: [ - currentLayouts[0].x, - currentLayouts[1].x, - currentLayouts[2].x, - ], - }), - width: panX.interpolate({ - inputRange: [-1, 0, 1], - outputRange: [ - currentLayouts[0].width, - currentLayouts[1].width, - currentLayouts[2].width, - ], - }), - } - - const onLayout = () => { - const promises = [] - for (let i = 0; i < items.length; i++) { - promises.push( - new Promise<Layout>(resolve => { - if (!containerRef.current || !itemRefs[i].current) { - return resolve({x: 0, width: 0}) - } - itemRefs[i].current?.measureLayout( - containerRef.current, - (x: number, _y: number, width: number) => { - resolve({x, width}) - }, - ) - }), - ) - } - Promise.all(promises).then((layouts: Layout[]) => { - setItemLayouts(layouts) - }) - } - - const onPressItem = (index: number) => { - onSelect?.(index) - } - - const numItems = items.length - - return ( - <View - style={[pal.view, styles.outer]} - onLayout={onLayout} - ref={containerRef}> - <Animated.View style={[styles.underline, underlineStyle]} /> - {items.map((item, i) => { - const selected = i === selectedIndex - return ( - <Pressable - testID={`selector-${i}`} - key={item} - onPress={() => onPressItem(i)} - accessibilityLabel={_(msg`Select ${item}`)} - accessibilityHint={_(msg`Select option ${i} of ${numItems}`)}> - <View style={styles.item} ref={itemRefs[i]}> - <Text - style={ - selected - ? [styles.labelSelected, pal.text] - : [styles.label, pal.textLight] - }> - {item} - </Text> - </View> - </Pressable> - ) - })} - </View> - ) -} - -const styles = StyleSheet.create({ - outer: { - flexDirection: 'row', - paddingTop: 8, - paddingBottom: 12, - paddingHorizontal: 14, - }, - item: { - marginRight: 14, - paddingHorizontal: 10, - }, - label: { - fontWeight: '600', - }, - labelSelected: { - fontWeight: '600', - }, - underline: { - position: 'absolute', - height: 4, - bottom: 0, - }, -}) diff --git a/src/view/com/util/anim/TriggerableAnimated.tsx b/src/view/com/util/anim/TriggerableAnimated.tsx deleted file mode 100644 index 97605fb46..000000000 --- a/src/view/com/util/anim/TriggerableAnimated.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React from 'react' -import {Animated, StyleProp, View, ViewStyle} from 'react-native' - -import {useAnimatedValue} from '#/lib/hooks/useAnimatedValue' - -type CreateAnimFn = (interp: Animated.Value) => Animated.CompositeAnimation -type FinishCb = () => void - -interface TriggeredAnimation { - start: CreateAnimFn - style: ( - interp: Animated.Value, - ) => Animated.WithAnimatedValue<StyleProp<ViewStyle>> -} - -export interface TriggerableAnimatedRef { - trigger: (anim: TriggeredAnimation, onFinish?: FinishCb) => void -} - -type TriggerableAnimatedProps = React.PropsWithChildren<{}> - -type PropsInner = TriggerableAnimatedProps & { - anim: TriggeredAnimation - onFinish: () => void -} - -export const TriggerableAnimated = React.forwardRef< - TriggerableAnimatedRef, - TriggerableAnimatedProps ->(function TriggerableAnimatedImpl({children, ...props}, ref) { - const [anim, setAnim] = React.useState<TriggeredAnimation | undefined>( - undefined, - ) - const [finishCb, setFinishCb] = React.useState<FinishCb | undefined>( - undefined, - ) - React.useImperativeHandle(ref, () => ({ - trigger(v: TriggeredAnimation, cb?: FinishCb) { - setFinishCb(() => cb) // note- wrap in function due to react behaviors around setstate - setAnim(v) - }, - })) - const onFinish = () => { - finishCb?.() - setAnim(undefined) - setFinishCb(undefined) - } - return ( - <View key="triggerable"> - {anim ? ( - <AnimatingView anim={anim} onFinish={onFinish} {...props}> - {children} - </AnimatingView> - ) : ( - children - )} - </View> - ) -}) - -function AnimatingView({ - anim, - onFinish, - children, -}: React.PropsWithChildren<PropsInner>) { - const interp = useAnimatedValue(0) - React.useEffect(() => { - anim?.start(interp).start(() => { - onFinish() - }) - }) - const animStyle = anim?.style(interp) - return <Animated.View style={animStyle}>{children}</Animated.View> -} diff --git a/src/view/com/util/text/RichText.tsx b/src/view/com/util/text/RichText.tsx deleted file mode 100644 index a4cf517a4..000000000 --- a/src/view/com/util/text/RichText.tsx +++ /dev/null @@ -1,201 +0,0 @@ -import React from 'react' -import {StyleProp, TextStyle} from 'react-native' -import {AppBskyRichtextFacet, RichText as RichTextObj} from '@atproto/api' - -import {usePalette} from '#/lib/hooks/usePalette' -import {makeTagLink} from '#/lib/routes/links' -import {toShortUrl} from '#/lib/strings/url-helpers' -import {lh} from '#/lib/styles' -import {TypographyVariant, useTheme} from '#/lib/ThemeContext' -import {isNative} from '#/platform/detection' -import {TagMenu, useTagMenuControl} from '#/components/TagMenu' -import {TextLink} from '../Link' -import {Text} from './Text' - -const WORD_WRAP = {wordWrap: 1} - -/** - * @deprecated use `#/components/RichText` - */ -export function RichText({ - testID, - type = 'md', - richText, - lineHeight = 1.2, - style, - numberOfLines, - selectable, - noLinks, -}: { - testID?: string - type?: TypographyVariant - richText?: RichTextObj - lineHeight?: number - style?: StyleProp<TextStyle> - numberOfLines?: number - selectable?: boolean - noLinks?: boolean -}) { - const theme = useTheme() - const pal = usePalette('default') - const lineHeightStyle = lh(theme, type, lineHeight) - - if (!richText) { - return null - } - - const {text, facets} = richText - if (!facets?.length) { - if (/^\p{Extended_Pictographic}+$/u.test(text) && text.length <= 5) { - style = { - fontSize: 26, - lineHeight: 30, - } - return ( - // @ts-ignore web only -prf - <Text - testID={testID} - style={[style, pal.text]} - dataSet={WORD_WRAP} - selectable={selectable}> - {text} - </Text> - ) - } - return ( - <Text - testID={testID} - type={type} - style={[style, pal.text, lineHeightStyle]} - numberOfLines={numberOfLines} - // @ts-ignore web only -prf - dataSet={WORD_WRAP} - selectable={selectable}> - {text} - </Text> - ) - } - if (!style) { - style = [] - } else if (!Array.isArray(style)) { - style = [style] - } - - const els = [] - let key = 0 - for (const segment of richText.segments()) { - const link = segment.link - const mention = segment.mention - const tag = segment.tag - if ( - !noLinks && - mention && - AppBskyRichtextFacet.validateMention(mention).success - ) { - els.push( - <TextLink - key={key} - type={type} - text={segment.text} - href={`/profile/${mention.did}`} - style={[style, lineHeightStyle, pal.link, {pointerEvents: 'auto'}]} - dataSet={WORD_WRAP} - selectable={selectable} - />, - ) - } else if (link && AppBskyRichtextFacet.validateLink(link).success) { - if (noLinks) { - els.push(toShortUrl(segment.text)) - } else { - els.push( - <TextLink - key={key} - type={type} - text={toShortUrl(segment.text)} - href={link.uri} - style={[style, lineHeightStyle, pal.link, {pointerEvents: 'auto'}]} - dataSet={WORD_WRAP} - selectable={selectable} - />, - ) - } - } else if ( - !noLinks && - tag && - AppBskyRichtextFacet.validateTag(tag).success - ) { - els.push( - <RichTextTag - key={key} - text={segment.text} - type={type} - style={style} - lineHeightStyle={lineHeightStyle} - selectable={selectable} - />, - ) - } else { - els.push(segment.text) - } - key++ - } - return ( - <Text - testID={testID} - type={type} - style={[style, pal.text, lineHeightStyle]} - numberOfLines={numberOfLines} - // @ts-ignore web only -prf - dataSet={WORD_WRAP} - selectable={selectable}> - {els} - </Text> - ) -} - -function RichTextTag({ - text: tag, - type, - style, - lineHeightStyle, - selectable, -}: { - text: string - type?: TypographyVariant - style?: StyleProp<TextStyle> - lineHeightStyle?: TextStyle - selectable?: boolean -}) { - const pal = usePalette('default') - const control = useTagMenuControl() - - const open = React.useCallback(() => { - control.open() - }, [control]) - - return ( - <React.Fragment> - <TagMenu control={control} tag={tag}> - {isNative ? ( - <TextLink - type={type} - text={tag} - // segment.text has the leading "#" while tag.tag does not - href={makeTagLink(tag)} - style={[style, lineHeightStyle, pal.link, {pointerEvents: 'auto'}]} - dataSet={WORD_WRAP} - selectable={selectable} - onPress={open} - /> - ) : ( - <Text - selectable={selectable} - type={type} - style={[style, lineHeightStyle, pal.link, {pointerEvents: 'auto'}]}> - {tag} - </Text> - )} - </TagMenu> - </React.Fragment> - ) -} |