diff --git a/apps/tlon-mobile/src/screens/Onboarding/PasteInviteLinkScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/PasteInviteLinkScreen.tsx index 906c24a663..38b51692eb 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/PasteInviteLinkScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/PasteInviteLinkScreen.tsx @@ -8,7 +8,6 @@ import { import { useBranch, useLureMetadata } from '@tloncorp/app/contexts/branch'; import { trackError, trackOnboardingAction } from '@tloncorp/app/utils/posthog'; import { - DeepLinkData, createInviteLinkRegex, extractNormalizedInviteLink, getInviteLinkMeta, @@ -17,7 +16,7 @@ import { Field, Pressable, ScreenHeader, - TextInputWithButton, + TextInput, TlonText, View, YStack, @@ -159,7 +158,7 @@ export const PasteInviteLinkScreen = ({ navigation }: Props) => { error={metadataError ?? errors.inviteLink?.message} paddingTop="$l" > - { keyboardType="email-address" autoCapitalize="none" autoCorrect={false} - buttonText="Paste" - onButtonPress={onHandlePasteClick} + rightControls={ + + } /> )} diff --git a/apps/tlon-mobile/src/screens/Onboarding/ShipLoginScreen.tsx b/apps/tlon-mobile/src/screens/Onboarding/ShipLoginScreen.tsx index 69524a6bad..be8d405cbd 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/ShipLoginScreen.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/ShipLoginScreen.tsx @@ -16,7 +16,6 @@ import { OnboardingTextBlock, ScreenHeader, TextInput, - TextInputWithButton, TlonText, View, YStack, @@ -211,7 +210,7 @@ export const ShipLoginScreen = ({ navigation }: Props) => { }} render={({ field: { onChange, onBlur, value } }) => ( - { @@ -226,8 +225,12 @@ export const ShipLoginScreen = ({ navigation }: Props) => { autoCorrect={false} returnKeyType="send" enablesReturnKeyAutomatically - buttonText={codevisible ? 'Hide' : 'Show'} - onButtonPress={() => setCodeVisible(!codevisible)} + rightControls={ + setCodeVisible(!codevisible)} + /> + } /> )} diff --git a/apps/tlon-mobile/src/screens/Onboarding/TlonLoginLegacy.tsx b/apps/tlon-mobile/src/screens/Onboarding/TlonLoginLegacy.tsx index 8673bc44d2..cd83b9bd2e 100644 --- a/apps/tlon-mobile/src/screens/Onboarding/TlonLoginLegacy.tsx +++ b/apps/tlon-mobile/src/screens/Onboarding/TlonLoginLegacy.tsx @@ -21,7 +21,6 @@ import { OnboardingTextBlock, ScreenHeader, TextInput, - TextInputWithButton, TlonText, View, YStack, @@ -229,7 +228,7 @@ export const TlonLoginLegacy = ({ navigation }: Props) => { }} render={({ field: { onChange, onBlur, value } }) => ( - { onBlur(); @@ -243,8 +242,12 @@ export const TlonLoginLegacy = ({ navigation }: Props) => { autoCorrect={false} returnKeyType="send" enablesReturnKeyAutomatically - buttonText={passwordVisible ? 'Hide' : 'Show'} - onButtonPress={() => setPasswordVisible(!passwordVisible)} + rightControls={ + setPasswordVisible(!passwordVisible)} + /> + } /> )} diff --git a/packages/app/fixtures/Form.fixture.tsx b/packages/app/fixtures/Form.fixture.tsx index 2552f0c490..51001081df 100644 --- a/packages/app/fixtures/Form.fixture.tsx +++ b/packages/app/fixtures/Form.fixture.tsx @@ -1,6 +1,6 @@ -import { Button, ScrollView } from '@tloncorp/ui'; +import { Button, IconType, ScrollView, View } from '@tloncorp/ui'; import * as Form from '@tloncorp/ui/src/components/Form'; -import { useMemo } from 'react'; +import React, { useMemo, useState } from 'react'; import { useForm } from 'react-hook-form'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; @@ -16,6 +16,7 @@ const FormFixture = () => { number: 'one', listItem: 'chat', image: undefined, + textarea: undefined, }, }); @@ -25,7 +26,12 @@ const FormFixture = () => { const options: Form.RadioInputOption[] = [ { title: 'One', value: 'one', description: 'This is one things' }, - { title: 'Two', value: 'two', description: 'This is two things' }, + { + title: 'Two', + value: 'two', + description: 'This is two things', + disabled: true, + }, { title: 'Three', value: 'three', description: 'This is three things' }, ]; @@ -51,10 +57,21 @@ const FormFixture = () => { icon: 'ChannelGalleries', }, ]; + const [selectedToggleGroupOption, setSelectedToggleGroupOptions] = + useState('chat'); + + const toggleGroupOptions: { label: string; value: string }[] = [ + { label: 'Chat', value: 'chat' }, + { label: 'Notebook', value: 'notebook' }, + { label: 'Gallery', value: 'gallery' }, + { label: 'And', value: '0' }, + { label: 'More', value: '1' }, + { label: 'Items', value: '2' }, + ]; return ( - + { label="Title" control={control} rules={formRules} + inputProps={{ icon: 'Search', placeholder: 'Type here' }} /> { label="List input" control={control} /> - - - - + + + + + + - + + + + + - {}} + rightControls={ + {}} /> + } /> @@ -108,8 +153,68 @@ const FormFixture = () => { ); }; -export default ( - - - -); +const accents: Form.Accent[] = ['neutral', 'positive', 'negative']; +const icons: (IconType | undefined)[] = [undefined, 'Search']; +const buttonLabels: (string | undefined)[] = [undefined, 'Clear']; +const backgroundTypes = ['primary', 'secondary'] as const; + +export default { + textInput: ( + + + {backgroundTypes.map((backgroundType) => ( + + {buttonLabels.map((buttonLabel) => ( + + {icons.map((icon) => ( + + + {!icon && !buttonLabel + ? 'Default' + : icon && !buttonLabel + ? 'Icon, no button' + : !icon && buttonLabel + ? 'Button, no icon' + : 'Icon and button'} + + + {accents.map((accent) => ( + + + ) : undefined + } + /> + + ))} + + + ))} + + ))} + + ))} + + + ), + full: ( + + + + ), +}; diff --git a/packages/app/tamagui.d.ts b/packages/app/tamagui.d.ts new file mode 100644 index 0000000000..ea81e5c0c9 --- /dev/null +++ b/packages/app/tamagui.d.ts @@ -0,0 +1,12 @@ +import { config } from '../ui/src/tamagui.config'; + +export type Conf = typeof config; + +// Sets up typing for tamagui so that theme variables autocomplete +declare module 'tamagui' { + interface TamaguiCustomConfig extends Conf {} + + interface TypeOverride { + groupNames(): 'button'; + } +} diff --git a/packages/app/tsconfig.json b/packages/app/tsconfig.json index 2670166dee..cd34d85852 100644 --- a/packages/app/tsconfig.json +++ b/packages/app/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "../../tsconfig", - "include": ["**/*.ts", "**/*.tsx", "window.ts"], + "include": ["**/*.ts", "**/*.tsx", "window.ts", "tamagui.d.ts"], "compilerOptions": { "composite": true, "jsx": "react-jsx", diff --git a/packages/ui/src/components/ActionList/index.tsx b/packages/ui/src/components/ActionList/index.tsx index ab4c010ed8..803ce9044f 100644 --- a/packages/ui/src/components/ActionList/index.tsx +++ b/packages/ui/src/components/ActionList/index.tsx @@ -7,10 +7,10 @@ import ListFrame from './ListFrame'; const ActionFrame = styled(ListItem, { borderRadius: 'unset', borderBottomWidth: 0.5, - borderBottomColor: '$secondaryBorder', + borderBottomColor: '$border', backgroundColor: 'transparent', pressStyle: { - backgroundColor: '$secondaryBorder', + backgroundColor: '$border', }, variants: { last: { diff --git a/packages/ui/src/components/ChatList.tsx b/packages/ui/src/components/ChatList.tsx index 4c659154ce..4b102d1398 100644 --- a/packages/ui/src/components/ChatList.tsx +++ b/packages/ui/src/components/ChatList.tsx @@ -22,7 +22,7 @@ import { Text, View, YStack, getTokenValue, useTheme } from 'tamagui'; import { useCalm, useChatOptions } from '../contexts'; import { getChannelTitle, getGroupTitle } from '../utils'; import { interactionWithTiming } from '../utils/animation'; -import { TextInputWithIconAndButton } from './Form'; +import { TextInput } from './Form'; import { ChatListItem, InteractableChatListItem } from './ListItem'; import Pressable from './Pressable'; import { SectionListHeader } from './SectionList'; @@ -292,7 +292,7 @@ const ChatListSearch = React.memo(function ChatListSearchComponent({ right={0} > - + } /> diff --git a/packages/ui/src/components/Form/Field.tsx b/packages/ui/src/components/Form/Field.tsx new file mode 100644 index 0000000000..b8e0af1110 --- /dev/null +++ b/packages/ui/src/components/Form/Field.tsx @@ -0,0 +1,90 @@ +import React, { useContext } from 'react'; +import { YStack, styled } from 'tamagui'; +import { createStyledContext } from 'tamagui'; + +import { VariantsFromStyledContext } from '../../types'; +import { Text } from '../TextV2'; +import { FormContext } from './Form'; +import { Accent, BackgroundType } from './formUtils'; + +// Single field +type FieldContextValue = { + accent: Accent; + disabled: boolean; + backgroundType: BackgroundType; +}; + +export const FieldContext = createStyledContext({ + accent: 'neutral', + disabled: false, + backgroundType: 'primary', +}); + +export const FieldFrame = styled(YStack, { + context: FieldContext, + variants: { + accent: {}, + } as VariantsFromStyledContext, +}); + +export const Field = FieldFrame.styleable<{ + label?: string; + error?: string; + required?: boolean; + renderInputContainer?: (props: { + children: React.ReactNode; + }) => React.ReactNode; +}>( + ( + { + children, + label, + required, + error, + renderInputContainer, + backgroundType, + ...props + }, + ref + ) => { + const { backgroundType: formBackgroundType } = useContext(FormContext); + + return ( + + {label ? ( + + {label} + {required ? '*' : null} + + ) : null} + {renderInputContainer ? renderInputContainer({ children }) : children} + {error && {error}} + + ); + }, + { + staticConfig: { + componentName: 'Field', + memo: true, + }, + } +); + +export const FieldLabel = React.memo( + styled(Text, { + color: '$tertiaryText', + size: '$label/m', + paddingHorizontal: '$xl', + paddingBottom: '$l', + }) +); + +export const FieldErrorMessage = styled(FieldLabel, { + color: '$negativeActionText', + paddingTop: '$l', +}); diff --git a/packages/ui/src/components/Form/Form.tsx b/packages/ui/src/components/Form/Form.tsx index 9e6dddfbf2..990f943113 100644 --- a/packages/ui/src/components/Form/Form.tsx +++ b/packages/ui/src/components/Form/Form.tsx @@ -1,78 +1,31 @@ -import React from 'react'; -import { ViewStyle, YStack, createStyledContext, styled } from 'tamagui'; +import { YStack, createStyledContext, styled } from 'tamagui'; import { Text } from '../TextV2'; +import { BackgroundType } from './formUtils'; -// Top level form +export const FormContext = createStyledContext<{ + backgroundType: BackgroundType; +}>({ backgroundType: 'primary' }); -export type Accent = 'positive' | 'negative' | 'neutral'; +// Top level form export const FormFrame = styled(YStack, { + context: FormContext, padding: '$2xl', gap: '$2xl', + variants: { + backgroundType: { + primary: { + backgroundColor: '$background', + }, + secondary: { + backgroundColor: '$secondaryBackground', + }, + }, + } as const, }); export const FormText = styled(Text, { size: '$body', padding: '$xl', }); - -// Single field - -export const FieldContext = createStyledContext<{ accent?: Accent }>({ - accent: 'neutral', -}); - -export const FieldFrame = styled(YStack, { - context: FieldContext, - variants: { - accent: {}, - } as { accent: Record }, -}); - -export const Field = React.memo( - FieldFrame.styleable<{ - label?: string; - error?: string; - required?: boolean; - renderInputContainer?: (props: { - children: React.ReactNode; - }) => React.ReactNode; - }>( - ( - { children, label, required, error, renderInputContainer, ...props }, - ref - ) => { - return ( - - {label ? ( - - {label} - {required ? '*' : null} - - ) : null} - {renderInputContainer ? renderInputContainer({ children }) : children} - {error && {error}} - - ); - } - ) -); - -export const FieldLabel = React.memo( - styled(Text, { - color: '$tertiaryText', - size: '$label/m', - paddingHorizontal: '$xl', - paddingBottom: '$l', - }) -); - -export const FieldErrorMessage = styled(FieldLabel, { - color: '$negativeActionText', - paddingTop: '$l', -}); diff --git a/packages/ui/src/components/Form/controlledFields.tsx b/packages/ui/src/components/Form/controlledFields.tsx index 700d836ab7..6f4ead2502 100644 --- a/packages/ui/src/components/Form/controlledFields.tsx +++ b/packages/ui/src/components/Form/controlledFields.tsx @@ -8,7 +8,7 @@ import { useController, } from 'react-hook-form'; -import { Field } from './Form'; +import { Field } from './Field'; import { ImageInput, ListItemInput, diff --git a/packages/ui/src/components/Form/formUtils.tsx b/packages/ui/src/components/Form/formUtils.tsx new file mode 100644 index 0000000000..ddef4a6c13 --- /dev/null +++ b/packages/ui/src/components/Form/formUtils.tsx @@ -0,0 +1,29 @@ +import { ColorTokens } from 'tamagui'; + +export type Accent = 'positive' | 'negative' | 'neutral'; +export type BackgroundType = 'primary' | 'secondary'; + +export function getBorderVariantStyle>( + backgroundType: BackgroundType, + { props }: { props: TProps } +) { + return { + borderColor: getBorderColor(backgroundType, props), + borderWidth: 1, + }; +} + +export function getBorderColor>( + backgroundType: BackgroundType, + props: TProps +): ColorTokens { + const accent = props.accent ? (props.accent as Accent) : 'neutral'; + switch (accent) { + case 'positive': + return '$positiveBorder'; + case 'negative': + return '$negativeBorder'; + case 'neutral': + return backgroundType === 'primary' ? '$border' : '$secondaryBorder'; + } +} diff --git a/packages/ui/src/components/Form/index.tsx b/packages/ui/src/components/Form/index.tsx index 062f137c3c..1092275788 100644 --- a/packages/ui/src/components/Form/index.tsx +++ b/packages/ui/src/components/Form/index.tsx @@ -1,3 +1,5 @@ export * from './Form'; export * from './controlledFields'; export * from './inputs'; +export * from './Field'; +export * from './formUtils'; diff --git a/packages/ui/src/components/Form/inputs.tsx b/packages/ui/src/components/Form/inputs.tsx index 2466db4f43..966f073677 100644 --- a/packages/ui/src/components/Form/inputs.tsx +++ b/packages/ui/src/components/Form/inputs.tsx @@ -1,19 +1,30 @@ import { ImagePickerAsset } from 'expo-image-picker'; -import React, { +import { ComponentProps, ReactElement, + ReactNode, useCallback, + useContext, useEffect, useState, } from 'react'; +import React from 'react'; import { Alert, TextInput as RNTextInput } from 'react-native'; -import { ScrollView, Spinner, View, XStack, YStack, styled } from 'tamagui'; -import { isWeb } from 'tamagui'; +import { + ScrollView, + Spinner, + View, + XStack, + YStack, + styled, + withStaticProperties, +} from 'tamagui'; import { useAttachmentContext, useMappedImageAttachments, } from '../../contexts'; +import { VariantsFromValues } from '../../types'; import AttachmentSheet from '../AttachmentSheet'; import { Button } from '../Button'; import { Icon, IconType } from '../Icon'; @@ -22,11 +33,38 @@ import { ListItem } from '../ListItem'; import { useBoundHandler } from '../ListItem/listItemUtils'; import Pressable from '../Pressable'; import { Text } from '../TextV2'; -import { FieldContext } from './Form'; +import { typeStyles } from '../TextV2/Text'; +import { FieldContext } from './Field'; +import { + Accent, + getBorderVariantStyle as getBackgroundTypeVariantStyle, +} from './formUtils'; -const StyledTextInput = styled( +export const RawTextInput = styled( RNTextInput, - {}, + { + name: 'RawTextInput', + ...typeStyles['$label/xl'], + lineHeight: 'unset', + context: FieldContext, + color: '$primaryText', + placeholderTextColor: '$tertiaryText', + fontFamily: '$body', + textAlignVertical: 'top', + paddingVertical: '$l', + numberOfLines: 1, + '$platform-web': { outlineStyle: 'none' }, + variants: { + accent: { + negative: { + color: '$negativeActionText', + }, + positive: { + color: '$positiveActionText', + }, + }, + } as const, + }, { isInput: true, accept: { @@ -38,119 +76,99 @@ const StyledTextInput = styled( // Text input -export const BaseTextInput = styled(StyledTextInput, { +export const InputFrame = styled(XStack, { + name: 'InputFrame', context: FieldContext, - color: '$primaryText', - borderRadius: '$l', + paddingLeft: '$xl', + paddingRight: '$l', + gap: '$m', borderWidth: 1, - borderColor: '$border', + borderRadius: '$l', backgroundColor: '$background', - placeholderTextColor: '$tertiaryText', - fontSize: '$l', - padding: '$xl', - fontFamily: '$body', - textAlignVertical: 'top', + borderColor: '$border', + overflow: 'hidden', + alignItems: 'center', + height: 56, variants: { accent: { negative: { backgroundColor: '$negativeBackground', - color: '$negativeActionText', borderColor: '$negativeBorder', }, + positive: { + backgroundColor: '$positiveBackground', + borderColor: '$positiveBorder', + }, }, - }, - ...(isWeb ? { outlineStyle: 'none' } : {}), + backgroundType: getBackgroundTypeVariantStyle, + } as VariantsFromValues<{ + accent: Accent; + backgroundType: 'primary' | 'secondary'; + }>, }); -export const TextInput = React.memo(BaseTextInput); +export type TextInputRef = RNTextInput; -export const TextInputWithIcon = React.memo( - BaseTextInput.styleable<{ icon: IconType }>(({ icon, ...props }, ref) => { +const TextInputComponent = RawTextInput.styleable<{ + icon?: IconType; + accent?: Accent; + backgroundType?: 'primary' | 'secondary'; + rightControls?: ReactNode; +}>( + ({ icon, accent, backgroundType, ...props }, ref) => { + const fieldContext = useContext(FieldContext); return ( - - - - + {icon ? : null} + + {props.rightControls} + ); - }) + }, + { staticConfig: { memo: true } } ); -interface TextInputWithButtonProps extends ComponentProps { - buttonText: string; - onButtonPress: () => void; -} - -const TextInputWithButtonFrame = styled(XStack, { - context: FieldContext, - borderWidth: 1, - borderColor: '$border', - borderRadius: '$l', - backgroundColor: '$background', - variants: { - accent: { - negative: { - backgroundColor: '$negativeBackground', - color: '$negativeActionText', - borderColor: '$negativeBorder', - }, - }, - } as const, -}); +const InnerButton = ({ + label: buttonText, + onPress: onPress, + ...props +}: { label: string; onPress?: () => void } & ComponentProps) => { + return ( + + + {buttonText} + + + ); +}; const TextInputButton = styled(Button, { context: FieldContext, backgroundColor: '$secondaryBackground', - padding: '$l', borderRadius: '$m', + height: '$3xl', + paddingHorizontal: '$l', variants: { accent: { negative: { backgroundColor: '$negativeBackground', - color: '$negativeActionText', borderColor: '$negativeBorder', }, + positive: { + backgroundColor: '$positiveBackground', + color: '$positiveActionText', + }, }, + backgroundType: getBackgroundTypeVariantStyle, } as const, }); -// Needs polish, I know we just talked about Ochre conformance plz forgive -export const TextInputWithButton: React.FC = - React.memo(function TextInputWithButtonRaw({ - buttonText, - onButtonPress, - ...textInputProps - }) { - return ( - - - - - {buttonText} - - - - ); - }); - export const ImageInput = XStack.styleable<{ buttonLabel?: string; value?: string; @@ -236,27 +254,12 @@ export const ImageInput = XStack.styleable<{ ); }); -const ImageInputButtonFrame = styled(XStack, { +const ImageInputButtonFrame = styled(InputFrame, { context: FieldContext, - borderRadius: '$l', - overflow: 'hidden', justifyContent: 'center', - borderWidth: 1, - borderColor: '$border', - backgroundColor: '$background', padding: '$xl', flex: 1, pressStyle: { backgroundColor: '$border' }, - variants: { - empty: {}, - accent: { - negative: { - backgroundColor: '$negativeBackground', - color: '$negativeActionText', - borderColor: '$negativeBorder', - }, - }, - }, }); const ImageInputButtonText = styled(Text, { @@ -268,7 +271,7 @@ const ImageInputButtonText = styled(Text, { }); const ImageInputPreviewFrame = styled(View, { - borderRadius: '$m', + borderRadius: '$l', aspectRatio: 1, overflow: 'hidden', backgroundColor: '$border', @@ -296,53 +299,6 @@ const ImageInputPreviewLoadingFrame = styled(View, { justifyContent: 'center', }); -interface TextInputWithIconAndButtonProps - extends ComponentProps { - icon: IconType; - buttonText: string; - onButtonPress: () => void; -} - -export const TextInputWithIconAndButton = React.memo( - React.forwardRef( - function TextInputWithIconAndButtonRaw( - { icon, buttonText, onButtonPress, ...textInputProps }, - ref - ) { - return ( - - - - - - ); - } - ) -); - -// Toggle group - export const ToggleGroupInput = ({ options, value, @@ -352,47 +308,60 @@ export const ToggleGroupInput = ({ value: string; onChange: (value: string) => void; }) => { + const { backgroundType } = useContext(FieldContext); + const [defaultColor, selectedColor] = + backgroundType === 'primary' + ? ['$background', '$secondaryBackground'] + : ['$secondaryBackground', '$background']; + return ( - - - {options.map((tab, index) => ( - - ))} - - + + + + {options.map((tab, index) => ( + + ))} + + + ); }; +const TextInputButtonText = styled(Text, { + size: '$label/m', +}); + +export const TextInput = withStaticProperties(TextInputComponent, { + InnerButton, +}); + // Shared control style between radio and checkbox -const ControlFrame = styled(View, { +const ControlFrame = styled(InputFrame, { width: '$3xl', height: '$3xl', borderWidth: 1, @@ -408,16 +377,14 @@ const ControlFrame = styled(View, { }, disabled: { true: { - backgroundColor: '$shadow', + backgroundColor: '$border', }, }, - variant: { - radio: { - borderRadius: 100, - }, - checkbox: { - borderRadius: '$s', - }, + radio: { + borderRadius: 150, + }, + checkbox: { + borderRadius: '$s', }, } as const, }); diff --git a/packages/ui/src/components/GlobalSearch/index.tsx b/packages/ui/src/components/GlobalSearch/index.tsx index 8f74e09975..61b38ab9f5 100644 --- a/packages/ui/src/components/GlobalSearch/index.tsx +++ b/packages/ui/src/components/GlobalSearch/index.tsx @@ -8,7 +8,8 @@ import { SectionListHeader, TabName, Text, - TextInputWithIconAndButton, + TextInput, + TextInputRef, View, XStack, YStack, @@ -21,7 +22,6 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { LayoutChangeEvent, NativeSyntheticEvent, - TextInput, TextInputKeyPressEventData, } from 'react-native'; import { getTokenValue } from 'tamagui'; @@ -40,7 +40,7 @@ export function GlobalSearch({ const { isOpen, setIsOpen } = useGlobalSearch(); const [searchQuery, setSearchQuery] = useState(''); const [selectedIndex, setSelectedIndex] = useState(0); - const inputRef = useRef(null); + const inputRef = useRef(null); const listRef = useRef>(null); const groupsQuery = store.useGroups({}); const contactsQuery = store.useContacts(); @@ -298,7 +298,7 @@ export function GlobalSearch({ maxWidth={600} gap="$l" > - setIsOpen(false)} - buttonText="Close" + rightControls={ + setIsOpen(false)} + /> + } spellCheck={false} autoCorrect={false} autoCapitalize="none" diff --git a/packages/ui/src/components/SearchBar.tsx b/packages/ui/src/components/SearchBar.tsx index 46d19ce37f..22802fadd4 100644 --- a/packages/ui/src/components/SearchBar.tsx +++ b/packages/ui/src/components/SearchBar.tsx @@ -1,8 +1,8 @@ import { debounce } from 'lodash'; import { ComponentProps, useCallback, useMemo, useState } from 'react'; -import { Circle, View, XStack } from 'tamagui'; +import { Circle, View } from 'tamagui'; -import { TextInput, TextInputWithIcon } from './Form'; +import { TextInput } from './Form'; import { Icon } from './Icon'; import { Input } from './Input'; import Pressable from './Pressable'; @@ -54,7 +54,7 @@ export function SearchBar({ alignItems="center" {...rest} > - ; export type FontStyle = keyof typeof trimAndroid; export type TextSize = keyof typeof typeStyles; diff --git a/packages/ui/src/tamagui.config.ts b/packages/ui/src/tamagui.config.ts index df6cfa73be..cdbf9711fc 100644 --- a/packages/ui/src/tamagui.config.ts +++ b/packages/ui/src/tamagui.config.ts @@ -100,7 +100,7 @@ export const themes = { shadow: 'rgba(255, 255, 255, 0.08)', tertiaryText: '#808080', border: '#333333', - secondaryBorder: '#4C4C4C', + secondaryBorder: '#3e3b3b', activeBorder: '#4C4C4C', positiveActionText: '#4E91F5', positiveBackground: '#143A5E', @@ -124,7 +124,7 @@ export const themes = { shadow: 'rgba(24, 24, 24, 0.08)', tertiaryText: '#999999', border: '#E5E5E5', - secondaryBorder: '#CCCCCC', + secondaryBorder: '#e3e3e3', activeBorder: '#CCCCCC', positiveActionText: '#3B80E8', positiveBackground: '#F5FAFF', diff --git a/packages/ui/src/types.ts b/packages/ui/src/types.ts index f0f0df330f..012be0ecf6 100644 --- a/packages/ui/src/types.ts +++ b/packages/ui/src/types.ts @@ -1,4 +1,27 @@ +import { Context, ContextType } from 'react'; import type { OpaqueColorValue } from 'react-native'; -import type { ColorTokens } from 'tamagui'; +import type { ColorTokens, ViewStyle } from 'tamagui'; export type ColorProp = OpaqueColorValue | ColorTokens; + +export type VariantsFromStyledContext< + TContext extends Context, + TStyle = ViewStyle, +> = + ContextType extends Record + ? { + [K in keyof ContextType]-?: Record< + Exclude[K], undefined>, + TStyle + >; + } + : never; + +export type VariantsFromValues< + TValues extends Record, + TStyle = ViewStyle, +> = { + [K in keyof TValues]: TValues[K] extends string + ? Partial> + : never; +};