Telephone country code select (#9473)
* add telephone code select * add flags * run svgo on flags * get it somewhat working on web * get web closed state working * verify country code we get from geo * trim down names to shorter common versions * add labels to other selects * make international tel codes a static object * add component to storybook * Update src/lib/international-telephone-codes.ts Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * update to new geo hook * use Intl.DisplayNames, add polyfill * use in rather than keys().includes() --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>
This commit is contained in:
@@ -7,7 +7,7 @@ import {
|
||||
useState,
|
||||
} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
|
||||
import {useTheme} from '#/alf'
|
||||
@@ -15,9 +15,9 @@ import {atoms as a} from '#/alf'
|
||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {useInteractionState} from '#/components/hooks/useInteractionState'
|
||||
import {Check_Stroke2_Corner0_Rounded as CheckIcon} from '#/components/icons/Check'
|
||||
import {ChevronTopBottom_Stroke2_Corner0_Rounded as ChevronUpDownIcon} from '#/components/icons/Chevron'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {BaseRadio} from '../forms/Toggle'
|
||||
import {
|
||||
type ContentProps,
|
||||
type IconProps,
|
||||
@@ -122,10 +122,12 @@ export function ValueText({
|
||||
const t = useTheme()
|
||||
|
||||
let text = value && children(value)
|
||||
if (typeof text !== 'string') text = placeholder
|
||||
if (!text) text = placeholder
|
||||
|
||||
return (
|
||||
<ButtonText style={[t.atoms.text, a.font_normal, style]}>{text}</ButtonText>
|
||||
<ButtonText style={[t.atoms.text, a.font_normal, style]} emoji>
|
||||
{text}
|
||||
</ButtonText>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -162,15 +164,14 @@ export function Content<T>({
|
||||
}
|
||||
|
||||
function ContentInner<T>({
|
||||
label,
|
||||
items,
|
||||
renderItem,
|
||||
valueExtractor,
|
||||
...context
|
||||
}: ContentProps<T> & ContextType) {
|
||||
const control = Dialog.useDialogContext()
|
||||
|
||||
const {_} = useLingui()
|
||||
const [headerHeight, setHeaderHeight] = useState(50)
|
||||
const [headerHeight, setHeaderHeight] = useState(61)
|
||||
|
||||
const render = useCallback(
|
||||
({item, index}: {item: T; index: number}) => {
|
||||
@@ -179,33 +180,26 @@ function ContentInner<T>({
|
||||
[renderItem, context.value],
|
||||
)
|
||||
|
||||
const doneButton = useCallback(
|
||||
() => (
|
||||
<Button
|
||||
label={_(msg`Done`)}
|
||||
onPress={() => control.close()}
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="ghost"
|
||||
style={[a.rounded_full]}>
|
||||
<ButtonText style={[a.text_md]}>
|
||||
<Trans>Done</Trans>
|
||||
</ButtonText>
|
||||
</Button>
|
||||
),
|
||||
[control, _],
|
||||
)
|
||||
|
||||
return (
|
||||
<Context.Provider value={context}>
|
||||
<Dialog.Header
|
||||
renderRight={doneButton}
|
||||
onLayout={evt => setHeaderHeight(evt.nativeEvent.layout.height)}
|
||||
style={[a.absolute, a.top_0, a.left_0, a.right_0, a.z_10]}>
|
||||
<Dialog.HeaderText>
|
||||
<Trans>Select an option</Trans>
|
||||
style={[
|
||||
a.absolute,
|
||||
a.top_0,
|
||||
a.left_0,
|
||||
a.right_0,
|
||||
a.z_10,
|
||||
a.pt_3xl,
|
||||
a.pb_sm,
|
||||
a.border_b_0,
|
||||
]}>
|
||||
<Dialog.HeaderText
|
||||
style={[a.flex_1, a.px_xl, a.text_left, a.font_bold, a.text_2xl]}>
|
||||
{label ?? _(msg`Select an option`)}
|
||||
</Dialog.HeaderText>
|
||||
</Dialog.Header>
|
||||
<Dialog.Handle />
|
||||
<Dialog.InnerFlatList
|
||||
headerOffset={headerHeight}
|
||||
data={items}
|
||||
@@ -258,11 +252,12 @@ export function Item({children, value, label, style}: ItemProps) {
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.pl_md,
|
||||
a.px_xl,
|
||||
(focused || pressed) && t.atoms.bg_contrast_25,
|
||||
a.flex_row,
|
||||
a.align_center,
|
||||
a.gap_sm,
|
||||
a.py_md,
|
||||
style,
|
||||
]}>
|
||||
{children}
|
||||
@@ -273,20 +268,48 @@ export function Item({children, value, label, style}: ItemProps) {
|
||||
)
|
||||
}
|
||||
|
||||
export function ItemText({children}: ItemTextProps) {
|
||||
export function ItemText({children, style, emoji}: ItemTextProps) {
|
||||
const {selected} = useItemContext()
|
||||
const t = useTheme()
|
||||
|
||||
// eslint-disable-next-line bsky-internal/avoid-unwrapped-text
|
||||
return (
|
||||
<View style={[a.flex_1, a.py_md, a.border_b, t.atoms.border_contrast_low]}>
|
||||
<Text style={[a.text_md, selected && a.font_semi_bold]}>{children}</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={[a.text_md, selected && a.font_semi_bold, style]}
|
||||
emoji={emoji}>
|
||||
{children}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
|
||||
export function ItemIndicator({icon: Icon = CheckIcon}: ItemIndicatorProps) {
|
||||
const {selected} = useItemContext()
|
||||
export function ItemIndicator({icon: Icon}: ItemIndicatorProps) {
|
||||
const {selected, focused, hovered} = useItemContext()
|
||||
|
||||
return <View style={{width: 24}}>{selected && <Icon size="md" />}</View>
|
||||
if (Icon) {
|
||||
return <View style={{width: 24}}>{selected && <Icon size="md" />}</View>
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseRadio
|
||||
selected={selected}
|
||||
focused={focused}
|
||||
hovered={hovered}
|
||||
isInvalid={false}
|
||||
disabled={false}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export function Separator() {
|
||||
const t = useTheme()
|
||||
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
a.mx_xl,
|
||||
a.my_xs,
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user