Skip to content

Commit

Permalink
Merge branch 'patch' of github.com:gluestack/gluestack-ui into patch
Browse files Browse the repository at this point in the history
rajat693 committed Feb 28, 2024
2 parents f8e6d77 + d3e5ada commit 703404f
Showing 62 changed files with 6,546 additions and 2,632 deletions.
5 changes: 4 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
@@ -7,5 +7,8 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": ["@gluestack/ui-storybook"]
"ignore": [
"@gluestack/ui-storybook",
"@nativewind-gluestack/storybook-nativewind"
]
}
6 changes: 4 additions & 2 deletions example/storybook-nativewind/.storybook/preview.js
Original file line number Diff line number Diff line change
@@ -45,24 +45,26 @@ export const parameters = {
'Feedback',
['Alert', 'Progress', 'Spinner', 'Toast'],
'Data Display',
['Badge'],
['Badge', 'Card'],
'Forms',
[
'Button',
'Checkbox',
'FormControl',
'Input',
'Link',
'Pressable',
'Radio',
'Slider',
'Switch',
'Textarea',
],
'Overlay',
['AlertDialog', 'Modal', 'Popover', 'Tooltip'],
'Disclosure',
['Actionsheet', 'Accordion'],
'Media And Icons',
['Avatar', 'Image'],
['Avatar', 'Image', 'Icon'],
'Others',
['Fab'],
],
8 changes: 4 additions & 4 deletions example/storybook-nativewind/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@gluestack/storybook-nativewind",
"name": "@nativewind-gluestack/storybook-nativewind",
"private": true,
"version": "0.1.0",
"main": "index.js",
@@ -28,8 +28,8 @@
"@expo/webpack-config": "^0.17.2",
"@geometricpanda/storybook-addon-iframe": "^0.2.2",
"@gluestack-style/react": "^1.0.26",
"@gluestack-ui/config": "^1.0.8",
"@gluestack-ui/themed": "^1.1.2",
"@gluestack-ui/config": "^1.1.10",
"@gluestack-ui/themed": "^1.1.8",
"@gluestack/design-system": "^0.5.27",
"@legendapp/motion": "^2.2.0",
"@react-aria/button": "^3.7.0",
@@ -40,7 +40,7 @@
"@react-aria/separator": "^3.3.0",
"@react-aria/utils": "^3.15.0",
"@react-native-aria/button": "^0.2.5",
"@react-native-aria/overlays": "0.3.10",
"@react-native-aria/overlays": "^0.3.10",
"@react-native-aria/separator": "^0.2.6",
"@react-native-async-storage/async-storage": "~1.17.3",
"@react-native-community/datetimepicker": "6.5.2",
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { createAccordion } from '@gluestack-ui/accordion';
import React from 'react';
import { createAccordion } from '@gluestack-ui/accordion';
import { View, Pressable, Text, Platform } from 'react-native';
import {
tva,
withStyleContext,
withStyleContextAndStates,
useStyleContext,
withStates,
VariantProps,
} from '@gluestack-ui/nativewind-utils';

import { H3 } from '@expo/html-elements';
@@ -14,14 +15,13 @@ import { cssInterop } from 'nativewind';
/** Styles */

const accordionStyle = tva({
base: 'w-full shadow-sm',
base: 'w-full',
variants: {
variant: {
filled: 'bg-white',
},
},
});

const accordionItemStyle = tva({
parentVariants: {
variant: {
@@ -30,7 +30,6 @@ const accordionItemStyle = tva({
},
},
});

const accordionTitleTextStyle = tva({
base: 'text-typography-900 font-bold flex-1 text-left',
parentVariants: {
@@ -44,7 +43,6 @@ const accordionTitleTextStyle = tva({
const accordionIconStyle = tva({
base: 'text-typography-900',
});

const accordionContentTextStyle = tva({
base: 'text-typography-700 font-normal',
parentVariants: {
@@ -55,25 +53,28 @@ const accordionContentTextStyle = tva({
},
},
});

const accordionHeaderStyle = tva({
base: 'mx-0 my-0',
});
const accordionContentStyle = tva({
base: 'px-5 mt-2 pb-5',
});

const accordionTriggerStyle = tva({
base: 'w-full py-5 px-5 flex-row justify-between items-center web:outline-none focus:outline-none data-[disabled=true]:opacity-40 data-[disabled=true]:cursor-not-allowed data-[focus-visible=true]:bg-background-50',
});

/** Creator */
const UIAccordion = createAccordion({
Root: withStyleContext(View),
//@ts-ignore
Root:
Platform.OS === 'web'
? withStyleContext(Pressable)
: withStyleContextAndStates(Pressable),
Item: View,
// @ts-ignore
Header: Platform.OS === 'web' ? H3 : View,
Trigger: Platform.OS === 'web' ? Pressable : withStates(Pressable),
//@ts-ignore
Trigger: Pressable,
Icon: View,
TitleText: Text,
ContentText: Text,
@@ -89,33 +90,71 @@ cssInterop(UIAccordion.TitleText, { className: 'style' });
cssInterop(UIAccordion.Content, { className: 'style' });
cssInterop(UIAccordion.ContentText, { className: 'style' });

type IAccordionProps = React.ComponentProps<typeof UIAccordion> &
VariantProps<typeof accordionStyle>;

type IAccordionItemProps = React.ComponentProps<typeof UIAccordion.Item> &
VariantProps<typeof accordionItemStyle>;

type IAccordionContentProps = React.ComponentProps<typeof UIAccordion.Content> &
VariantProps<typeof accordionContentStyle>;

type IAccordionContentTextProps = React.ComponentProps<
typeof UIAccordion.ContentText
> &
VariantProps<typeof accordionContentTextStyle>;

type IAccordionIconProps = React.ComponentProps<typeof UIAccordion.Icon> & {
as?: any;
};

type IAccordionHeaderProps = React.ComponentProps<typeof UIAccordion.Header> &
VariantProps<typeof accordionHeaderStyle>;

type IAccordionTriggerProps = React.ComponentProps<typeof UIAccordion.Trigger> &
VariantProps<typeof accordionTriggerStyle>;

type IAccordionTitleTextProps = React.ComponentProps<
typeof UIAccordion.TitleText
> &
VariantProps<typeof accordionTitleTextStyle>;

/** Components */

const Accordion = React.forwardRef(
(
{ className, variant = 'filled', size = 'md', ...props }: any,
{
className,
variant = 'filled',
//@ts-ignore
size = 'md',
...props
}: { className?: string } & IAccordionProps,
ref?: any
) => {
return (
<UIAccordion
ref={ref}
{...props}
className={accordionStyle({ variant, size, class: className })}
className={accordionStyle({ variant, class: className })}
context={{ variant, size }}
/>
);
}
);

const AccordionItem = React.forwardRef(
({ className, ...props }: any, ref?: any) => {
const { variant, size } = useStyleContext();
(
{ className, ...props }: { className?: string } & IAccordionItemProps,
ref?: any
) => {
const { variant } = useStyleContext();
return (
<UIAccordion.Item
ref={ref}
{...props}
className={accordionItemStyle({
parentVariants: { variant, size },
parentVariants: { variant },
class: className,
})}
/>
@@ -124,7 +163,10 @@ const AccordionItem = React.forwardRef(
);

const AccordionContent = React.forwardRef(
({ className, ...props }: any, ref?: any) => {
(
{ className, ...props }: { className?: string } & IAccordionContentProps,
ref?: any
) => {
return (
<UIAccordion.Content
ref={ref}
@@ -138,36 +180,56 @@ const AccordionContent = React.forwardRef(
);

const AccordionContentText = React.forwardRef(
({ className, ...props }: any, ref?: any) => {
const { variant, size } = useStyleContext();
(
{
className,
...props
}: { className?: string } & IAccordionContentTextProps,
ref?: any
) => {
const { size } = useStyleContext();
return (
<UIAccordion.ContentText
ref={ref}
{...props}
className={accordionContentTextStyle({
parentVariants: { variant, size },
parentVariants: { size },
class: className,
})}
/>
);
}
);

const AccordionIcon = React.forwardRef(
({ className, ...props }: any, ref?: any) => {
const AccordionIcon = ({
className,
as: AsComp,
...props
}: IAccordionIconProps & { className?: any }) => {
if (AsComp) {
return (
<UIAccordion.Icon
ref={ref}
{...props}
<AsComp
className={accordionIconStyle({
class: className,
})}
{...props}
/>
);
}
);
return (
<UIAccordion.Icon
className={accordionIconStyle({
class: className,
})}
{...props}
/>
);
};
const AccordionHeader = React.forwardRef(
({ className, ...props }: any, ref?: any) => {
(
{ className, ...props }: { className?: string } & IAccordionHeaderProps,
ref?: any
) => {
return (
<UIAccordion.Header
ref={ref}
@@ -180,7 +242,10 @@ const AccordionHeader = React.forwardRef(
}
);
const AccordionTrigger = React.forwardRef(
({ className, ...props }: any, ref?: any) => {
(
{ className, ...props }: { className?: string } & IAccordionTriggerProps,
ref?: any
) => {
return (
<UIAccordion.Trigger
ref={ref}
@@ -193,14 +258,17 @@ const AccordionTrigger = React.forwardRef(
}
);
const AccordionTitleText = React.forwardRef(
({ className, ...props }: any, ref?: any) => {
const { variant, size } = useStyleContext();
(
{ className, ...props }: { className?: string } & IAccordionTitleTextProps,
ref?: any
) => {
const { size } = useStyleContext();
return (
<UIAccordion.Header
<UIAccordion.TitleText
ref={ref}
{...props}
className={accordionTitleTextStyle({
parentVariants: { variant, size },
parentVariants: { size },
class: className,
})}
/>

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ cssInterop(UIButton.Spinner, { className: 'style' });
cssInterop(UIButton.Icon, { className: 'style' });

const buttonStyle = tva({
base: 'group/button rounded-lg bg-primary-500 flex-row items-center justify-center data-[focus=true]:web:outline-none data-[focus-visible=true]:web:ring-2 ',
base: 'group/button rounded-lg bg-primary-500 flex-row items-center justify-center data-[focus=true]:web:outline-none data-[focus-visible=true]:web:ring-2 data-[disabled=true]:opacity-40',
variants: {
action: {
primary:
@@ -160,6 +160,62 @@ const buttonTextStyle = tva({
],
});

const buttonIconStyle = tva({
base: 'text-typography-0',
parentVariants: {
action: {
primary:
'text-primary-600 group-hover/button:text-primary-600 group-active/button:text-primary-700',
secondary:
'text-secondary-600 group-hover/button:text-secondary-600 group-active/button:text-secondary-700',
positive:
'text-success-600 group-hover/button:text-success-600 group-active/button:text-success-700',
negative:
'text-error-600 group-hover/button:text-error-600 group-active/button:text-error-700',
},
variant: {
link: 'group-hover/button:underline group-active/button:underline',
outline: '',
solid:
'text-typography-0 group-hover/button:text-typography-0 group-active/button:text-typography-0',
},
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',
},
},
parentCompoundVariants: [
{
variant: 'solid',
action: 'primary',
class:
'text-typography-0 group-hover/button:text-typography-0 group-active/button:text-typography-0',
},
{
variant: 'solid',
action: 'secondary',
class:
'text-typography-0 group-hover/button:text-typography-0 group-active/button:text-typography-0',
},
{
variant: 'solid',
action: 'positive',
class:
'text-typography-0 group-hover/button:text-typography-0 group-active/button:text-typography-0',
},
{
variant: 'solid',
action: 'negative',
class:
'text-typography-0 group-hover/button:text-typography-0 group-active/button:text-typography-0',
},
],
});

type IButtonProps = React.ComponentProps<typeof UIButton> &
VariantProps<typeof buttonStyle>;

@@ -178,6 +234,7 @@ const Button = React.forwardRef(
) => {
return (
<UIButton
// @ts-ignore
ref={ref}
{...props}
className={buttonStyle({ variant, size, action, class: className })}
@@ -199,7 +256,7 @@ const ButtonText = React.forwardRef(
action,
...props
}: { className?: string } & IButtonTextProps,
ref
ref?: any
) => {
const {
variant: parentVariant,
@@ -209,6 +266,7 @@ const ButtonText = React.forwardRef(

return (
<UIButton.Text
// @ts-ignore
ref={ref}
{...props}
className={buttonTextStyle({
@@ -232,12 +290,43 @@ const ButtonSpinner = UIButton.Spinner;
const ButtonIcon = ({
className,
as: AsComp,
size,
...props
}: IButtonIcon & { className?: any }) => {
const {
variant: parentVariant,
size: parentSize,
action: parentAction,
} = useStyleContext();

if (AsComp) {
return <AsComp className={className} {...props} />;
return (
<AsComp
{...props}
className={buttonIconStyle({
parentVariants: {
size: parentSize,
variant: parentVariant,
action: parentAction,
},
size,
class: className,
})}
/>
);
}
return <UIButton.Icon className={className} {...props} />;
return (
<UIButton.Icon
{...props}
className={buttonIconStyle({
parentVariants: {
size: parentSize,
},
size,
class: className,
})}
/>
);
};
Button.displayName = 'Button';
ButtonText.displayName = 'ButtonText';
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { tva } from '@gluestack-ui/nativewind-utils';
import { View } from 'react-native';

const cardStyle = tva({
variants: {
size: {
sm: 'p-3 rounded-sm',
md: 'p-4 rounded-md',
lg: 'p-6 rounded-xl',
},
variant: {
elevated: 'bg-background-0',
outline: 'border border-outline-200 ',
ghost: 'rounded-none',
filled: 'bg-background-50',
},
},
});

const Card = ({
className,
size = 'md',
variant = 'elevated',
...props
}: any) => {
return (
<View
className={cardStyle({ size, variant, class: className })}
{...props}
/>
);
};

Card.displayName = 'Card';

export { Card };
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Text, View } from 'react-native';
import React from 'react';
import { createFormControl } from '@gluestack-ui/form-control';
import { cssInterop } from 'nativewind';
import {
tva,
withStyleContext,
useStyleContext,
cssInterop,
VariantProps,
} from '@gluestack-ui/nativewind-utils';

const formControlStyle = tva({
@@ -220,7 +221,13 @@ cssInterop(UIFormControl.Label.Astrick, { className: 'style' });
cssInterop(UIFormControl.Helper, { className: 'style' });
cssInterop(UIFormControl.Helper.Text, { className: 'style' });

const FormControl = ({ className, size = 'md', ...props }: any) => {
type IFormControlProps = React.ComponentProps<typeof UIFormControl> &
VariantProps<typeof formControlStyle>;
const FormControl = ({
className,
size = 'md',
...props
}: { className?: string } & IFormControlProps) => {
return (
<UIFormControl
className={formControlStyle({ class: className })}
@@ -229,15 +236,30 @@ const FormControl = ({ className, size = 'md', ...props }: any) => {
/>
);
};
const FormControlError = ({ className, ...props }: any) => {

type IFormControlErrorProps = React.ComponentProps<typeof UIFormControl.Error> &
VariantProps<typeof formControlErrorStyle>;
const FormControlError = ({
className,
...props
}: { className?: string } & IFormControlErrorProps) => {
return (
<UIFormControl.Error
className={formControlErrorStyle({ class: className })}
{...props}
/>
);
};
const FormControlErrorText = ({ className, size, ...props }: any) => {

type IFormControlErrorTextProps = React.ComponentProps<
typeof UIFormControl.Error.Text
> &
VariantProps<typeof formControlErrorTextStyle>;
const FormControlErrorText = ({
className,
size,
...props
}: { className?: string } & IFormControlErrorTextProps) => {
const { size: parentSize } = useStyleContext();
return (
<UIFormControl.Error.Text
@@ -250,9 +272,30 @@ const FormControlErrorText = ({ className, size, ...props }: any) => {
/>
);
};
const FormControlErrorIcon = ({ className, size, ...props }: any) => {
const { size: parentSize } = useStyleContext();

type IFormControlErrorIconProps = React.ComponentProps<
typeof UIFormControl.Error.Icon
> &
VariantProps<typeof formControlErrorIconStyle>;
const FormControlErrorIcon = ({
className,
size,
as: AsComp,
...props
}: { className?: string } & IFormControlErrorIconProps) => {
const { size: parentSize } = useStyleContext();
if (AsComp) {
return (
<AsComp
className={formControlErrorIconStyle({
parentVariants: { size: parentSize },
size,
class: className,
})}
{...props}
/>
);
}
return (
<UIFormControl.Error.Icon
className={formControlErrorIconStyle({
@@ -264,15 +307,30 @@ const FormControlErrorIcon = ({ className, size, ...props }: any) => {
/>
);
};
const FormControlLabel = ({ className, ...props }: any) => {

type IFormControlLabelProps = React.ComponentProps<typeof UIFormControl.Label> &
VariantProps<typeof formControlLabelStyle>;
const FormControlLabel = ({
className,
...props
}: { className?: string } & IFormControlLabelProps) => {
return (
<UIFormControl.Label
className={formControlLabelStyle({ class: className })}
{...props}
/>
);
};
const FormControlLabelText = ({ className, size, ...props }: any) => {

type IFormControlLabelTextProps = React.ComponentProps<
typeof UIFormControl.Label.Text
> &
VariantProps<typeof formControlLabelTextStyle>;
const FormControlLabelText = ({
className,
size,
...props
}: { className?: string } & IFormControlLabelTextProps) => {
const { size: parentSize } = useStyleContext();

return (
@@ -286,7 +344,16 @@ const FormControlLabelText = ({ className, size, ...props }: any) => {
/>
);
};
const FormControlLabelAstrick = ({ className, size, ...props }: any) => {

type IFormControlLabelAstrickProps = React.ComponentProps<
typeof UIFormControl.Label.Astrick
> &
VariantProps<typeof formControlLabelAstrickStyle>;
const FormControlLabelAstrick = ({
className,
size,
...props
}: { className?: string } & IFormControlLabelAstrickProps) => {
const { size: parentSize } = useStyleContext();

return (
@@ -300,7 +367,15 @@ const FormControlLabelAstrick = ({ className, size, ...props }: any) => {
/>
);
};
const FormControlHelper = ({ className, ...props }: any) => {

type IFormControlHelperProps = React.ComponentProps<
typeof UIFormControl.Helper
> &
VariantProps<typeof formControlHelperStyle>;
const FormControlHelper = ({
className,
...props
}: { className?: string } & IFormControlHelperProps) => {
return (
<UIFormControl.Helper
className={formControlHelperStyle({
@@ -310,7 +385,16 @@ const FormControlHelper = ({ className, ...props }: any) => {
/>
);
};
const FormControlHelperText = ({ className, size, ...props }: any) => {

type IFormControlHelperTextProps = React.ComponentProps<
typeof UIFormControl.Helper.Text
> &
VariantProps<typeof formControlHelperTextStyle>;
const FormControlHelperText = ({
className,
size,
...props
}: { className?: string } & IFormControlHelperTextProps) => {
const { size: parentSize } = useStyleContext();

return (
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import React from 'react';
import { createIcon } from '@gluestack-ui/icon';
import { Path, Svg } from 'react-native-svg';
import { tva } from '@gluestack-ui/nativewind-utils';
import { cssInterop } from 'nativewind';
import { tva, cssInterop } from '@gluestack-ui/nativewind-utils';

export const UIIcon = createIcon({
Root: Svg,
});

const iconStyle = tva({
base: 'color-background-800',
base: 'text-background-800 fill-none',
variants: {
size: {
'2xs': 'h-3 w-3',
@@ -26,7 +25,17 @@ const iconStyle = tva({
});

export const Icon = React.forwardRef(
({ fill = 'none', size, className, ...props }: any, ref) => {
({ fill = 'none', size, className, as: AsComp, ...props }: any, ref) => {
if (AsComp) {
return (
<AsComp
ref={ref}
{...props}
fill={fill}
className={iconStyle({ size, class: className })}
/>
);
}
return (
<UIIcon
ref={ref}
@@ -188,6 +197,7 @@ cssInterop(UIIcon, { className: 'style' });
// });

type ParameterTypes = Omit<Parameters<typeof createIcon>[0], 'Root'>;

const createIconUI = ({ ...props }: ParameterTypes) => {
const UIIcon = createIcon({ Root: Svg, ...props });

@@ -240,7 +250,7 @@ const AddIcon = React.forwardRef(({ className, size, ...props }: any, ref) => {
AddIcon.displayName = 'AddIcon';
export { AddIcon };

export const UIAlertCircleIcon = createIcon({
const UIAlertCircleIcon = createIcon({
Root: Svg,
viewBox: '0 0 24 24',
path: (
@@ -283,6 +293,7 @@ const AlertCircleIcon = React.forwardRef(
);

AlertCircleIcon.displayName = 'AlertCircleIcon';
export { AlertCircleIcon };

const UIArrowUpIcon = createIcon({
Root: Svg,
@@ -1440,7 +1451,7 @@ GripVerticalIcon.displayName = 'GripVerticalIcon';

export { GripVerticalIcon };

export const UIHelpCircleIcon = createIcon({
const UIHelpCircleIcon = createIcon({
Root: Svg,
viewBox: '0 0 24 24',
path: (
@@ -1483,8 +1494,9 @@ const HelpCircleIcon = React.forwardRef(
);

HelpCircleIcon.displayName = 'HelpCircleIcon';
export { HelpCircleIcon };

export const UIInfoIcon = createIcon({
const UIInfoIcon = createIcon({
Root: Svg,
viewBox: '0 0 24 24',
path: (
@@ -1525,6 +1537,7 @@ const InfoIcon = React.forwardRef(({ className, size, ...props }: any, ref) => {
});

InfoIcon.displayName = 'InfoIcon';
export { InfoIcon };

const UILinkIcon = createIcon({
Root: Svg,
@@ -1713,7 +1726,7 @@ MailIcon.displayName = 'MailIcon';

export { MailIcon };

export const UIMenuIcon = createIcon({
const UIMenuIcon = createIcon({
Root: Svg,
viewBox: '0 0 24 24',
path: (
@@ -1754,6 +1767,7 @@ const MenuIcon = React.forwardRef(({ className, size, ...props }: any, ref) => {
});

MenuIcon.displayName = 'MenuIcon';
export { MenuIcon };

const UIMessageCircleIcon = createIcon({
Root: Svg,
@@ -1787,7 +1801,7 @@ MessageCircleIcon.displayName = 'MessageCircleIcon';

export { MessageCircleIcon };

export const UIMoonIcon = createIcon({
const UIMoonIcon = createIcon({
Root: Svg,
viewBox: '0 0 24 24',
path: (
@@ -1814,6 +1828,7 @@ const MoonIcon = React.forwardRef(({ className, size, ...props }: any, ref) => {
});

MoonIcon.displayName = 'MoonIcon';
export { MoonIcon };

const UIPaperclipIcon = createIcon({
Root: Svg,
@@ -1916,7 +1931,7 @@ PlayIcon.displayName = 'PlayIcon';

export { PlayIcon };

export const UIRemoveIcon = createIcon({
const UIRemoveIcon = createIcon({
Root: Svg,
viewBox: '0 0 24 24',
path: (
@@ -1945,6 +1960,7 @@ const RemoveIcon = React.forwardRef(
);

RemoveIcon.displayName = 'RemoveIcon';
export { RemoveIcon };

const UIRepeatIcon = createIcon({
Root: Svg,
@@ -2057,7 +2073,7 @@ Repeat1Icon.displayName = 'Repeat1Icon';

export { RepeatIcon, Repeat1Icon };

export const UISearchIcon = createIcon({
const UISearchIcon = createIcon({
Root: Svg,
viewBox: '0 0 24 24',
path: (
@@ -2093,6 +2109,7 @@ const SearchIcon = React.forwardRef(
);

SearchIcon.displayName = 'SearchIcon';
export { SearchIcon };

const UISettingsIcon = createIcon({
Root: Svg,
@@ -2262,7 +2279,7 @@ StarIcon.displayName = 'StarIcon';

export { StarIcon };

export const UISunIcon = createIcon({
const UISunIcon = createIcon({
Root: Svg,
viewBox: '0 0 24 24',
path: (
@@ -2345,6 +2362,7 @@ const SunIcon = React.forwardRef(({ className, size, ...props }: any, ref) => {
});

SunIcon.displayName = 'SunIcon';
export { SunIcon };

const UIThreeDotsIcon = createIcon({
Root: Svg,

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,8 +1,20 @@
import React, { useEffect } from 'react';
import { createModal } from '@gluestack-ui/modal';
import { Pressable, View, StyleSheet } from 'react-native';
import { cn } from '@gluestack-ui/nativewind-utils';
import { cssInterop } from 'nativewind';
import {
Pressable,
View,
StyleSheet,
ScrollView,
Platform,
} from 'react-native';
import {
tva,
cssInterop,
VariantProps,
useStyleContext,
withStyleContext,
withStyleContextAndStates,
} from '@gluestack-ui/nativewind-utils';
import Animated, {
Easing,
useSharedValue,
@@ -12,10 +24,14 @@ import Animated, {

const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
const UIModal = createModal({
Root: View,
// @ts-ignore
Root:
Platform.OS === 'web'
? withStyleContext(View)
: withStyleContextAndStates(View),
Backdrop: AnimatedPressable,
Content: Animated.View,
Body: View,
Body: ScrollView,
CloseButton: Pressable,
Footer: View,
Header: View,
@@ -24,115 +40,212 @@ const UIModal = createModal({
cssInterop(UIModal, { className: 'style' });
cssInterop(UIModal.Backdrop, { className: 'style' });
cssInterop(UIModal.Content, { className: 'style' });
cssInterop(UIModal.Body, { className: 'style' });
cssInterop(UIModal.CloseButton, { className: 'style' });
cssInterop(UIModal.Footer, { className: 'style' });
cssInterop(UIModal.Header, { className: 'style' });
cssInterop(UIModal.Body, { className: 'style' });
cssInterop(UIModal.Footer, { className: 'style' });

const Modal = React.forwardRef(({ className, ...props }: any, ref) => (
<UIModal
className={cn(
'flex-1 w-full h-full justify-center items-center',
className
)}
{...props}
ref={ref}
pointerEvents="box-none"
/>
));

const ModalBackdrop = React.forwardRef(({ className, ...props }: any, ref) => {
const opacity = useSharedValue(0);

useEffect(() => {
opacity.value = withTiming(0.5, {
easing: Easing.linear,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<UIModal.Backdrop
className={cn(
'h-full w-full bg-background-950 cursor-default opacity-50 pointer-events-auto',
className
)}
style={[
StyleSheet.absoluteFill,
{
opacity: opacity,
},
]}
{...props}
const modalStyle = tva({
base: 'group/modal w-full h-full justify-center items-center web:pointer-events-none',
});

const modalBackdropStyle = tva({
base: 'absolute left-0 top-0 right-0 bottom-0 bg-background-950 web:cursor-default',
});

const modalContentStyle = tva({
base: 'bg-background-50 rounded-lg overflow-hidden',
parentVariants: {
size: {
xs: 'w-[60%] max-w-[360px]',
sm: 'w-[70%] max-w-[420px]',
md: 'w-[80%] max-w-[510px]',
lg: 'w-[90%] max-w-[640px]',
full: 'w-full',
},
},
});

const modalBodyStyle = tva({
base: 'px-4 pb-2 pt-0',
});

const modalCloseButtonStyle = tva({
base: 'group/modal-close-button z-10 p-2 rounded-sm data-[focus-visible=true]:web:bg-background-100 web:outline-0 cursor-pointer',
});

const modalHeaderStyle = tva({
base: 'px-4 pt-4 pb-2 justify-between items-center flex-row',
});

const modalFooterStyle = tva({
base: 'p-4 flex-row justify-end items-center flex-wrap',
});

type IModalProps = React.ComponentProps<typeof UIModal> &
VariantProps<typeof modalStyle>;

type IModalBackdropProps = React.ComponentProps<typeof UIModal.Backdrop> &
VariantProps<typeof modalBackdropStyle>;

type IModalContentProps = React.ComponentProps<typeof UIModal.Content> &
VariantProps<typeof modalContentStyle>;

type IModalHeaderProps = React.ComponentProps<typeof UIModal.Header> &
VariantProps<typeof modalHeaderStyle>;

type IModalBodyProps = React.ComponentProps<typeof UIModal.Body> &
VariantProps<typeof modalBodyStyle>;

type IModalFooterProps = React.ComponentProps<typeof UIModal.Footer> &
VariantProps<typeof modalFooterStyle>;

type IModalCloseButtonProps = React.ComponentProps<typeof UIModal.CloseButton> &
VariantProps<typeof modalCloseButtonStyle>;

const Modal = React.forwardRef(
(
//@ts-ignore
{ className, size = 'md', ...props }: { className?: string } & IModalProps,
ref
) => (
<UIModal
ref={ref}
{...props}
pointerEvents="box-none"
className={modalStyle({ size, class: className })}
context={{ size }}
/>
);
});
)
);

const ModalBackdrop = React.forwardRef(
(
{ className, ...props }: { className?: string } & IModalBackdropProps,
ref
) => {
const opacity = useSharedValue(0);

useEffect(() => {
opacity.value = withTiming(0.5, {
easing: Easing.linear,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<UIModal.Backdrop
ref={ref}
{...props}
className={modalBackdropStyle({
class: className,
})}
style={[
StyleSheet.absoluteFill,
{
opacity: opacity,
},
]}
/>
);
}
);

const ModalContent = React.forwardRef(({ className, ...props }: any, ref) => {
const opacity = useSharedValue(0);
const scale = useSharedValue(0.9);
useEffect(() => {
opacity.value = withTiming(1, {
easing: Easing.linear,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
scale.value = withSpring(1, {
damping: 18,
stiffness: 250,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<UIModal.Content
pointerEvents="auto"
className={cn(
'w-70% max-w-[420px] bg-background-50 rounded-md',
className
)}
const ModalContent = React.forwardRef(
(
{ className, size, ...props }: { className?: string } & IModalContentProps,
ref
) => {
const { size: parentSize } = useStyleContext();

const opacity = useSharedValue(0);
const scale = useSharedValue(0.9);
useEffect(() => {
opacity.value = withTiming(1, {
easing: Easing.linear,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
useEffect(() => {
scale.value = withSpring(1, {
damping: 18,
stiffness: 250,
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<UIModal.Content
ref={ref}
{...props}
className={modalContentStyle({
parentVariants: {
size: parentSize,
},
size,
class: className,
})}
style={{
opacity: opacity,
transform: [{ scale: scale }],
}}
pointerEvents="auto"
/>
);
}
);

const ModalHeader = React.forwardRef(
(
{ className, ...props }: { className?: string } & IModalHeaderProps,
ref
) => (
<UIModal.Header
ref={ref}
{...props}
style={{
opacity: opacity,
transform: [{ scale: scale }],
}}
className={modalHeaderStyle({
class: className,
})}
/>
)
);

const ModalBody = React.forwardRef(
({ className, ...props }: { className?: string } & IModalBodyProps, ref) => (
<UIModal.Body
ref={ref}
{...props}
className={modalBodyStyle({
class: className,
})}
/>
);
});
)
);

const ModalHeader = React.forwardRef(({ className, ...props }: any, ref) => (
<UIModal.Header
className={cn(
'px-4 pt-4 pb-2 justify-between item-center flex-row',
className
)}
{...props}
ref={ref}
/>
));

const ModalBody = React.forwardRef(({ className, ...props }: any, ref) => (
<UIModal.Body className={cn('px-4 pb-2', className)} {...props} ref={ref} />
));

const ModalFooter = React.forwardRef(({ className, ...props }: any, ref) => (
<UIModal.Footer
className={cn('p-4 flex-row flex-wrap justify-end items-center', className)}
{...props}
ref={ref}
/>
));
const ModalFooter = React.forwardRef(
(
{ className, ...props }: { className?: string } & IModalFooterProps,
ref
) => (
<UIModal.Footer
ref={ref}
{...props}
className={modalFooterStyle({
class: className,
})}
/>
)
);

const ModalCloseButton = React.forwardRef(
({ className, ...props }: any, ref) => (
(
{ className, ...props }: { className?: string } & IModalCloseButtonProps,
ref
) => (
<UIModal.CloseButton
className={cn(
'group/modal-close-button p-2 rounded-lg focus-visible:bg-background-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',
className
)}
{...props}
ref={ref}
{...props}
className={modalCloseButtonStyle({
class: className,
})}
/>
)
);
Original file line number Diff line number Diff line change
@@ -1,309 +1,101 @@
import React from 'react';
import { createTextarea } from '@gluestack-ui/textarea';
import { View, TextInput } from 'react-native';
import { View, TextInput, Platform } from 'react-native';
import {
tva,
withStyleContextAndStates,
useStyleContext,
withStyleContext,
withStates,
cssInterop,
VariantProps,
} from '@gluestack-ui/nativewind-utils';
import { cssInterop } from 'nativewind';

const UITextarea = createTextarea({
// @ts-ignore
Root: withStyleContextAndStates(View),
Input: TextInput,
Root:
Platform.OS === 'web'
? withStyleContext(View)
: withStyleContextAndStates(View),
Input: Platform.OS === 'web' ? TextInput : withStates(TextInput),
});

// @ts-ignore
cssInterop(UITextarea, { className: 'style' });
cssInterop(UITextarea.Input, { className: 'style' });

const textareaStyle = tva({
base: 'w-full h-[100px] border border-background-300 rounded-sm hover:border-outline-400 hover:border-primary-700 focus:border-primary-700 disabled:opacity-40 disable:border-background-300',
base: 'w-full h-[100px] border border-background-300 rounded hover: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',

variants: {
size: {
sm: '',
md: '',
lg: '',
// _input: {
// fontSize: '$lg',
// },
// {
// _input: {
// fontSize: '$md',
// },
// },
// {
// _input: {
// fontSize: '$sm',
// },
// },
xl: '',
// {
// _input: {
// fontSize: '$xl',
// },
// },
},
variant: {
default:
'focus:border-primary-700 focus:border-primary-700 focus:web:shadows-sm invalid:border-error-700 invalid:web:shadows-sm',
// {
// '_input': {
// _web: {
// outlineWidth: '0',
// outline: 'none',
// },
// },

// ':focus': {
// borderColor: '$primary700',
// _web: {
// boxShadow: 'inset 0 0 0 1px $primary700',
// },
// },

// ':invalid': {
// 'borderColor': '$error700',
// '_web': {
// boxShadow: 'inset 0 0 0 1px $error700',
// },
// ':hover': {
// borderColor: '$error700',
// },
// ':focus': {
// ':hover': {
// borderColor: '$primary700',
// _web: {
// boxShadow: 'inset 0 0 0 1px $primary700',
// },
// },
// },
// ':disabled': {
// ':hover': {
// borderColor: '$error700',
// _web: {
// boxShadow: 'inset 0 0 0 1px $error700',
// },
// },
// },
// },
// },
},

defaultVariants: {
variant: 'default',
size: 'md',
'data-[focus=true]:border-primary-700 data-[focus=true]:web:ring-1 data-[focus=true]:web:ring-inset data-[focus=true]:web:ring-primary-700 data-[invalid=true]:border-error-700 data-[invalid=true]:web:ring-1 data-[invalid=true]:web:ring-inset data-[invalid=true]:web:ring-error-700 data-[invalid=true]:hover:border-error-700 data-[invalid=true]:data-[focus=true]:hover:border-primary-700 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-primary-700 data-[invalid=true]:data-[disabled=true]:hover:border-error-700 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-error-700 ',
},
},
});

const textareaInputStyle = tva({
base: 'p-2 flex-1 color-typography-900 align-text-top',

// / p: '$2',
// color: '$text900',
// textAlignVertical: 'top',
// flex: 1,

// props: {
// // @ts-ignore
// multiline: true,
// placeholderTextColor: '$text500',
// },

// _web: {
// 'cursor': 'text',
// ':disabled': {
// cursor: 'not-allowed',
// },
// },
// },
// {
// ancestorStyle: ['_input'],
// resolveProps: ['placeholderTextColor'],
// },
// {
// propertyTokenMap: {
// placeholderTextColor: 'colors',
// },
// }
base: 'p-3 web:outline-0 web:outline-none flex-1 color-typography-900 align-text-top 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',
},
},
});

// const StyledRoot =
// {

// '_input': {
// p: '$3',
// _web: {
// outlineWidth: 0,
// outline: 'none',
// },
// },

// ':hover': {
// borderColor: '$border400',
// },

// ':focus': {
// 'borderColor': '$primary700',
// ':hover': {
// borderColor: '$primary700',
// },
// },

// ':disabled': {
// 'opacity': 0.4,
// ':hover': {
// borderColor: '$background300',
// },
// },

// 'variants': {
// size: {
// xl: {
// _input: {
// fontSize: '$xl',
// },
// },

// lg: {
// _input: {
// fontSize: '$lg',
// },
// },
// md: {
// _input: {
// fontSize: '$md',
// },
// },
// sm: {
// _input: {
// fontSize: '$sm',
// },
// },
// },
// variant: {
// default: {
// '_input': {
// _web: {
// outlineWidth: '0',
// outline: 'none',
// },
// },

// ':focus': {
// borderColor: '$primary700',
// _web: {
// boxShadow: 'inset 0 0 0 1px $primary700',
// },
// },

// ':invalid': {
// 'borderColor': '$error700',
// '_web': {
// boxShadow: 'inset 0 0 0 1px $error700',
// },
// ':hover': {
// borderColor: '$error700',
// },
// ':focus': {
// ':hover': {
// borderColor: '$primary700',
// _web: {
// boxShadow: 'inset 0 0 0 1px $primary700',
// },
// },
// },
// ':disabled': {
// ':hover': {
// borderColor: '$error700',
// _web: {
// boxShadow: 'inset 0 0 0 1px $error700',
// },
// },
// },
// },
// },
// },
// },

// 'defaultProps': {
// variant: 'default',
// size: 'md',
// },
// },

// const StyledInput = styled(
// TextInput,
// {
// p: '$2',
// color: '$text900',
// textAlignVertical: 'top',
// flex: 1,

// props: {
// // @ts-ignore
// multiline: true,
// placeholderTextColor: '$text500',
// },

// _web: {
// 'cursor': 'text',
// ':disabled': {
// cursor: 'not-allowed',
// },
// },
// },
// {
// ancestorStyle: ['_input'],
// resolveProps: ['placeholderTextColor'],
// },
// {
// propertyTokenMap: {
// placeholderTextColor: 'colors',
// },
// }
// );

export const Textarea = React.forwardRef(
({ className, variant = 'solid', size = 'md', ...props }: any, ref) => {
type ITextareaProps = React.ComponentProps<typeof UITextarea> &
VariantProps<typeof textareaStyle>;

const Textarea = React.forwardRef(
(
{
className,
variant = 'default',
size = 'md',
...props
}: { className?: string } & ITextareaProps,
ref
) => {
return (
<UITextarea
ref={ref}
{...props}
className={textareaStyle({ variant, size, class: className })}
context={{ variant, size }}
className={textareaStyle({ variant, class: className })}
context={{ size }}
/>
);
}
);

export const TextareaInput = React.forwardRef(
({ className, variant, size, action, ...props }: any, ref) => {
const {
variant: parentVariant,
size: parentSize,
action: parentAction,
} = useStyleContext();
type ITextareaInputProps = React.ComponentProps<typeof UITextarea.Input> &
VariantProps<typeof textareaInputStyle>;

const TextareaInput = React.forwardRef(
(
{ className, ...props }: { className?: string } & ITextareaInputProps,
ref
) => {
const { size: parentSize } = useStyleContext();

return (
<UITextarea.Input
ref={ref}
{...props}
className={textareaInputStyle({
parentVariants: {
variant: parentVariant,
size: parentSize,
action: parentAction,
},
variant,
size,
action,
class: className,
})}
/>
);
}
);

Textarea.displayName = 'Textarea';
TextareaInput.displayName = 'TextareaInput';

export { Textarea, TextareaInput };
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { View } from 'react-native';
import { styled } from '@gluestack-style/react';

const StyledRoot = styled(View, {
variants: {
size: {
sm: {
p: '$3',
borderRadius: '$sm',
},
md: {
p: '$4',
borderRadius: '$md',
},
lg: {
p: '$6',
borderRadius: '$xl',
},
},
variant: {
elevated: {
bg: '$backgroundLight0',
shadowColor: '$backgroundLight800',
shadowOffset: {
width: 0,
height: 1,
},
shadowOpacity: 0.22,
shadowRadius: 2.22,
elevation: 3,
_dark: {
bg: '$backgroundDark900',
},
},
outline: {
borderWidth: 1,
borderColor: '$borderLight200',
_dark: {
borderColor: '$borderDark800',
},
},
ghost: {
borderRadius: '$none',
},
filled: {
bg: '$backgroundLight50',
_dark: {
bg: '$backgroundDark900',
},
},
},
},
defaultProps: {
theme: 'light',
size: 'md',
variant: 'elevated',
},
});

export const Card = StyledRoot;
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ export * from './AlertDialog';
export * from './Avatar';
export * from './Badge';
export * from './Box';
export * from './Card';
export * from './FlatList';
export * from './Center';
export * from './Checkbox';
Original file line number Diff line number Diff line change
@@ -9,9 +9,12 @@ import {
AccordionContent,
AccordionContentText,
} from '@/components/ui/Accordion';
import { ChevronDownIcon } from 'lucide-react-native';
import { ChevronUpIcon } from 'lucide-react-native';
import { MinusIcon, PlusIcon } from 'lucide-react-native';
import {
ChevronDownIcon,
ChevronUpIcon,
MinusIcon,
PlusIcon,
} from 'lucide-react-native';

const AccordionBasic = ({ ...props }: any) => {
const accRef = React.useRef(null);
143 changes: 39 additions & 104 deletions example/storybook-nativewind/src/components/AlertDialog/AlertDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
import React, { useState } from 'react';

import {
CloseIcon,
Button,
ButtonText,
ButtonGroup,
Text,
Center,
Icon,
Heading,
AlertCircleIcon,
CheckCircleIcon,
HStack,
} from '@gluestack-ui/themed';
import { Button, ButtonText } from '@/components/ui/Button';
import { Text } from '@/components/ui/Text';
import { Heading } from '@/components/ui/Heading';
import { X } from 'lucide-react-native';

import {
AlertDialog,
@@ -23,91 +13,45 @@ import {
AlertDialogFooter,
AlertDialogBody,
} from '@/components/ui/AlertDialog';
import { AlertTriangleIcon } from 'lucide-react-native';

const AlertDialogBasic = ({
showAlertDialog: showAlertDialogProp = true,
...props
}) => {
const AlertDialogBasic = ({ ...props }) => {
const [showAlertDialog, setShowAlertDialog] = useState(false);
const handleClose = () => setShowAlertDialog(!showAlertDialog);
return (
<AlertDialog
isOpen={showAlertDialog || showAlertDialogProp}
onClose={handleClose}
{...props}
>
<AlertDialogBackdrop />
<AlertDialogContent>
<AlertDialogHeader>
<Heading>Return Policy</Heading>
<AlertDialogCloseButton>
<Icon as={CloseIcon} />
</AlertDialogCloseButton>
</AlertDialogHeader>
<AlertDialogBody>
<Text>
Whoa, slow down there! This modal is like a red light at an
intersection, reminding you to stop and think before you proceed. Is
deleting this folder the right choice?
</Text>
</AlertDialogBody>
<AlertDialogFooter gap="$3">
<Button variant="outline" action="secondary" onPress={handleClose}>
<ButtonText>Cancel</ButtonText>
</Button>
<Button action="negative" onPress={handleClose}>
<ButtonText>Delete</ButtonText>
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
);
};

const FigmaAlertDialogStory = ({
showAlertDialog: _showAlertDialogProp = true,
_colorMode,
...props
}) => {
return (
<AlertDialog
isOpen={true}
py="$16"
w={1230}
bg="#00000080"
sx={{
_dark: {
bg: '#ffffff80',
},
}}
_experimentalOverlay={true}
{...props}
>
<AlertDialogContent>
<AlertDialogHeader>
<Heading>Return Policy</Heading>
<AlertDialogCloseButton>
<Icon as={CloseIcon} />
</AlertDialogCloseButton>
</AlertDialogHeader>
<AlertDialogBody>
<Text>
Whoa, slow down there! This modal is like a red light at an
intersection, reminding you to stop and think before you proceed. Is
deleting this folder the right choice?
</Text>
</AlertDialogBody>
<AlertDialogFooter gap="$3">
<Button variant="outline" action="secondary">
<ButtonText>Cancel</ButtonText>
</Button>
<Button action="negative">
<ButtonText>Delete</ButtonText>
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<>
<Button onPress={() => setShowAlertDialog(true)}>
<ButtonText>Click me</ButtonText>
</Button>
<AlertDialog isOpen={showAlertDialog} onClose={handleClose} {...props}>
<AlertDialogBackdrop />
<AlertDialogContent>
<AlertDialogHeader>
<Heading>Return Policy</Heading>
<AlertDialogCloseButton>
<X
size={20}
className="stroke-background-400 group-[:hover]/alert-dialog-close-button:stroke-background-700 group-[:active]/alert-dialog-close-button:stroke-background-900 group-[:focus-visible]/alert-dialog-close-button:stroke-background-900"
/>
</AlertDialogCloseButton>
</AlertDialogHeader>
<AlertDialogBody>
<Text>
Whoa, slow down there! This modal is like a red light at an
intersection, reminding you to stop and think before you proceed.
Is deleting this folder the right choice?
</Text>
</AlertDialogBody>
<AlertDialogFooter className="gap-3">
<Button variant="outline" action="secondary" onPress={handleClose}>
<ButtonText>Cancel</ButtonText>
</Button>
<Button action="negative" onPress={handleClose}>
<ButtonText>Delete</ButtonText>
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
};

@@ -117,7 +61,6 @@ AlertDialogBasic.description =
export default AlertDialogBasic;

export {
FigmaAlertDialogStory,
AlertDialog,
AlertDialogBackdrop,
AlertDialogContent,
@@ -127,14 +70,6 @@ export {
AlertDialogBody,
Button,
ButtonText,
ButtonGroup,
Text,
CloseIcon,
Center,
Heading,
Icon,
AlertCircleIcon,
HStack,
AlertTriangleIcon,
CheckCircleIcon,
};
52 changes: 45 additions & 7 deletions example/storybook-nativewind/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
import React from 'react';
import { Button, ButtonText } from '@/components/ui/Button';
import {
Button,
ButtonText,
ButtonIcon,
ButtonSpinner,
} from '@/components/ui/Button';
import { EditIcon, ArrowLeftIcon } from 'lucide-react-native';
import {
Icon,
ArrowUpIcon,
AddIcon,
InfoIcon,
ThreeDotsIcon,
} from '@/components/ui/Icon';
import { Input, InputField } from '@/components/ui/Input';
import { HStack } from '@/components/ui/HStack';
import { VStack } from '@/components/ui/VStack';
import { Text } from '@/components/ui/Text';
import { Heading } from '@/components/ui/Heading';
import { Box } from '@/components/ui/Box';
import { Center } from '@/components/ui/Center';

export const ButtonBasic = (props: any) => {
return (
<>
<Button {...props}>
<ButtonText>Hello World 22</ButtonText>
</Button>
</>
<Button {...props}>
<ButtonText>Hello World 22</ButtonText>
</Button>
);
};

@@ -16,4 +34,24 @@ ButtonBasic.description =

export default ButtonBasic;

export { Button, ButtonText };
export {
Button,
ButtonText,
ButtonIcon,
ButtonSpinner,
EditIcon,
ArrowLeftIcon,
Icon,
ArrowUpIcon,
AddIcon,
InfoIcon,
ThreeDotsIcon,
Input,
InputField,
HStack,
VStack,
Text,
Heading,
Box,
Center,
};
298 changes: 134 additions & 164 deletions example/storybook-nativewind/src/components/Button/index.nw.stories.mdx

Large diffs are not rendered by default.

65 changes: 65 additions & 0 deletions example/storybook-nativewind/src/components/Card/BlogCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// import React from 'react';
// import {
// Card,
// Text,
// Box,
// VStack,
// Heading,
// Avatar,
// AvatarFallbackText,
// AvatarImage,
// } from '@gluestack-ui/themed';

// const BlogCard = () => {
// return (
// <Card p="$5" borderRadius="$lg" maxWidth={360}>
// <Text
// fontSize="$sm"
// fontStyle="normal"
// fontFamily="$heading"
// fontWeight="$normal"
// lineHeight="$sm"
// mb="$2"
// sx={{
// color: '$textLight700',
// _dark: {
// color: '$textDark200',
// },
// }}
// >
// May 15, 2023
// </Text>
// <VStack mb="$6">
// <Heading size="md" fontFamily="$heading" mb="$4">
// The Power of Positive Thinking
// </Heading>
// <Text size="sm" fontFamily="$heading">
// Discover how the power of positive thinking can transform your life,
// boost your confidence, and help you overcome challenges. Explore
// practical tips and techniques to cultivate a positive mindset for
// greater happiness and success.
// </Text>
// </VStack>
// <Box flexDirection="row">
// <Avatar mr="$3">
// <AvatarFallbackText fontFamily="$heading">RR</AvatarFallbackText>
// <AvatarImage
// source={{
// uri: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxzZWFyY2h8Mnx8dXNlcnxlbnwwfHwwfHw%3D&auto=format&fit=crop&w=800&q=60',
// }}
// />
// </Avatar>
// <VStack>
// <Heading size="sm" fontFamily="$heading" mb="$1">
// John Smith
// </Heading>
// <Text size="sm" fontFamily="$heading">
// Motivational Speaker
// </Text>
// </VStack>
// </Box>
// </Card>
// );
// };

// export default BlogCard;
38 changes: 38 additions & 0 deletions example/storybook-nativewind/src/components/Card/Card.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { ComponentMeta } from '@storybook/react-native';
import Card from './Card';
// import BlogCard from './BlogCard';
// import ProductCard from './ProductCard';
// import ImageCard from './ImageCard';
// import ProfileCard from './ProfileCard';

const CardMeta: ComponentMeta<typeof Card> = {
title: 'stories/Card',
component: Card,
// metaInfo is required for figma generation
// @ts-ignore
metaInfo: {
componentDescription: `A Card component serves as a visual container that groups related content and actions.`,
},
argTypes: {
size: {
control: 'select',
options: ['sm', 'md', 'lg'],
defaultValue: 'md',
},
variant: {
control: 'select',
options: ['elevated', 'outline', 'ghost', 'filled'],
defaultValue: 'elevated',
},
},
};

export default CardMeta;

export {
Card,
// BlogCard,
// ProductCard,
// ImageCard,
// ProfileCard
};
21 changes: 21 additions & 0 deletions example/storybook-nativewind/src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React from 'react';
import { Card } from '@/components/ui/Card';
import { Text } from '@/components/ui/Text';
import { Heading } from '@/components/ui/Heading';

const CardBasic = ({ ...props }: any) => {
return (
<Card {...props}>
<Heading size="md" className={'mb-1'}>
Quick Start
</Heading>
<Text size="sm">Start building your next project in minutes</Text>
</Card>
);
};

CardBasic.description =
'This is a basic Card component example. A Card component serves as a visual container that groups related content and actions.';

export default CardBasic;
export { Card, Heading, Text };
72 changes: 72 additions & 0 deletions example/storybook-nativewind/src/components/Card/ImageCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// import React from 'react';
// import {
// Card,
// Text,
// Heading,
// Image,
// Link,
// HStack,
// LinkText,
// Icon,
// ArrowRightIcon,
// } from '@gluestack-ui/themed';

// const ImageCard = () => {
// return (
// <Card p="$5" borderRadius="$lg" maxWidth={360}>
// <Image
// mb="$6"
// h={240}
// width="$full"
// borderRadius="$md"
// source={{
// uri: 'https://images.unsplash.com/photo-1529693662653-9d480530a697?q=80&w=2831&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
// }}
// />

// <Text
// fontSize="$sm"
// fontStyle="normal"
// fontFamily="$heading"
// fontWeight="$normal"
// lineHeight="$sm"
// mb="$2"
// sx={{
// color: '$textLight700',
// _dark: {
// color: '$textDark200',
// },
// }}
// >
// May 15, 2023
// </Text>
// <Heading size="md" fontFamily="$heading" mb="$4">
// The Power of Positive Thinking
// </Heading>
// <Link href="https://gluestack.io/" isExternal>
// <HStack alignItems="center">
// <LinkText
// size="sm"
// fontFamily="$heading"
// fontWeight="$semibold"
// color="$primary600"
// $dark-color="$primary300"
// textDecorationLine="none"
// >
// Read Blog
// </LinkText>
// <Icon
// as={ArrowRightIcon}
// size="sm"
// color="$primary600"
// mt="$0.5"
// ml="$0.5"
// $dark-color="$primary300"
// />
// </HStack>
// </Link>
// </Card>
// );
// };

// export default ImageCard;
101 changes: 101 additions & 0 deletions example/storybook-nativewind/src/components/Card/ProductCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// import React from 'react';
// import {
// Card,
// Text,
// VStack,
// Heading,
// Button,
// ButtonText,
// Box,
// Image,
// } from '@gluestack-ui/themed';

// const ProductCard = () => {
// return (
// <Card p="$5" borderRadius="$lg" maxWidth={360}>
// <Image
// mb="$6"
// h={240}
// width="$full"
// borderRadius="$md"
// source={{
// uri: 'https://images.unsplash.com/photo-1595231712325-9fedecef7575?w=800&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDJ8fHxlbnwwfHx8fHw%3D',
// }}
// />

// <Text
// fontSize="$sm"
// fontStyle="normal"
// fontFamily="$heading"
// fontWeight="$normal"
// lineHeight="$sm"
// mb="$2"
// sx={{
// color: '$textLight700',
// _dark: {
// color: '$textDark200',
// },
// }}
// >
// Fashion Clothing
// </Text>
// <VStack mb="$6">
// <Heading size="md" fontFamily="$heading" mb="$4">
// Cotton Kurta
// </Heading>
// <Text size="sm" fontFamily="$heading">
// Floral embroidered notch neck thread work cotton kurta in white and
// black.
// </Text>
// </VStack>
// <Box
// flexDirection="column"
// sx={{
// '@sm': {
// flexDirection: 'row',
// },
// }}
// >
// <Button
// px="$4"
// py="$2"
// fontFamily="$heading"
// mr="$0"
// mb="$3"
// sx={{
// '@sm': {
// mr: '$3',
// mb: '$0',
// flex: 1,
// },
// }}
// >
// <ButtonText size="sm">Add to cart</ButtonText>
// </Button>
// <Button
// px="$4"
// py="$2"
// variant="outline"
// fontFamily="$heading"
// borderColor="$borderLight300"
// $dark-borderColor="$backgroundDark600"
// sx={{
// '@sm': {
// flex: 1,
// },
// }}
// >
// <ButtonText
// size="sm"
// color="$textLight600"
// $dark-color="$textDark400"
// >
// Wishlist
// </ButtonText>
// </Button>
// </Box>
// </Card>
// );
// };

// export default ProductCard;
183 changes: 183 additions & 0 deletions example/storybook-nativewind/src/components/Card/ProfileCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// import React from 'react';
// import {
// Card,
// Text,
// Box,
// VStack,
// Heading,
// Avatar,
// AvatarFallbackText,
// AvatarImage,
// Image,
// Button,
// ButtonText,
// Divider,
// } from '@gluestack-ui/themed';

// const ProfileCard = () => {
// return (
// <Card p="$6" borderRadius="$lg" maxWidth={360}>
// <Box flexDirection="row">
// <Avatar mr="$4">
// <AvatarFallbackText fontFamily="$heading">JD</AvatarFallbackText>
// <AvatarImage
// source={{
// uri: 'https://images.unsplash.com/photo-1620403724159-40363e84a155?q=80&w=2646&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
// }}
// />
// </Avatar>
// <VStack>
// <Heading size="md" fontFamily="$heading" mb="$1">
// Jane Doe
// </Heading>
// <Text size="sm" fontFamily="$heading">
// janedoe@sample.com
// </Text>
// </VStack>
// </Box>
// <Box
// my="$5"
// sx={{
// 'flexDirection': 'column',
// '@sm': {
// my: '$6',
// flexDirection: 'row',
// },
// }}
// >
// <VStack
// alignItems="center"
// sx={{
// 'pb': '$2',
// '@sm': {
// flex: 1,
// pb: '$0',
// borderRightWidth: 1,
// borderColor: '$backgroundLight300',
// _dark: {
// borderRightColor: '$backgroundDark800',
// },
// },
// }}
// >
// <Heading size="xs" fontFamily="$heading">
// 81
// </Heading>
// <Text size="xs">posts</Text>
// </VStack>
// <Divider
// orientation="horizontal"
// width="40%"
// alignSelf="center"
// sx={{
// 'bg': '$backgroundLight300',
// 'display': 'flex',
// '_dark': {
// bg: '$backgroundDark800',
// },
// '@sm': {
// display: 'none',
// },
// }}
// />
// <VStack
// alignItems="center"
// sx={{
// 'py': '$2',
// '@sm': {
// flex: 1,
// py: '$0',
// borderRightWidth: 1,
// borderColor: '$backgroundLight300',
// _dark: {
// borderRightColor: '$backgroundDark800',
// },
// },
// }}
// >
// <Heading size="xs" fontFamily="$heading">
// 5,281
// </Heading>
// <Text size="xs">followers</Text>
// </VStack>
// <Divider
// orientation="horizontal"
// width="40%"
// alignSelf="center"
// sx={{
// 'bg': '$backgroundLight300',
// 'display': 'flex',
// '_dark': {
// bg: '$backgroundDark800',
// },
// '@sm': {
// display: 'none',
// },
// }}
// />
// <VStack
// alignItems="center"
// sx={{
// 'pt': '$2',
// '@sm': {
// flex: 1,
// pt: '$0',
// },
// }}
// >
// <Heading size="xs" fontFamily="$heading">
// 281
// </Heading>
// <Text size="xs">following</Text>
// </VStack>
// </Box>
// <Box
// mb="$5"
// sx={{
// 'flexDirection': 'column',
// '@sm': {
// mb: '$6',
// flexDirection: 'row',
// },
// }}
// >
// <Image
// mb="$3"
// borderRadius="$md"
// sx={{
// 'width': '$full',
// 'height': 140,
// '@sm': {
// mb: '$0',
// mr: '$3',
// width: 150,
// height: 154,
// },
// }}
// source={{
// uri: 'https://images.unsplash.com/photo-1592089416462-2b0cb7da8379?q=80&w=2865&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
// }}
// />
// <Image
// borderRadius="$md"
// sx={{
// 'width': '$full',
// 'height': 140,
// '@sm': {
// width: 150,
// height: 154,
// },
// }}
// source={{
// uri: 'https://images.unsplash.com/photo-1484406566174-9da000fda645?q=80&w=2425&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
// }}
// />
// </Box>
// <Button py="$2" px="$4">
// <ButtonText size="sm">Follow</ButtonText>
// </Button>
// </Card>
// );
// };

// export default ProfileCard;
667 changes: 667 additions & 0 deletions example/storybook-nativewind/src/components/Card/index.nw.stories.mdx

Large diffs are not rendered by default.

700 changes: 700 additions & 0 deletions example/storybook-nativewind/src/components/Card/index.stories.mdx

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -45,7 +45,6 @@ import {
Icon,
CircleIcon,
CheckIcon,
AlertCircleIcon,
ChevronDownIcon,
} from '@gluestack-ui/themed';
import {
@@ -58,6 +57,7 @@ import {
FormControlErrorIcon,
FormControlErrorText,
} from '@/components/ui/FormControl';
import { AlertCircleIcon } from 'lucide-react-native';

import { Input, InputField } from '@/components/ui/Input';
const FormControlBasic = ({ ...props }: any) => {
Original file line number Diff line number Diff line change
@@ -1,21 +1,390 @@
---
title: FormControl | gluestack-ui | Installation, Usage, and API
title: gluestack-ui FormControl Component | Installation, Usage, and API

description: By using FormControl, developers can provide important context to form elements. This context can include whether the element is invalid, disabled, or required.

pageTitle: FormControl

pageDescription: By using FormControl, developers can provide important context to form elements. This context can include whether the element is invalid, disabled, or required.

showHeader: false

tag: coming soon
showHeader: true
---

import { Meta } from '@storybook/addon-docs';

<Meta title="with-nativewind/components/Forms/FormControl" />

# FormControl
import {
Icon,
CircleIcon,
CheckIcon,
} from '@gluestack-ui/themed';
import {
FormControl,
FormControlLabel,
FormControlLabelText,
FormControlHelper,
FormControlHelperText,
FormControlError,
FormControlErrorIcon,
FormControlErrorText,
Input,
InputField,
AlertCircleIcon,
} from './FormControl';
import { Center, Text as FormControlText, Heading } from '@gluestack-ui/themed';
import {
ChevronDownIcon,
Button,
ButtonText,
Radio,
RadioGroup,
RadioIcon,
RadioIndicator,
RadioLabel,
Checkbox,
CheckboxGroup,
CheckboxIndicator,
CheckboxIcon,
CheckboxLabel,
} from '@gluestack-ui/themed';
import {
Textarea,
TextareaInput,
Select,
SelectTrigger,
SelectInput,
SelectIcon,
SelectPortal,
SelectBackdrop,
SelectContent,
SelectDragIndicatorWrapper,
SelectDragIndicator,
SelectItem,
Switch,
} from '@gluestack-ui/themed';
import {
Slider,
SliderTrack,
SliderFilledTrack,
SliderThumb,
Modal,
ModalBackdrop,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
ModalFooter,
HStack,
VStack,
} from '@gluestack-ui/themed';
import { transformedCode } from '../../utils';
import {
AppProvider,
CodePreview,
Table,
TableContainer,
Text,
InlineCode,
} from '@gluestack/design-system';

import { useState } from 'react';

import Wrapper from '../../components-example/nativewind/Wrapper';
import { Box } from '../../components-example/nativewind/Box';
import { CollapsibleCode } from '@gluestack/design-system';

This is an illustration of **FormControl** component.

<Wrapper>
<CodePreview
showComponentRenderer={true}
showArgsController={true}
metaData={{
code: `
<Box className="h-32 w-72">
<FormControl {...props}>
<FormControlLabel className="mb-1">
<FormControlLabelText>Password</FormControlLabelText>
</FormControlLabel>
<Input>
<InputField
type="password"
defaultValue="12345"
placeholder="password"
/>
</Input>
<FormControlHelper>
<FormControlHelperText>
Must be at least 6 characters.
</FormControlHelperText>
</FormControlHelper>
<FormControlError>
<FormControlErrorIcon
as={AlertCircleIcon}
/>
<FormControlErrorText>
At least 6 characters are required.
</FormControlErrorText>
</FormControlError>
</FormControl>
</Box>
`,
transformCode: (code) => {
return transformedCode(code);
},
scope: {
Wrapper,
FormControl,
FormControlLabel,
FormControlLabelText,
FormControlHelper,
FormControlHelperText,
FormControlError,
FormControlErrorIcon,
FormControlErrorText,
Icon,
Input,
InputField,
AlertCircleIcon,
Box,
},
argsType: {
size: {
control: 'select',
options: ['sm', 'md', 'lg'],
default: 'md',
},
isDisabled: {
control: 'boolean',
default: false,
},
isInvalid: {
control: 'boolean',
default: false,
},
isReadOnly: {
control: 'boolean',
default: false,
},
isRequired: {
control: 'boolean',
default: false,
},
},
}}
/>
</Wrapper>

<br />

## Installation

### Step 1: Install the following dependencies:

```bash

npm i @gluestack-ui/form-control

```

### Step 2: Copy and paste the following code into your project.

<CollapsibleCode>
```jsx
%%-- File: components-example/nativewind/FormControl/index.tsx --%%
```
</CollapsibleCode>

### Step 3: Update the import paths to match your project setup.

## API Reference

To use this component in your project, include the following import statement in your file.

```jsx
import { FormControl } from '@/components/ui/FormControl';
```

```jsx
export default () => (
<FormControl>
<FormControlLabel>
<FormControlLabelText />
</FormControlLabel>
<FormControlHelper>
<FormControlHelperText />
</FormControlHelper>
<FormControlError>
<FormControlErrorIcon />
<FormControlErrorText />
</FormControlError>
</FormControl>
);
```

### Component Props

This section provides a comprehensive reference list for the component props, detailing descriptions, properties, types, and default behavior for easy project integration.

#### FormControl

It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.

<Wrapper>
<TableContainer>
<Table>
<Table.THead>
<Table.TR>
<Table.TH>
<Table.TText>Prop</Table.TText>
</Table.TH>
<Table.TH>
<Table.TText>Type</Table.TText>
</Table.TH>
<Table.TH>
<Table.TText>Default</Table.TText>
</Table.TH>
<Table.TH>
<Table.TText>Description</Table.TText>
</Table.TH>
</Table.TR>
</Table.THead>
<Table.TBody>
<Table.TR>
<Table.TD>
<Table.TText>
<InlineCode>isInvalid</InlineCode>
</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>bool</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>false</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>{`When true, invalid state.`}</Table.TText>
</Table.TD>
</Table.TR>
<Table.TR>
<Table.TD>
<Table.TText>
<InlineCode>isRequired</InlineCode>
</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>bool</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>false</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>{`If true, astrick gets activated.`}</Table.TText>
</Table.TD>
</Table.TR>
<Table.TR>
<Table.TD>
<Table.TText>
<InlineCode>isDisabled</InlineCode>
</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>bool</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>false</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>{`Disabled state true.`}</Table.TText>
</Table.TD>
</Table.TR>
<Table.TR>
<Table.TD>
<Table.TText>
<InlineCode>isReadOnly</InlineCode>
</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>bool</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>false</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>{`To manually set read-only state.`}</Table.TText>
</Table.TD>
</Table.TR>
<Table.TR>
<Table.TD>
<Table.TText>
<InlineCode>isDisabled</InlineCode>
</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>bool</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>false</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>{`To manually set disable to the FormControl.`}</Table.TText>
</Table.TD>
</Table.TR>
</Table.TBody>
</Table>
</TableContainer>
</Wrapper>

#### FormControlLabel

It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.

#### FormControlLabelText

It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.

#### FormControlHelper

It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.

#### FormControlHelperText

It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.

#### FormControlError

It inherits all the properties of React Native's [View](https://reactnative.dev/docs/view) component.

#### FormControlErrorIcon

It inherits all the properties of gluestack Style's [AsForwarder](/style/docs/api/as-forwarder) component.

#### FormControlErrorText

It inherits all the properties of React Native's [Text](https://reactnative.dev/docs/text) component.

### Features

- Keyboard support for actions.
- Support for hover, focus and active states.
- Option to add your styles or use the default styles.

### Props

FormControl component is created using View component from react-native. It extends all the props supported by [React Native View](https://reactnative.dev/docs/view#props).

<!--
## Spec Doc

Explore the comprehensive details of the Input in this document, including its implementation details, checklist, and potential future additions. Dive into the thought process behind the component and gain insights into its development journey.

Coming Soon!
<iframe
style={{
borderRadius: '8px',
border: ' 1px solid rgba(0, 0, 0, 0.1)',
aspectRatio: 736 / 585,
}}
src="https://www.figma.com/embed?embed_host=share&url=https%3A%2F%2Fwww.figma.com%2Fproto%2FNcXOxqKbdnGsLQNex3H76u%2F%25C2%25A0%25F0%259F%2593%259Agluestack-UI-handbook%3Ftype%3Ddesign%26node-id%3D5030-22048%26t%3DIS25mxEIlIP9OacJ-1%26scaling%3Dscale-down%26page-id%3D5030%253A19919%26starting-point-node-id%3D5030%253A22048%26mode%3Ddesign"
allowfullscreen
></iframe> -->

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: FormControl | gluestack-ui | Installation, Usage, and API
title: gluestack-ui FormControl Component | Installation, Usage, and API

description: By using FormControl, developers can provide important context to form elements. This context can include whether the element is invalid, disabled, or required.

@@ -72,7 +72,7 @@ import {
Icon,
Button,
ButtonText,
} from '../../components-example/themed';
} from './FormControl';
import {
AppProvider,
CodePreview,
@@ -192,7 +192,7 @@ npm i @gluestack-ui/form-control

To use this component in your project, include the following import statement in your file.

```bash
```jsx
import { FormControl } from '@/components/ui/FormControl';
```

Loading

0 comments on commit 703404f

Please sign in to comment.