main
parent
2499fba82c
commit
c7f1a03688
@ -1 +1 @@ |
|||||||
legacy-peer-deps=true |
legacy-peer-deps=true |
||||||
|
@ -0,0 +1,19 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { View, ViewProps } from 'react-native'; |
||||||
|
|
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { boxStyle } from './styles'; |
||||||
|
|
||||||
|
type IBoxProps = ViewProps & |
||||||
|
VariantProps<typeof boxStyle> & { className?: string }; |
||||||
|
|
||||||
|
const Box = React.forwardRef<React.ComponentRef<typeof View>, IBoxProps>( |
||||||
|
function Box({ className, ...props }, ref) { |
||||||
|
return ( |
||||||
|
<View ref={ref} {...props} className={boxStyle({ class: className })} /> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
Box.displayName = 'Box'; |
||||||
|
export { Box }; |
@ -0,0 +1,19 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { boxStyle } from './styles'; |
||||||
|
|
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
|
||||||
|
type IBoxProps = React.ComponentPropsWithoutRef<'div'> & |
||||||
|
VariantProps<typeof boxStyle> & { className?: string }; |
||||||
|
|
||||||
|
const Box = React.forwardRef<HTMLDivElement, IBoxProps>(function Box( |
||||||
|
{ className, ...props }, |
||||||
|
ref |
||||||
|
) { |
||||||
|
return ( |
||||||
|
<div ref={ref} className={boxStyle({ class: className })} {...props} /> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
Box.displayName = 'Box'; |
||||||
|
export { Box }; |
@ -0,0 +1,10 @@ |
|||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { isWeb } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
|
||||||
|
const baseStyle = isWeb |
||||||
|
? 'flex flex-col relative z-0 box-border border-0 list-none min-w-0 min-h-0 bg-transparent items-stretch m-0 p-0 text-decoration-none' |
||||||
|
: ''; |
||||||
|
|
||||||
|
export const boxStyle = tva({ |
||||||
|
base: baseStyle, |
||||||
|
}); |
@ -0,0 +1,439 @@ |
|||||||
|
'use client'; |
||||||
|
import React from 'react'; |
||||||
|
import { createButton } from '@gluestack-ui/core/button/creator'; |
||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { |
||||||
|
withStyleContext, |
||||||
|
useStyleContext, |
||||||
|
} from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { cssInterop } from 'nativewind'; |
||||||
|
import { ActivityIndicator, Pressable, Text, View } from 'react-native'; |
||||||
|
import type { VariantProps } from 'tailwind-variants'; |
||||||
|
import { PrimitiveIcon, UIIcon } from '@gluestack-ui/core/icon/creator'; |
||||||
|
|
||||||
|
const SCOPE = 'BUTTON'; |
||||||
|
|
||||||
|
const Root = withStyleContext(Pressable, SCOPE); |
||||||
|
|
||||||
|
const UIButton = createButton({ |
||||||
|
Root: Root, |
||||||
|
Text, |
||||||
|
Group: View, |
||||||
|
Spinner: ActivityIndicator, |
||||||
|
Icon: UIIcon, |
||||||
|
}); |
||||||
|
|
||||||
|
cssInterop(PrimitiveIcon, { |
||||||
|
className: { |
||||||
|
target: 'style', |
||||||
|
nativeStyleToProp: { |
||||||
|
height: true, |
||||||
|
width: true, |
||||||
|
fill: true, |
||||||
|
color: 'classNameColor', |
||||||
|
stroke: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const buttonStyle = tva({ |
||||||
|
base: 'group/button rounded bg-primary-500 flex-row items-center justify-center data-[focus-visible=true]:web:outline-none data-[focus-visible=true]:web:ring-2 data-[disabled=true]:opacity-40 gap-2', |
||||||
|
variants: { |
||||||
|
action: { |
||||||
|
primary: |
||||||
|
'bg-primary-500 data-[hover=true]:bg-primary-600 data-[active=true]:bg-primary-700 border-primary-300 data-[hover=true]:border-primary-400 data-[active=true]:border-primary-500 data-[focus-visible=true]:web:ring-indicator-info', |
||||||
|
secondary: |
||||||
|
'bg-secondary-500 border-secondary-300 data-[hover=true]:bg-secondary-600 data-[hover=true]:border-secondary-400 data-[active=true]:bg-secondary-700 data-[active=true]:border-secondary-700 data-[focus-visible=true]:web:ring-indicator-info', |
||||||
|
positive: |
||||||
|
'bg-success-500 border-success-300 data-[hover=true]:bg-success-600 data-[hover=true]:border-success-400 data-[active=true]:bg-success-700 data-[active=true]:border-success-500 data-[focus-visible=true]:web:ring-indicator-info', |
||||||
|
negative: |
||||||
|
'bg-error-500 border-error-300 data-[hover=true]:bg-error-600 data-[hover=true]:border-error-400 data-[active=true]:bg-error-700 data-[active=true]:border-error-500 data-[focus-visible=true]:web:ring-indicator-info', |
||||||
|
default: |
||||||
|
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', |
||||||
|
}, |
||||||
|
variant: { |
||||||
|
link: 'px-0', |
||||||
|
outline: |
||||||
|
'bg-transparent border data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', |
||||||
|
solid: '', |
||||||
|
}, |
||||||
|
|
||||||
|
size: { |
||||||
|
xs: 'px-3.5 h-8', |
||||||
|
sm: 'px-4 h-9', |
||||||
|
md: 'px-5 h-10', |
||||||
|
lg: 'px-6 h-11', |
||||||
|
xl: 'px-7 h-12', |
||||||
|
}, |
||||||
|
}, |
||||||
|
compoundVariants: [ |
||||||
|
{ |
||||||
|
action: 'primary', |
||||||
|
variant: 'link', |
||||||
|
class: |
||||||
|
'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent', |
||||||
|
}, |
||||||
|
{ |
||||||
|
action: 'secondary', |
||||||
|
variant: 'link', |
||||||
|
class: |
||||||
|
'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent', |
||||||
|
}, |
||||||
|
{ |
||||||
|
action: 'positive', |
||||||
|
variant: 'link', |
||||||
|
class: |
||||||
|
'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent', |
||||||
|
}, |
||||||
|
{ |
||||||
|
action: 'negative', |
||||||
|
variant: 'link', |
||||||
|
class: |
||||||
|
'px-0 bg-transparent data-[hover=true]:bg-transparent data-[active=true]:bg-transparent', |
||||||
|
}, |
||||||
|
{ |
||||||
|
action: 'primary', |
||||||
|
variant: 'outline', |
||||||
|
class: |
||||||
|
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', |
||||||
|
}, |
||||||
|
{ |
||||||
|
action: 'secondary', |
||||||
|
variant: 'outline', |
||||||
|
class: |
||||||
|
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', |
||||||
|
}, |
||||||
|
{ |
||||||
|
action: 'positive', |
||||||
|
variant: 'outline', |
||||||
|
class: |
||||||
|
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', |
||||||
|
}, |
||||||
|
{ |
||||||
|
action: 'negative', |
||||||
|
variant: 'outline', |
||||||
|
class: |
||||||
|
'bg-transparent data-[hover=true]:bg-background-50 data-[active=true]:bg-transparent', |
||||||
|
}, |
||||||
|
], |
||||||
|
}); |
||||||
|
|
||||||
|
const buttonTextStyle = tva({ |
||||||
|
base: 'text-typography-0 font-semibold web:select-none', |
||||||
|
parentVariants: { |
||||||
|
action: { |
||||||
|
primary: |
||||||
|
'text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700', |
||||||
|
secondary: |
||||||
|
'text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700', |
||||||
|
positive: |
||||||
|
'text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700', |
||||||
|
negative: |
||||||
|
'text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700', |
||||||
|
}, |
||||||
|
variant: { |
||||||
|
link: 'data-[hover=true]:underline data-[active=true]:underline', |
||||||
|
outline: '', |
||||||
|
solid: |
||||||
|
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', |
||||||
|
}, |
||||||
|
size: { |
||||||
|
xs: 'text-xs', |
||||||
|
sm: 'text-sm', |
||||||
|
md: 'text-base', |
||||||
|
lg: 'text-lg', |
||||||
|
xl: 'text-xl', |
||||||
|
}, |
||||||
|
}, |
||||||
|
parentCompoundVariants: [ |
||||||
|
{ |
||||||
|
variant: 'solid', |
||||||
|
action: 'primary', |
||||||
|
class: |
||||||
|
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'solid', |
||||||
|
action: 'secondary', |
||||||
|
class: |
||||||
|
'text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'solid', |
||||||
|
action: 'positive', |
||||||
|
class: |
||||||
|
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'solid', |
||||||
|
action: 'negative', |
||||||
|
class: |
||||||
|
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'outline', |
||||||
|
action: 'primary', |
||||||
|
class: |
||||||
|
'text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'outline', |
||||||
|
action: 'secondary', |
||||||
|
class: |
||||||
|
'text-typography-500 data-[hover=true]:text-primary-600 data-[active=true]:text-typography-700', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'outline', |
||||||
|
action: 'positive', |
||||||
|
class: |
||||||
|
'text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'outline', |
||||||
|
action: 'negative', |
||||||
|
class: |
||||||
|
'text-primary-500 data-[hover=true]:text-primary-500 data-[active=true]:text-primary-500', |
||||||
|
}, |
||||||
|
], |
||||||
|
}); |
||||||
|
|
||||||
|
const buttonIconStyle = tva({ |
||||||
|
base: 'fill-none', |
||||||
|
parentVariants: { |
||||||
|
variant: { |
||||||
|
link: 'data-[hover=true]:underline data-[active=true]:underline', |
||||||
|
outline: '', |
||||||
|
solid: |
||||||
|
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', |
||||||
|
}, |
||||||
|
size: { |
||||||
|
xs: 'h-3.5 w-3.5', |
||||||
|
sm: 'h-4 w-4', |
||||||
|
md: 'h-[18px] w-[18px]', |
||||||
|
lg: 'h-[18px] w-[18px]', |
||||||
|
xl: 'h-5 w-5', |
||||||
|
}, |
||||||
|
action: { |
||||||
|
primary: |
||||||
|
'text-primary-600 data-[hover=true]:text-primary-600 data-[active=true]:text-primary-700', |
||||||
|
secondary: |
||||||
|
'text-typography-500 data-[hover=true]:text-typography-600 data-[active=true]:text-typography-700', |
||||||
|
positive: |
||||||
|
'text-success-600 data-[hover=true]:text-success-600 data-[active=true]:text-success-700', |
||||||
|
|
||||||
|
negative: |
||||||
|
'text-error-600 data-[hover=true]:text-error-600 data-[active=true]:text-error-700', |
||||||
|
}, |
||||||
|
}, |
||||||
|
parentCompoundVariants: [ |
||||||
|
{ |
||||||
|
variant: 'solid', |
||||||
|
action: 'primary', |
||||||
|
class: |
||||||
|
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'solid', |
||||||
|
action: 'secondary', |
||||||
|
class: |
||||||
|
'text-typography-800 data-[hover=true]:text-typography-800 data-[active=true]:text-typography-800', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'solid', |
||||||
|
action: 'positive', |
||||||
|
class: |
||||||
|
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'solid', |
||||||
|
action: 'negative', |
||||||
|
class: |
||||||
|
'text-typography-0 data-[hover=true]:text-typography-0 data-[active=true]:text-typography-0', |
||||||
|
}, |
||||||
|
], |
||||||
|
}); |
||||||
|
|
||||||
|
const buttonGroupStyle = tva({ |
||||||
|
base: '', |
||||||
|
variants: { |
||||||
|
space: { |
||||||
|
'xs': 'gap-1', |
||||||
|
'sm': 'gap-2', |
||||||
|
'md': 'gap-3', |
||||||
|
'lg': 'gap-4', |
||||||
|
'xl': 'gap-5', |
||||||
|
'2xl': 'gap-6', |
||||||
|
'3xl': 'gap-7', |
||||||
|
'4xl': 'gap-8', |
||||||
|
}, |
||||||
|
isAttached: { |
||||||
|
true: 'gap-0', |
||||||
|
}, |
||||||
|
flexDirection: { |
||||||
|
'row': 'flex-row', |
||||||
|
'column': 'flex-col', |
||||||
|
'row-reverse': 'flex-row-reverse', |
||||||
|
'column-reverse': 'flex-col-reverse', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
type IButtonProps = Omit< |
||||||
|
React.ComponentPropsWithoutRef<typeof UIButton>, |
||||||
|
'context' |
||||||
|
> & |
||||||
|
VariantProps<typeof buttonStyle> & { className?: string }; |
||||||
|
|
||||||
|
const Button = React.forwardRef< |
||||||
|
React.ElementRef<typeof UIButton>, |
||||||
|
IButtonProps |
||||||
|
>( |
||||||
|
( |
||||||
|
{ className, variant = 'solid', size = 'md', action = 'primary', ...props }, |
||||||
|
ref |
||||||
|
) => { |
||||||
|
return ( |
||||||
|
<UIButton |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={buttonStyle({ variant, size, action, class: className })} |
||||||
|
context={{ variant, size, action }} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
type IButtonTextProps = React.ComponentPropsWithoutRef<typeof UIButton.Text> & |
||||||
|
VariantProps<typeof buttonTextStyle> & { className?: string }; |
||||||
|
|
||||||
|
const ButtonText = React.forwardRef< |
||||||
|
React.ElementRef<typeof UIButton.Text>, |
||||||
|
IButtonTextProps |
||||||
|
>(({ className, variant, size, action, ...props }, ref) => { |
||||||
|
const { |
||||||
|
variant: parentVariant, |
||||||
|
size: parentSize, |
||||||
|
action: parentAction, |
||||||
|
} = useStyleContext(SCOPE); |
||||||
|
|
||||||
|
return ( |
||||||
|
<UIButton.Text |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={buttonTextStyle({ |
||||||
|
parentVariants: { |
||||||
|
variant: parentVariant, |
||||||
|
size: parentSize, |
||||||
|
action: parentAction, |
||||||
|
}, |
||||||
|
variant: variant as 'link' | 'outline' | 'solid' | undefined, |
||||||
|
size, |
||||||
|
action: action as |
||||||
|
| 'primary' |
||||||
|
| 'secondary' |
||||||
|
| 'positive' |
||||||
|
| 'negative' |
||||||
|
| undefined, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
const ButtonSpinner = UIButton.Spinner; |
||||||
|
|
||||||
|
type IButtonIcon = React.ComponentPropsWithoutRef<typeof UIButton.Icon> & |
||||||
|
VariantProps<typeof buttonIconStyle> & { |
||||||
|
className?: string | undefined; |
||||||
|
as?: React.ElementType; |
||||||
|
height?: number; |
||||||
|
width?: number; |
||||||
|
}; |
||||||
|
|
||||||
|
const ButtonIcon = React.forwardRef< |
||||||
|
React.ElementRef<typeof UIButton.Icon>, |
||||||
|
IButtonIcon |
||||||
|
>(({ className, size, ...props }, ref) => { |
||||||
|
const { |
||||||
|
variant: parentVariant, |
||||||
|
size: parentSize, |
||||||
|
action: parentAction, |
||||||
|
} = useStyleContext(SCOPE); |
||||||
|
|
||||||
|
if (typeof size === 'number') { |
||||||
|
return ( |
||||||
|
<UIButton.Icon |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={buttonIconStyle({ class: className })} |
||||||
|
size={size} |
||||||
|
/> |
||||||
|
); |
||||||
|
} else if ( |
||||||
|
(props.height !== undefined || props.width !== undefined) && |
||||||
|
size === undefined |
||||||
|
) { |
||||||
|
return ( |
||||||
|
<UIButton.Icon |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={buttonIconStyle({ class: className })} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
return ( |
||||||
|
<UIButton.Icon |
||||||
|
{...props} |
||||||
|
className={buttonIconStyle({ |
||||||
|
parentVariants: { |
||||||
|
size: parentSize, |
||||||
|
variant: parentVariant, |
||||||
|
action: parentAction, |
||||||
|
}, |
||||||
|
size, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
ref={ref} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
type IButtonGroupProps = React.ComponentPropsWithoutRef<typeof UIButton.Group> & |
||||||
|
VariantProps<typeof buttonGroupStyle>; |
||||||
|
|
||||||
|
const ButtonGroup = React.forwardRef< |
||||||
|
React.ElementRef<typeof UIButton.Group>, |
||||||
|
IButtonGroupProps |
||||||
|
>( |
||||||
|
( |
||||||
|
{ |
||||||
|
className, |
||||||
|
space = 'md', |
||||||
|
isAttached = false, |
||||||
|
flexDirection = 'column', |
||||||
|
...props |
||||||
|
}, |
||||||
|
ref |
||||||
|
) => { |
||||||
|
return ( |
||||||
|
<UIButton.Group |
||||||
|
className={buttonGroupStyle({ |
||||||
|
class: className, |
||||||
|
space, |
||||||
|
isAttached: isAttached as boolean, |
||||||
|
flexDirection: flexDirection as any, |
||||||
|
})} |
||||||
|
{...props} |
||||||
|
ref={ref} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
Button.displayName = 'Button'; |
||||||
|
ButtonText.displayName = 'ButtonText'; |
||||||
|
ButtonSpinner.displayName = 'ButtonSpinner'; |
||||||
|
ButtonIcon.displayName = 'ButtonIcon'; |
||||||
|
ButtonGroup.displayName = 'ButtonGroup'; |
||||||
|
|
||||||
|
export { Button, ButtonText, ButtonSpinner, ButtonIcon, ButtonGroup }; |
@ -0,0 +1,27 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { View } from 'react-native'; |
||||||
|
import type { ViewProps } from 'react-native'; |
||||||
|
import { hstackStyle } from './styles'; |
||||||
|
|
||||||
|
type IHStackProps = ViewProps & VariantProps<typeof hstackStyle>; |
||||||
|
|
||||||
|
const HStack = React.forwardRef<React.ComponentRef<typeof View>, IHStackProps>( |
||||||
|
function HStack({ className, space, reversed, ...props }, ref) { |
||||||
|
return ( |
||||||
|
<View |
||||||
|
className={hstackStyle({ |
||||||
|
space, |
||||||
|
reversed: reversed as boolean, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
{...props} |
||||||
|
ref={ref} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
HStack.displayName = 'HStack'; |
||||||
|
|
||||||
|
export { HStack }; |
@ -0,0 +1,26 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { hstackStyle } from './styles'; |
||||||
|
|
||||||
|
type IHStackProps = React.ComponentPropsWithoutRef<'div'> & |
||||||
|
VariantProps<typeof hstackStyle>; |
||||||
|
|
||||||
|
const HStack = React.forwardRef<React.ComponentRef<'div'>, IHStackProps>( |
||||||
|
function HStack({ className, space, reversed, ...props }, ref) { |
||||||
|
return ( |
||||||
|
<div |
||||||
|
className={hstackStyle({ |
||||||
|
space, |
||||||
|
reversed: reversed as boolean, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
{...props} |
||||||
|
ref={ref} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
HStack.displayName = 'HStack'; |
||||||
|
|
||||||
|
export { HStack }; |
@ -0,0 +1,25 @@ |
|||||||
|
import { isWeb } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
|
||||||
|
const baseStyle = isWeb |
||||||
|
? 'flex relative z-0 box-border border-0 list-none min-w-0 min-h-0 bg-transparent items-stretch m-0 p-0 text-decoration-none' |
||||||
|
: ''; |
||||||
|
|
||||||
|
export const hstackStyle = tva({ |
||||||
|
base: `flex-row ${baseStyle}`, |
||||||
|
variants: { |
||||||
|
space: { |
||||||
|
'xs': 'gap-1', |
||||||
|
'sm': 'gap-2', |
||||||
|
'md': 'gap-3', |
||||||
|
'lg': 'gap-4', |
||||||
|
'xl': 'gap-5', |
||||||
|
'2xl': 'gap-6', |
||||||
|
'3xl': 'gap-7', |
||||||
|
'4xl': 'gap-8', |
||||||
|
}, |
||||||
|
reversed: { |
||||||
|
true: 'flex-row-reverse', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
@ -0,0 +1,48 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { createImage } from '@gluestack-ui/core/image/creator'; |
||||||
|
import { Platform, Image as RNImage } from 'react-native'; |
||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
|
||||||
|
const imageStyle = tva({ |
||||||
|
base: 'max-w-full', |
||||||
|
variants: { |
||||||
|
size: { |
||||||
|
'2xs': 'h-6 w-6', |
||||||
|
'xs': 'h-10 w-10', |
||||||
|
'sm': 'h-16 w-16', |
||||||
|
'md': 'h-20 w-20', |
||||||
|
'lg': 'h-24 w-24', |
||||||
|
'xl': 'h-32 w-32', |
||||||
|
'2xl': 'h-64 w-64', |
||||||
|
'full': 'h-full w-full', |
||||||
|
'none': '', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const UIImage = createImage({ Root: RNImage }); |
||||||
|
|
||||||
|
type ImageProps = VariantProps<typeof imageStyle> & |
||||||
|
React.ComponentProps<typeof UIImage>; |
||||||
|
const Image = React.forwardRef< |
||||||
|
React.ComponentRef<typeof UIImage>, |
||||||
|
ImageProps & { className?: string } |
||||||
|
>(function Image({ size = 'md', className, ...props }, ref) { |
||||||
|
return ( |
||||||
|
<UIImage |
||||||
|
className={imageStyle({ size, class: className })} |
||||||
|
{...props} |
||||||
|
ref={ref} |
||||||
|
// @ts-expect-error : web only
|
||||||
|
style={ |
||||||
|
Platform.OS === 'web' |
||||||
|
? { height: 'revert-layer', width: 'revert-layer' } |
||||||
|
: undefined |
||||||
|
} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
Image.displayName = 'Image'; |
||||||
|
export { Image }; |
@ -0,0 +1,217 @@ |
|||||||
|
'use client'; |
||||||
|
import React from 'react'; |
||||||
|
import { createInput } from '@gluestack-ui/core/input/creator'; |
||||||
|
import { View, Pressable, TextInput } from 'react-native'; |
||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { |
||||||
|
withStyleContext, |
||||||
|
useStyleContext, |
||||||
|
} from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { cssInterop } from 'nativewind'; |
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { PrimitiveIcon, UIIcon } from '@gluestack-ui/core/icon/creator'; |
||||||
|
|
||||||
|
const SCOPE = 'INPUT'; |
||||||
|
|
||||||
|
const UIInput = createInput({ |
||||||
|
Root: withStyleContext(View, SCOPE), |
||||||
|
Icon: UIIcon, |
||||||
|
Slot: Pressable, |
||||||
|
Input: TextInput, |
||||||
|
}); |
||||||
|
|
||||||
|
cssInterop(PrimitiveIcon, { |
||||||
|
className: { |
||||||
|
target: 'style', |
||||||
|
nativeStyleToProp: { |
||||||
|
height: true, |
||||||
|
width: true, |
||||||
|
fill: true, |
||||||
|
color: 'classNameColor', |
||||||
|
stroke: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const inputStyle = tva({ |
||||||
|
base: 'border-background-300 flex-row overflow-hidden content-center data-[hover=true]:border-outline-400 data-[focus=true]:border-primary-700 data-[focus=true]:hover:border-primary-700 data-[disabled=true]:opacity-40 data-[disabled=true]:hover:border-background-300 items-center', |
||||||
|
|
||||||
|
variants: { |
||||||
|
size: { |
||||||
|
xl: 'h-12', |
||||||
|
lg: 'h-11', |
||||||
|
md: 'h-10', |
||||||
|
sm: 'h-9', |
||||||
|
}, |
||||||
|
|
||||||
|
variant: { |
||||||
|
underlined: |
||||||
|
'rounded-none border-b data-[invalid=true]:border-b-2 data-[invalid=true]:border-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-error-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700', |
||||||
|
|
||||||
|
outline: |
||||||
|
'rounded border data-[invalid=true]:border-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-error-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700 data-[focus=true]:web:ring-1 data-[focus=true]:web:ring-inset data-[focus=true]:web:ring-indicator-primary data-[invalid=true]:web:ring-1 data-[invalid=true]:web:ring-inset data-[invalid=true]:web:ring-indicator-error data-[invalid=true]:data-[focus=true]:hover:web:ring-1 data-[invalid=true]:data-[focus=true]:hover:web:ring-inset data-[invalid=true]:data-[focus=true]:hover:web:ring-indicator-error data-[invalid=true]:data-[disabled=true]:hover:web:ring-1 data-[invalid=true]:data-[disabled=true]:hover:web:ring-inset data-[invalid=true]:data-[disabled=true]:hover:web:ring-indicator-error', |
||||||
|
|
||||||
|
rounded: |
||||||
|
'rounded-full border data-[invalid=true]:border-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-error-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700 data-[focus=true]:web:ring-1 data-[focus=true]:web:ring-inset data-[focus=true]:web:ring-indicator-primary data-[invalid=true]:web:ring-1 data-[invalid=true]:web:ring-inset data-[invalid=true]:web:ring-indicator-error data-[invalid=true]:data-[focus=true]:hover:web:ring-1 data-[invalid=true]:data-[focus=true]:hover:web:ring-inset data-[invalid=true]:data-[focus=true]:hover:web:ring-indicator-error data-[invalid=true]:data-[disabled=true]:hover:web:ring-1 data-[invalid=true]:data-[disabled=true]:hover:web:ring-inset data-[invalid=true]:data-[disabled=true]:hover:web:ring-indicator-error', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const inputIconStyle = tva({ |
||||||
|
base: 'justify-center items-center text-typography-400 fill-none', |
||||||
|
parentVariants: { |
||||||
|
size: { |
||||||
|
'2xs': 'h-3 w-3', |
||||||
|
'xs': 'h-3.5 w-3.5', |
||||||
|
'sm': 'h-4 w-4', |
||||||
|
'md': 'h-[18px] w-[18px]', |
||||||
|
'lg': 'h-5 w-5', |
||||||
|
'xl': 'h-6 w-6', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const inputSlotStyle = tva({ |
||||||
|
base: 'justify-center items-center web:disabled:cursor-not-allowed', |
||||||
|
}); |
||||||
|
|
||||||
|
const inputFieldStyle = tva({ |
||||||
|
base: 'flex-1 text-typography-900 py-0 px-3 placeholder:text-typography-500 h-full ios:leading-[0px] web:cursor-text web:data-[disabled=true]:cursor-not-allowed', |
||||||
|
|
||||||
|
parentVariants: { |
||||||
|
variant: { |
||||||
|
underlined: 'web:outline-0 web:outline-none px-0', |
||||||
|
outline: 'web:outline-0 web:outline-none', |
||||||
|
rounded: 'web:outline-0 web:outline-none px-4', |
||||||
|
}, |
||||||
|
|
||||||
|
size: { |
||||||
|
'2xs': 'text-2xs', |
||||||
|
'xs': 'text-xs', |
||||||
|
'sm': 'text-sm', |
||||||
|
'md': 'text-base', |
||||||
|
'lg': 'text-lg', |
||||||
|
'xl': 'text-xl', |
||||||
|
'2xl': 'text-2xl', |
||||||
|
'3xl': 'text-3xl', |
||||||
|
'4xl': 'text-4xl', |
||||||
|
'5xl': 'text-5xl', |
||||||
|
'6xl': 'text-6xl', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
type IInputProps = React.ComponentProps<typeof UIInput> & |
||||||
|
VariantProps<typeof inputStyle> & { className?: string }; |
||||||
|
const Input = React.forwardRef<React.ComponentRef<typeof UIInput>, IInputProps>( |
||||||
|
function Input( |
||||||
|
{ className, variant = 'outline', size = 'md', ...props }, |
||||||
|
ref |
||||||
|
) { |
||||||
|
return ( |
||||||
|
<UIInput |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={inputStyle({ variant, size, class: className })} |
||||||
|
context={{ variant, size }} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
type IInputIconProps = React.ComponentProps<typeof UIInput.Icon> & |
||||||
|
VariantProps<typeof inputIconStyle> & { |
||||||
|
className?: string; |
||||||
|
height?: number; |
||||||
|
width?: number; |
||||||
|
}; |
||||||
|
|
||||||
|
const InputIcon = React.forwardRef< |
||||||
|
React.ComponentRef<typeof UIInput.Icon>, |
||||||
|
IInputIconProps |
||||||
|
>(function InputIcon({ className, size, ...props }, ref) { |
||||||
|
const { size: parentSize } = useStyleContext(SCOPE); |
||||||
|
|
||||||
|
if (typeof size === 'number') { |
||||||
|
return ( |
||||||
|
<UIInput.Icon |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={inputIconStyle({ class: className })} |
||||||
|
size={size} |
||||||
|
/> |
||||||
|
); |
||||||
|
} else if ( |
||||||
|
(props.height !== undefined || props.width !== undefined) && |
||||||
|
size === undefined |
||||||
|
) { |
||||||
|
return ( |
||||||
|
<UIInput.Icon |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={inputIconStyle({ class: className })} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
return ( |
||||||
|
<UIInput.Icon |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={inputIconStyle({ |
||||||
|
parentVariants: { |
||||||
|
size: parentSize, |
||||||
|
}, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
type IInputSlotProps = React.ComponentProps<typeof UIInput.Slot> & |
||||||
|
VariantProps<typeof inputSlotStyle> & { className?: string }; |
||||||
|
|
||||||
|
const InputSlot = React.forwardRef< |
||||||
|
React.ComponentRef<typeof UIInput.Slot>, |
||||||
|
IInputSlotProps |
||||||
|
>(function InputSlot({ className, ...props }, ref) { |
||||||
|
return ( |
||||||
|
<UIInput.Slot |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={inputSlotStyle({ |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
type IInputFieldProps = React.ComponentProps<typeof UIInput.Input> & |
||||||
|
VariantProps<typeof inputFieldStyle> & { className?: string }; |
||||||
|
|
||||||
|
const InputField = React.forwardRef< |
||||||
|
React.ComponentRef<typeof UIInput.Input>, |
||||||
|
IInputFieldProps |
||||||
|
>(function InputField({ className, ...props }, ref) { |
||||||
|
const { variant: parentVariant, size: parentSize } = useStyleContext(SCOPE); |
||||||
|
|
||||||
|
return ( |
||||||
|
<UIInput.Input |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={inputFieldStyle({ |
||||||
|
parentVariants: { |
||||||
|
variant: parentVariant, |
||||||
|
size: parentSize, |
||||||
|
}, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
Input.displayName = 'Input'; |
||||||
|
InputIcon.displayName = 'InputIcon'; |
||||||
|
InputSlot.displayName = 'InputSlot'; |
||||||
|
InputField.displayName = 'InputField'; |
||||||
|
|
||||||
|
export { Input, InputField, InputIcon, InputSlot }; |
@ -0,0 +1,40 @@ |
|||||||
|
'use client'; |
||||||
|
import { ActivityIndicator } from 'react-native'; |
||||||
|
import React from 'react'; |
||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { cssInterop } from 'nativewind'; |
||||||
|
|
||||||
|
cssInterop(ActivityIndicator, { |
||||||
|
className: { target: 'style', nativeStyleToProp: { color: true } }, |
||||||
|
}); |
||||||
|
|
||||||
|
const spinnerStyle = tva({}); |
||||||
|
|
||||||
|
const Spinner = React.forwardRef< |
||||||
|
React.ComponentRef<typeof ActivityIndicator>, |
||||||
|
React.ComponentProps<typeof ActivityIndicator> |
||||||
|
>(function Spinner( |
||||||
|
{ |
||||||
|
className, |
||||||
|
color, |
||||||
|
focusable = false, |
||||||
|
'aria-label': ariaLabel = 'loading', |
||||||
|
...props |
||||||
|
}, |
||||||
|
ref |
||||||
|
) { |
||||||
|
return ( |
||||||
|
<ActivityIndicator |
||||||
|
ref={ref} |
||||||
|
focusable={focusable} |
||||||
|
aria-label={ariaLabel} |
||||||
|
{...props} |
||||||
|
color={color} |
||||||
|
className={spinnerStyle({ class: className })} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
Spinner.displayName = 'Spinner'; |
||||||
|
|
||||||
|
export { Spinner }; |
@ -0,0 +1,48 @@ |
|||||||
|
import React from 'react'; |
||||||
|
|
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { Text as RNText } from 'react-native'; |
||||||
|
import { textStyle } from './styles'; |
||||||
|
|
||||||
|
type ITextProps = React.ComponentProps<typeof RNText> & |
||||||
|
VariantProps<typeof textStyle>; |
||||||
|
|
||||||
|
const Text = React.forwardRef<React.ComponentRef<typeof RNText>, ITextProps>( |
||||||
|
function Text( |
||||||
|
{ |
||||||
|
className, |
||||||
|
isTruncated, |
||||||
|
bold, |
||||||
|
underline, |
||||||
|
strikeThrough, |
||||||
|
size = 'md', |
||||||
|
sub, |
||||||
|
italic, |
||||||
|
highlight, |
||||||
|
...props |
||||||
|
}, |
||||||
|
ref |
||||||
|
) { |
||||||
|
return ( |
||||||
|
<RNText |
||||||
|
className={textStyle({ |
||||||
|
isTruncated: isTruncated as boolean, |
||||||
|
bold: bold as boolean, |
||||||
|
underline: underline as boolean, |
||||||
|
strikeThrough: strikeThrough as boolean, |
||||||
|
size, |
||||||
|
sub: sub as boolean, |
||||||
|
italic: italic as boolean, |
||||||
|
highlight: highlight as boolean, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
{...props} |
||||||
|
ref={ref} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
Text.displayName = 'Text'; |
||||||
|
|
||||||
|
export { Text }; |
@ -0,0 +1,45 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { textStyle } from './styles'; |
||||||
|
|
||||||
|
type ITextProps = React.ComponentProps<'span'> & VariantProps<typeof textStyle>; |
||||||
|
|
||||||
|
const Text = React.forwardRef<React.ComponentRef<'span'>, ITextProps>( |
||||||
|
function Text( |
||||||
|
{ |
||||||
|
className, |
||||||
|
isTruncated, |
||||||
|
bold, |
||||||
|
underline, |
||||||
|
strikeThrough, |
||||||
|
size = 'md', |
||||||
|
sub, |
||||||
|
italic, |
||||||
|
highlight, |
||||||
|
...props |
||||||
|
}: { className?: string } & ITextProps, |
||||||
|
ref |
||||||
|
) { |
||||||
|
return ( |
||||||
|
<span |
||||||
|
className={textStyle({ |
||||||
|
isTruncated: isTruncated as boolean, |
||||||
|
bold: bold as boolean, |
||||||
|
underline: underline as boolean, |
||||||
|
strikeThrough: strikeThrough as boolean, |
||||||
|
size, |
||||||
|
sub: sub as boolean, |
||||||
|
italic: italic as boolean, |
||||||
|
highlight: highlight as boolean, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
{...props} |
||||||
|
ref={ref} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
Text.displayName = 'Text'; |
||||||
|
|
||||||
|
export { Text }; |
@ -0,0 +1,47 @@ |
|||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { isWeb } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
|
||||||
|
const baseStyle = isWeb |
||||||
|
? 'font-sans tracking-sm my-0 bg-transparent border-0 box-border display-inline list-none margin-0 padding-0 position-relative text-start no-underline whitespace-pre-wrap word-wrap-break-word' |
||||||
|
: ''; |
||||||
|
|
||||||
|
export const textStyle = tva({ |
||||||
|
base: `text-typography-700 font-body ${baseStyle}`, |
||||||
|
|
||||||
|
variants: { |
||||||
|
isTruncated: { |
||||||
|
true: 'web:truncate', |
||||||
|
}, |
||||||
|
bold: { |
||||||
|
true: 'font-bold', |
||||||
|
}, |
||||||
|
underline: { |
||||||
|
true: 'underline', |
||||||
|
}, |
||||||
|
strikeThrough: { |
||||||
|
true: 'line-through', |
||||||
|
}, |
||||||
|
size: { |
||||||
|
'2xs': 'text-2xs', |
||||||
|
'xs': 'text-xs', |
||||||
|
'sm': 'text-sm', |
||||||
|
'md': 'text-base', |
||||||
|
'lg': 'text-lg', |
||||||
|
'xl': 'text-xl', |
||||||
|
'2xl': 'text-2xl', |
||||||
|
'3xl': 'text-3xl', |
||||||
|
'4xl': 'text-4xl', |
||||||
|
'5xl': 'text-5xl', |
||||||
|
'6xl': 'text-6xl', |
||||||
|
}, |
||||||
|
sub: { |
||||||
|
true: 'text-xs', |
||||||
|
}, |
||||||
|
italic: { |
||||||
|
true: 'italic', |
||||||
|
}, |
||||||
|
highlight: { |
||||||
|
true: 'bg-yellow-500', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
@ -0,0 +1,94 @@ |
|||||||
|
'use client'; |
||||||
|
import React from 'react'; |
||||||
|
import { createTextarea } from '@gluestack-ui/core/textarea/creator'; |
||||||
|
import { View, TextInput } from 'react-native'; |
||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { |
||||||
|
withStyleContext, |
||||||
|
useStyleContext, |
||||||
|
} from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
|
||||||
|
const SCOPE = 'TEXTAREA'; |
||||||
|
const UITextarea = createTextarea({ |
||||||
|
Root: withStyleContext(View, SCOPE), |
||||||
|
Input: TextInput, |
||||||
|
}); |
||||||
|
|
||||||
|
const textareaStyle = tva({ |
||||||
|
base: 'w-full h-[100px] border border-background-300 rounded data-[hover=true]:border-outline-400 data-[focus=true]:border-primary-700 data-[focus=true]:data-[hover=true]:border-primary-700 data-[disabled=true]:opacity-40 data-[disabled=true]:bg-background-50 data-[disabled=true]:data-[hover=true]:border-background-300', |
||||||
|
|
||||||
|
variants: { |
||||||
|
variant: { |
||||||
|
default: |
||||||
|
'data-[focus=true]:border-primary-700 data-[focus=true]:web:ring-1 data-[focus=true]:web:ring-inset data-[focus=true]:web:ring-indicator-primary data-[invalid=true]:border-error-700 data-[invalid=true]:web:ring-1 data-[invalid=true]:web:ring-inset data-[invalid=true]:web:ring-indicator-error data-[invalid=true]:data-[hover=true]:border-error-700 data-[invalid=true]:data-[focus=true]:data-[hover=true]:border-primary-700 data-[invalid=true]:data-[focus=true]:data-[hover=true]:web:ring-1 data-[invalid=true]:data-[focus=true]:data-[hover=true]:web:ring-inset data-[invalid=true]:data-[focus=true]:data-[hover=true]:web:ring-indicator-primary data-[invalid=true]:data-[disabled=true]:data-[hover=true]:border-error-700 data-[invalid=true]:data-[disabled=true]:data-[hover=true]:web:ring-1 data-[invalid=true]:data-[disabled=true]:data-[hover=true]:web:ring-inset data-[invalid=true]:data-[disabled=true]:data-[hover=true]:web:ring-indicator-error ', |
||||||
|
}, |
||||||
|
size: { |
||||||
|
sm: '', |
||||||
|
md: '', |
||||||
|
lg: '', |
||||||
|
xl: '', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const textareaInputStyle = tva({ |
||||||
|
base: 'p-2 web:outline-0 web:outline-none flex-1 color-typography-900 placeholder:text-typography-500 web:cursor-text web:data-[disabled=true]:cursor-not-allowed', |
||||||
|
parentVariants: { |
||||||
|
size: { |
||||||
|
sm: 'text-sm', |
||||||
|
md: 'text-base', |
||||||
|
lg: 'text-lg', |
||||||
|
xl: 'text-xl', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
type ITextareaProps = React.ComponentProps<typeof UITextarea> & |
||||||
|
VariantProps<typeof textareaStyle>; |
||||||
|
|
||||||
|
const Textarea = React.forwardRef< |
||||||
|
React.ComponentRef<typeof UITextarea>, |
||||||
|
ITextareaProps |
||||||
|
>(function Textarea( |
||||||
|
{ className, variant = 'default', size = 'md', ...props }, |
||||||
|
ref |
||||||
|
) { |
||||||
|
return ( |
||||||
|
<UITextarea |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={textareaStyle({ variant, class: className })} |
||||||
|
context={{ size }} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
type ITextareaInputProps = React.ComponentProps<typeof UITextarea.Input> & |
||||||
|
VariantProps<typeof textareaInputStyle>; |
||||||
|
|
||||||
|
const TextareaInput = React.forwardRef< |
||||||
|
React.ComponentRef<typeof UITextarea.Input>, |
||||||
|
ITextareaInputProps |
||||||
|
>(function TextareaInput({ className, ...props }, ref) { |
||||||
|
const { size: parentSize } = useStyleContext(SCOPE); |
||||||
|
|
||||||
|
return ( |
||||||
|
<UITextarea.Input |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
textAlignVertical="top" |
||||||
|
className={textareaInputStyle({ |
||||||
|
parentVariants: { |
||||||
|
size: parentSize, |
||||||
|
}, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
Textarea.displayName = 'Textarea'; |
||||||
|
TextareaInput.displayName = 'TextareaInput'; |
||||||
|
|
||||||
|
export { Textarea, TextareaInput }; |
@ -0,0 +1,240 @@ |
|||||||
|
'use client'; |
||||||
|
import React from 'react'; |
||||||
|
import { createToastHook } from '@gluestack-ui/core/toast/creator'; |
||||||
|
import { AccessibilityInfo, Text, View, ViewStyle } from 'react-native'; |
||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { cssInterop } from 'nativewind'; |
||||||
|
import { |
||||||
|
Motion, |
||||||
|
AnimatePresence, |
||||||
|
MotionComponentProps, |
||||||
|
} from '@legendapp/motion'; |
||||||
|
import { |
||||||
|
withStyleContext, |
||||||
|
useStyleContext, |
||||||
|
} from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
|
||||||
|
type IMotionViewProps = React.ComponentProps<typeof View> & |
||||||
|
MotionComponentProps<typeof View, ViewStyle, unknown, unknown, unknown>; |
||||||
|
|
||||||
|
const MotionView = Motion.View as React.ComponentType<IMotionViewProps>; |
||||||
|
|
||||||
|
const useToast = createToastHook(MotionView, AnimatePresence); |
||||||
|
const SCOPE = 'TOAST'; |
||||||
|
|
||||||
|
cssInterop(MotionView, { className: 'style' }); |
||||||
|
|
||||||
|
const toastStyle = tva({ |
||||||
|
base: 'p-4 m-1 rounded-md gap-1 web:pointer-events-auto shadow-hard-5 border-outline-100', |
||||||
|
variants: { |
||||||
|
action: { |
||||||
|
error: 'bg-error-800', |
||||||
|
warning: 'bg-warning-700', |
||||||
|
success: 'bg-success-700', |
||||||
|
info: 'bg-info-700', |
||||||
|
muted: 'bg-background-800', |
||||||
|
}, |
||||||
|
|
||||||
|
variant: { |
||||||
|
solid: '', |
||||||
|
outline: 'border bg-background-0', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const toastTitleStyle = tva({ |
||||||
|
base: 'text-typography-0 font-medium font-body tracking-md text-left', |
||||||
|
variants: { |
||||||
|
isTruncated: { |
||||||
|
true: '', |
||||||
|
}, |
||||||
|
bold: { |
||||||
|
true: 'font-bold', |
||||||
|
}, |
||||||
|
underline: { |
||||||
|
true: 'underline', |
||||||
|
}, |
||||||
|
strikeThrough: { |
||||||
|
true: 'line-through', |
||||||
|
}, |
||||||
|
size: { |
||||||
|
'2xs': 'text-2xs', |
||||||
|
'xs': 'text-xs', |
||||||
|
'sm': 'text-sm', |
||||||
|
'md': 'text-base', |
||||||
|
'lg': 'text-lg', |
||||||
|
'xl': 'text-xl', |
||||||
|
'2xl': 'text-2xl', |
||||||
|
'3xl': 'text-3xl', |
||||||
|
'4xl': 'text-4xl', |
||||||
|
'5xl': 'text-5xl', |
||||||
|
'6xl': 'text-6xl', |
||||||
|
}, |
||||||
|
}, |
||||||
|
parentVariants: { |
||||||
|
variant: { |
||||||
|
solid: '', |
||||||
|
outline: '', |
||||||
|
}, |
||||||
|
action: { |
||||||
|
error: '', |
||||||
|
warning: '', |
||||||
|
success: '', |
||||||
|
info: '', |
||||||
|
muted: '', |
||||||
|
}, |
||||||
|
}, |
||||||
|
parentCompoundVariants: [ |
||||||
|
{ |
||||||
|
variant: 'outline', |
||||||
|
action: 'error', |
||||||
|
class: 'text-error-800', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'outline', |
||||||
|
action: 'warning', |
||||||
|
class: 'text-warning-800', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'outline', |
||||||
|
action: 'success', |
||||||
|
class: 'text-success-800', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'outline', |
||||||
|
action: 'info', |
||||||
|
class: 'text-info-800', |
||||||
|
}, |
||||||
|
{ |
||||||
|
variant: 'outline', |
||||||
|
action: 'muted', |
||||||
|
class: 'text-background-800', |
||||||
|
}, |
||||||
|
], |
||||||
|
}); |
||||||
|
|
||||||
|
const toastDescriptionStyle = tva({ |
||||||
|
base: 'font-normal font-body tracking-md text-left', |
||||||
|
variants: { |
||||||
|
isTruncated: { |
||||||
|
true: '', |
||||||
|
}, |
||||||
|
bold: { |
||||||
|
true: 'font-bold', |
||||||
|
}, |
||||||
|
underline: { |
||||||
|
true: 'underline', |
||||||
|
}, |
||||||
|
strikeThrough: { |
||||||
|
true: 'line-through', |
||||||
|
}, |
||||||
|
size: { |
||||||
|
'2xs': 'text-2xs', |
||||||
|
'xs': 'text-xs', |
||||||
|
'sm': 'text-sm', |
||||||
|
'md': 'text-base', |
||||||
|
'lg': 'text-lg', |
||||||
|
'xl': 'text-xl', |
||||||
|
'2xl': 'text-2xl', |
||||||
|
'3xl': 'text-3xl', |
||||||
|
'4xl': 'text-4xl', |
||||||
|
'5xl': 'text-5xl', |
||||||
|
'6xl': 'text-6xl', |
||||||
|
}, |
||||||
|
}, |
||||||
|
parentVariants: { |
||||||
|
variant: { |
||||||
|
solid: 'text-typography-50', |
||||||
|
outline: 'text-typography-900', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
|
||||||
|
const Root = withStyleContext(View, SCOPE); |
||||||
|
type IToastProps = React.ComponentProps<typeof Root> & { |
||||||
|
className?: string; |
||||||
|
} & VariantProps<typeof toastStyle>; |
||||||
|
|
||||||
|
const Toast = React.forwardRef<React.ComponentRef<typeof Root>, IToastProps>( |
||||||
|
function Toast( |
||||||
|
{ className, variant = 'solid', action = 'muted', ...props }, |
||||||
|
ref |
||||||
|
) { |
||||||
|
return ( |
||||||
|
<Root |
||||||
|
ref={ref} |
||||||
|
className={toastStyle({ variant, action, class: className })} |
||||||
|
context={{ variant, action }} |
||||||
|
{...props} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
type IToastTitleProps = React.ComponentProps<typeof Text> & { |
||||||
|
className?: string; |
||||||
|
} & VariantProps<typeof toastTitleStyle>; |
||||||
|
|
||||||
|
const ToastTitle = React.forwardRef< |
||||||
|
React.ComponentRef<typeof Text>, |
||||||
|
IToastTitleProps |
||||||
|
>(function ToastTitle({ className, size = 'md', children, ...props }, ref) { |
||||||
|
const { variant: parentVariant, action: parentAction } = |
||||||
|
useStyleContext(SCOPE); |
||||||
|
React.useEffect(() => { |
||||||
|
// Issue from react-native side
|
||||||
|
// Hack for now, will fix this later
|
||||||
|
AccessibilityInfo.announceForAccessibility(children as string); |
||||||
|
}, [children]); |
||||||
|
|
||||||
|
return ( |
||||||
|
<Text |
||||||
|
{...props} |
||||||
|
ref={ref} |
||||||
|
aria-live="assertive" |
||||||
|
aria-atomic="true" |
||||||
|
role="alert" |
||||||
|
className={toastTitleStyle({ |
||||||
|
size, |
||||||
|
class: className, |
||||||
|
parentVariants: { |
||||||
|
variant: parentVariant, |
||||||
|
action: parentAction, |
||||||
|
}, |
||||||
|
})} |
||||||
|
> |
||||||
|
{children} |
||||||
|
</Text> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
type IToastDescriptionProps = React.ComponentProps<typeof Text> & { |
||||||
|
className?: string; |
||||||
|
} & VariantProps<typeof toastDescriptionStyle>; |
||||||
|
|
||||||
|
const ToastDescription = React.forwardRef< |
||||||
|
React.ComponentRef<typeof Text>, |
||||||
|
IToastDescriptionProps |
||||||
|
>(function ToastDescription({ className, size = 'md', ...props }, ref) { |
||||||
|
const { variant: parentVariant } = useStyleContext(SCOPE); |
||||||
|
return ( |
||||||
|
<Text |
||||||
|
ref={ref} |
||||||
|
{...props} |
||||||
|
className={toastDescriptionStyle({ |
||||||
|
size, |
||||||
|
class: className, |
||||||
|
parentVariants: { |
||||||
|
variant: parentVariant, |
||||||
|
}, |
||||||
|
})} |
||||||
|
/> |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
Toast.displayName = 'Toast'; |
||||||
|
ToastTitle.displayName = 'ToastTitle'; |
||||||
|
ToastDescription.displayName = 'ToastDescription'; |
||||||
|
|
||||||
|
export { useToast, Toast, ToastTitle, ToastDescription }; |
@ -0,0 +1,28 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { View } from 'react-native'; |
||||||
|
|
||||||
|
import { vstackStyle } from './styles'; |
||||||
|
|
||||||
|
type IVStackProps = React.ComponentProps<typeof View> & |
||||||
|
VariantProps<typeof vstackStyle>; |
||||||
|
|
||||||
|
const VStack = React.forwardRef<React.ComponentRef<typeof View>, IVStackProps>( |
||||||
|
function VStack({ className, space, reversed, ...props }, ref) { |
||||||
|
return ( |
||||||
|
<View |
||||||
|
className={vstackStyle({ |
||||||
|
space, |
||||||
|
reversed: reversed as boolean, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
{...props} |
||||||
|
ref={ref} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
VStack.displayName = 'VStack'; |
||||||
|
|
||||||
|
export { VStack }; |
@ -0,0 +1,27 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import type { VariantProps } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
|
||||||
|
import { vstackStyle } from './styles'; |
||||||
|
|
||||||
|
type IVStackProps = React.ComponentProps<'div'> & |
||||||
|
VariantProps<typeof vstackStyle>; |
||||||
|
|
||||||
|
const VStack = React.forwardRef<React.ComponentRef<'div'>, IVStackProps>( |
||||||
|
function VStack({ className, space, reversed, ...props }, ref) { |
||||||
|
return ( |
||||||
|
<div |
||||||
|
className={vstackStyle({ |
||||||
|
space, |
||||||
|
reversed: reversed as boolean, |
||||||
|
class: className, |
||||||
|
})} |
||||||
|
{...props} |
||||||
|
ref={ref} |
||||||
|
/> |
||||||
|
); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
VStack.displayName = 'VStack'; |
||||||
|
|
||||||
|
export { VStack }; |
@ -0,0 +1,25 @@ |
|||||||
|
import { isWeb } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
import { tva } from '@gluestack-ui/utils/nativewind-utils'; |
||||||
|
|
||||||
|
const baseStyle = isWeb |
||||||
|
? 'flex flex-col relative z-0 box-border border-0 list-none min-w-0 min-h-0 bg-transparent items-stretch m-0 p-0 text-decoration-none' |
||||||
|
: ''; |
||||||
|
|
||||||
|
export const vstackStyle = tva({ |
||||||
|
base: `flex-col ${baseStyle}`, |
||||||
|
variants: { |
||||||
|
space: { |
||||||
|
'xs': 'gap-1', |
||||||
|
'sm': 'gap-2', |
||||||
|
'md': 'gap-3', |
||||||
|
'lg': 'gap-4', |
||||||
|
'xl': 'gap-5', |
||||||
|
'2xl': 'gap-6', |
||||||
|
'3xl': 'gap-7', |
||||||
|
'4xl': 'gap-8', |
||||||
|
}, |
||||||
|
reversed: { |
||||||
|
true: 'flex-col-reverse', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
Loading…
Reference in new issue