about summary refs log tree commit diff
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/forms/TextField.tsx84
-rw-r--r--src/components/icons/Circle.tsx5
2 files changed, 80 insertions, 9 deletions
diff --git a/src/components/forms/TextField.tsx b/src/components/forms/TextField.tsx
index 9b7ada319..3913c3283 100644
--- a/src/components/forms/TextField.tsx
+++ b/src/components/forms/TextField.tsx
@@ -1,4 +1,4 @@
-import React from 'react'
+import {createContext, useContext, useMemo, useRef} from 'react'
 import {
   type AccessibilityProps,
   StyleSheet,
@@ -16,7 +16,9 @@ import {
   applyFonts,
   atoms as a,
   ios,
+  platform,
   type TextStyleProp,
+  tokens,
   useAlf,
   useTheme,
   web,
@@ -25,7 +27,7 @@ import {useInteractionState} from '#/components/hooks/useInteractionState'
 import {type Props as SVGIconProps} from '#/components/icons/common'
 import {Text} from '#/components/Typography'
 
-const Context = React.createContext<{
+const Context = createContext<{
   inputRef: React.RefObject<TextInput> | null
   isInvalid: boolean
   hovered: boolean
@@ -48,7 +50,7 @@ const Context = React.createContext<{
 export type RootProps = React.PropsWithChildren<{isInvalid?: boolean}>
 
 export function Root({children, isInvalid = false}: RootProps) {
-  const inputRef = React.useRef<TextInput>(null)
+  const inputRef = useRef<TextInput>(null)
   const {
     state: hovered,
     onIn: onHoverIn,
@@ -56,7 +58,7 @@ export function Root({children, isInvalid = false}: RootProps) {
   } = useInteractionState()
   const {state: focused, onIn: onFocus, onOut: onBlur} = useInteractionState()
 
-  const context = React.useMemo(
+  const context = useMemo(
     () => ({
       inputRef,
       hovered,
@@ -96,7 +98,7 @@ export function Root({children, isInvalid = false}: RootProps) {
 
 export function useSharedInputStyles() {
   const t = useTheme()
-  return React.useMemo(() => {
+  return useMemo(() => {
     const hover: ViewStyle[] = [
       {
         borderColor: t.palette.contrast_100,
@@ -158,7 +160,7 @@ export function createInput(Component: typeof TextInput) {
   }: InputProps) {
     const t = useTheme()
     const {fonts} = useAlf()
-    const ctx = React.useContext(Context)
+    const ctx = useContext(Context)
     const withinRoot = Boolean(ctx.inputRef)
 
     const {chromeHover, chromeFocus, chromeError, chromeErrorHover} =
@@ -283,8 +285,8 @@ export function LabelText({
 
 export function Icon({icon: Comp}: {icon: React.ComponentType<SVGIconProps>}) {
   const t = useTheme()
-  const ctx = React.useContext(Context)
-  const {hover, focus, errorHover, errorFocus} = React.useMemo(() => {
+  const ctx = useContext(Context)
+  const {hover, focus, errorHover, errorFocus} = useMemo(() => {
     const hover: TextStyle[] = [
       {
         color: t.palette.contrast_800,
@@ -342,7 +344,7 @@ export function SuffixText({
   }
 >) {
   const t = useTheme()
-  const ctx = React.useContext(Context)
+  const ctx = useContext(Context)
   return (
     <Text
       accessibilityLabel={label}
@@ -362,3 +364,67 @@ export function SuffixText({
     </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>
+  )
+}
diff --git a/src/components/icons/Circle.tsx b/src/components/icons/Circle.tsx
new file mode 100644
index 000000000..93d837119
--- /dev/null
+++ b/src/components/icons/Circle.tsx
@@ -0,0 +1,5 @@
+import {createSinglePathSVG} from './TEMPLATE'
+
+export const Circle_Stroke2_Corner0_Rounded = createSinglePathSVG({
+  path: 'M12 4a8 8 0 1 0 0 16 8 8 0 0 0 0-16ZM2 12C2 6.477 6.477 2 12 2s10 4.477 10 10-4.477 10-10 10S2 17.523 2 12Z',
+})