move ghosttext component to textfield
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react'
|
import {createContext, useContext, useMemo, useRef} from 'react'
|
||||||
import {
|
import {
|
||||||
type AccessibilityProps,
|
type AccessibilityProps,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
@@ -16,7 +16,9 @@ import {
|
|||||||
applyFonts,
|
applyFonts,
|
||||||
atoms as a,
|
atoms as a,
|
||||||
ios,
|
ios,
|
||||||
|
platform,
|
||||||
type TextStyleProp,
|
type TextStyleProp,
|
||||||
|
tokens,
|
||||||
useAlf,
|
useAlf,
|
||||||
useTheme,
|
useTheme,
|
||||||
web,
|
web,
|
||||||
@@ -25,7 +27,7 @@ import {useInteractionState} from '#/components/hooks/useInteractionState'
|
|||||||
import {type Props as SVGIconProps} from '#/components/icons/common'
|
import {type Props as SVGIconProps} from '#/components/icons/common'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
const Context = React.createContext<{
|
const Context = createContext<{
|
||||||
inputRef: React.RefObject<TextInput> | null
|
inputRef: React.RefObject<TextInput> | null
|
||||||
isInvalid: boolean
|
isInvalid: boolean
|
||||||
hovered: boolean
|
hovered: boolean
|
||||||
@@ -48,7 +50,7 @@ const Context = React.createContext<{
|
|||||||
export type RootProps = React.PropsWithChildren<{isInvalid?: boolean}>
|
export type RootProps = React.PropsWithChildren<{isInvalid?: boolean}>
|
||||||
|
|
||||||
export function Root({children, isInvalid = false}: RootProps) {
|
export function Root({children, isInvalid = false}: RootProps) {
|
||||||
const inputRef = React.useRef<TextInput>(null)
|
const inputRef = useRef<TextInput>(null)
|
||||||
const {
|
const {
|
||||||
state: hovered,
|
state: hovered,
|
||||||
onIn: onHoverIn,
|
onIn: onHoverIn,
|
||||||
@@ -56,7 +58,7 @@ export function Root({children, isInvalid = false}: RootProps) {
|
|||||||
} = useInteractionState()
|
} = useInteractionState()
|
||||||
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
|
const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
|
||||||
|
|
||||||
const context = React.useMemo(
|
const context = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
inputRef,
|
inputRef,
|
||||||
hovered,
|
hovered,
|
||||||
@@ -96,7 +98,7 @@ export function Root({children, isInvalid = false}: RootProps) {
|
|||||||
|
|
||||||
export function useSharedInputStyles() {
|
export function useSharedInputStyles() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
return React.useMemo(() => {
|
return useMemo(() => {
|
||||||
const hover: ViewStyle[] = [
|
const hover: ViewStyle[] = [
|
||||||
{
|
{
|
||||||
borderColor: t.palette.contrast_100,
|
borderColor: t.palette.contrast_100,
|
||||||
@@ -158,7 +160,7 @@ export function createInput(Component: typeof TextInput) {
|
|||||||
}: InputProps) {
|
}: InputProps) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {fonts} = useAlf()
|
const {fonts} = useAlf()
|
||||||
const ctx = React.useContext(Context)
|
const ctx = useContext(Context)
|
||||||
const withinRoot = Boolean(ctx.inputRef)
|
const withinRoot = Boolean(ctx.inputRef)
|
||||||
|
|
||||||
const {chromeHover, chromeFocus, chromeError, chromeErrorHover} =
|
const {chromeHover, chromeFocus, chromeError, chromeErrorHover} =
|
||||||
@@ -283,8 +285,8 @@ export function LabelText({
|
|||||||
|
|
||||||
export function Icon({icon: Comp}: {icon: React.ComponentType<SVGIconProps>}) {
|
export function Icon({icon: Comp}: {icon: React.ComponentType<SVGIconProps>}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const ctx = React.useContext(Context)
|
const ctx = useContext(Context)
|
||||||
const {hover, focus, errorHover, errorFocus} = React.useMemo(() => {
|
const {hover, focus, errorHover, errorFocus} = useMemo(() => {
|
||||||
const hover: TextStyle[] = [
|
const hover: TextStyle[] = [
|
||||||
{
|
{
|
||||||
color: t.palette.contrast_800,
|
color: t.palette.contrast_800,
|
||||||
@@ -342,7 +344,7 @@ export function SuffixText({
|
|||||||
}
|
}
|
||||||
>) {
|
>) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const ctx = React.useContext(Context)
|
const ctx = useContext(Context)
|
||||||
return (
|
return (
|
||||||
<Text
|
<Text
|
||||||
accessibilityLabel={label}
|
accessibilityLabel={label}
|
||||||
@@ -362,3 +364,67 @@ export function SuffixText({
|
|||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GhostText({
|
||||||
|
children,
|
||||||
|
value,
|
||||||
|
}: {
|
||||||
|
children: string
|
||||||
|
value: string
|
||||||
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
|
// eslint-disable-next-line bsky-internal/avoid-unwrapped-text
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.pointer_events_none,
|
||||||
|
a.absolute,
|
||||||
|
a.z_10,
|
||||||
|
{
|
||||||
|
paddingLeft: platform({
|
||||||
|
native:
|
||||||
|
// input padding
|
||||||
|
tokens.space.md +
|
||||||
|
// icon
|
||||||
|
tokens.space.xl +
|
||||||
|
// icon padding
|
||||||
|
tokens.space.xs +
|
||||||
|
// text input padding
|
||||||
|
tokens.space.xs,
|
||||||
|
web:
|
||||||
|
// icon
|
||||||
|
tokens.space.xl +
|
||||||
|
// icon padding
|
||||||
|
tokens.space.xs +
|
||||||
|
// text input padding
|
||||||
|
tokens.space.xs,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
web(a.pr_md),
|
||||||
|
a.overflow_hidden,
|
||||||
|
a.max_w_full,
|
||||||
|
]}
|
||||||
|
aria-hidden={true}
|
||||||
|
accessibilityElementsHidden
|
||||||
|
importantForAccessibility="no-hide-descendants">
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
{color: 'transparent'},
|
||||||
|
a.text_md,
|
||||||
|
{lineHeight: a.text_md.fontSize * 1.1875},
|
||||||
|
a.w_full,
|
||||||
|
]}
|
||||||
|
numberOfLines={1}>
|
||||||
|
{children}
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
t.atoms.text_contrast_low,
|
||||||
|
a.text_md,
|
||||||
|
{lineHeight: a.text_md.fontSize * 1.1875},
|
||||||
|
]}>
|
||||||
|
{value}
|
||||||
|
</Text>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {useHandleAvailabilityQuery} from '#/state/queries/handle-availablility'
|
|||||||
import {useAgent} from '#/state/session'
|
import {useAgent} from '#/state/session'
|
||||||
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
import {ScreenTransition} from '#/screens/Login/ScreenTransition'
|
||||||
import {useSignupContext} from '#/screens/Signup/state'
|
import {useSignupContext} from '#/screens/Signup/state'
|
||||||
import {atoms as a, platform, tokens, useTheme, web} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import * as TextField from '#/components/forms/TextField'
|
import * as TextField from '#/components/forms/TextField'
|
||||||
import {useThrottledValue} from '#/components/hooks/useThrottledValue'
|
import {useThrottledValue} from '#/components/hooks/useThrottledValue'
|
||||||
import {At_Stroke2_Corner0_Rounded as AtIcon} from '#/components/icons/At'
|
import {At_Stroke2_Corner0_Rounded as AtIcon} from '#/components/icons/At'
|
||||||
@@ -152,9 +152,10 @@ export function StepHandle() {
|
|||||||
autoFocus
|
autoFocus
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{draftValue.length > 0 && (
|
{draftValue.length > 0 && (
|
||||||
<GhostText value={state.userDomain}>{draftValue}</GhostText>
|
<TextField.GhostText value={state.userDomain}>
|
||||||
|
{draftValue}
|
||||||
|
</TextField.GhostText>
|
||||||
)}
|
)}
|
||||||
</TextField.Root>
|
</TextField.Root>
|
||||||
</View>
|
</View>
|
||||||
@@ -297,61 +298,3 @@ function RequirementIcon({
|
|||||||
return <Loader size="sm" style={t.atoms.text_contrast_low} />
|
return <Loader size="sm" style={t.atoms.text_contrast_low} />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function GhostText({children, value}: {children: string; value: string}) {
|
|
||||||
const t = useTheme()
|
|
||||||
// eslint-disable-next-line bsky-internal/avoid-unwrapped-text
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
a.pointer_events_none,
|
|
||||||
a.absolute,
|
|
||||||
a.z_10,
|
|
||||||
{
|
|
||||||
paddingLeft: platform({
|
|
||||||
native:
|
|
||||||
// input padding
|
|
||||||
tokens.space.md +
|
|
||||||
// icon
|
|
||||||
tokens.space.xl +
|
|
||||||
// icon padding
|
|
||||||
tokens.space.xs +
|
|
||||||
// text input padding
|
|
||||||
tokens.space.xs,
|
|
||||||
web:
|
|
||||||
// icon
|
|
||||||
tokens.space.xl +
|
|
||||||
// icon padding
|
|
||||||
tokens.space.xs +
|
|
||||||
// text input padding
|
|
||||||
tokens.space.xs,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
web(a.pr_md),
|
|
||||||
a.overflow_hidden,
|
|
||||||
a.max_w_full,
|
|
||||||
]}
|
|
||||||
aria-hidden={true}
|
|
||||||
accessibilityElementsHidden
|
|
||||||
importantForAccessibility="no-hide-descendants">
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
{color: 'transparent'},
|
|
||||||
a.text_md,
|
|
||||||
{lineHeight: a.text_md.fontSize * 1.1875},
|
|
||||||
a.w_full,
|
|
||||||
]}
|
|
||||||
numberOfLines={1}>
|
|
||||||
{children}
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
t.atoms.text_contrast_low,
|
|
||||||
a.text_md,
|
|
||||||
{lineHeight: a.text_md.fontSize * 1.1875},
|
|
||||||
]}>
|
|
||||||
{value}
|
|
||||||
</Text>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user