diff --git a/apps/docs/pages/docs/v2/Components/spinner.en-US.mdx b/apps/docs/pages/docs/v2/Components/spinner.en-US.mdx new file mode 100644 index 00000000..9249aef3 --- /dev/null +++ b/apps/docs/pages/docs/v2/Components/spinner.en-US.mdx @@ -0,0 +1,44 @@ +--- +searchable: true +--- + +import { CodeEditor } from '@components/code-editor'; + +# Spinner + +Simple spinner component that is based on react native `ActivityIndicator` component. + +## Import + +```js +import { Spinner } from "@ficus-ui/native"; +import PropsTable from "@components/docs/props-table"; +``` + +## Usage + + + + + + + +`} /> + +## Props + +Extends every `Box` props and react native `ActivityIndicator` component. + +https://reactnative.dev/docs/activityindicator#props + +### `color` + + +### `size` + \ No newline at end of file diff --git a/apps/examples/app/components-v2/Spinner.tsx b/apps/examples/app/components-v2/Spinner.tsx new file mode 100644 index 00000000..aed4c108 --- /dev/null +++ b/apps/examples/app/components-v2/Spinner.tsx @@ -0,0 +1,29 @@ +import ExampleSection from "@/src/ExampleSection"; +import { Box, SafeAreaBox, Spinner, Spinner2, Text } from "@ficus-ui/native"; + + +const SpinnerComponent = () => { + return ( + + + Spinner component + + {/* + + + + + + */} + + + + + + + + + ); +}; + +export default SpinnerComponent; diff --git a/apps/examples/app/items-v2.ts b/apps/examples/app/items-v2.ts index b68bac8e..82b54bf5 100644 --- a/apps/examples/app/items-v2.ts +++ b/apps/examples/app/items-v2.ts @@ -10,6 +10,7 @@ import BadgeComponent from './components-v2/Badge'; import TouchableHighlightComponent from './components-v2/TouchableHighlight'; import TouchableOpacityComponent from './components-v2/TouchableOpacity'; import TouchableWithoutFeedbackComponent from './components-v2/TouchableWithoutFeedback'; +import SpinnerComponent from '@/app/components-v2/Spinner'; type ExampleComponentType = { onScreenName: string; @@ -29,4 +30,5 @@ export const components: ExampleComponentType[] = [ { navigationPath: 'TouchableHighlight', onScreenName: 'TouchableHighlight', component: TouchableHighlightComponent }, { navigationPath: 'TouchableOpacity', onScreenName: 'TouchableOpacity', component: TouchableOpacityComponent }, { navigationPath: 'TouchableWithoutFeedback', onScreenName: 'TouchableWithoutFeedback', component: TouchableWithoutFeedbackComponent }, + { navigationPath: 'Spinner', onScreenName: 'Spinner', component: SpinnerComponent }, ]; diff --git a/packages/components/src/index.ts b/packages/components/src/index.ts index 357e910e..66e8d2a5 100644 --- a/packages/components/src/index.ts +++ b/packages/components/src/index.ts @@ -7,5 +7,6 @@ export * from './safe-area-box'; export * from './center'; export * from './badge'; export * from './touchables'; +export * from './spinner'; export { ThemeProvider } from '@ficus-ui/theme'; diff --git a/packages/components/src/spinner/index.tsx b/packages/components/src/spinner/index.tsx new file mode 100644 index 00000000..831a9aae --- /dev/null +++ b/packages/components/src/spinner/index.tsx @@ -0,0 +1,53 @@ +import { useTheme } from '@ficus-ui/theme'; + +import { type NativeFicusProps, ficus } from '../system'; + +export interface SpinnerProps extends NativeFicusProps<'ActivityIndicator'> {} + +// export const Spinner = ficus('ActivityIndicator'); // something wrong with ficus making color return undefined but size still working correctly + +/** ... */ +export const Spinner = (props: SpinnerProps) => { + const { theme } = useTheme(); + const color = getThemeColor(theme.colors, props.color); + + return ; +}; + +export const getThemeColor = ( + themeColors: any, + value: string | any | undefined +): string => { + let colorValueResult: string | String = value as string; + + if (themeColors && value) { + // Check if color value is a valid theme color + if ( + themeColors.hasOwnProperty(value) && + (typeof themeColors[value] === 'string' || + themeColors[value] instanceof String) + ) { + const colorValue: string | String = themeColors[value] as string; + return colorValue as string; + } + + // If color value contains dots, check into theme sub objects if it's a valid theme color + if (value?.includes('.')) { + const keyParts = value.split('.'); + let subPropertyValue: any = themeColors; + for (const part of keyParts) { + if (subPropertyValue && part) { + subPropertyValue = subPropertyValue[part]; + } + } + if ( + typeof subPropertyValue === 'string' || + subPropertyValue instanceof String + ) { + colorValueResult = subPropertyValue; + } + } + } + + return colorValueResult as string; +}; diff --git a/packages/components/src/spinner/spinner.spec.tsx b/packages/components/src/spinner/spinner.spec.tsx new file mode 100644 index 00000000..2f586f64 --- /dev/null +++ b/packages/components/src/spinner/spinner.spec.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +import { render } from '@testing-library/react-native'; + +import { Spinner } from '.'; + +describe('ActivityIndicator Component', () => { + it('renders with default props', () => { + const { getByTestId } = render(); + expect(getByTestId('loading-indicator')).toBeTruthy(); + }); + + it('applies custom color', () => { + const { getByTestId } = render( + + ); + expect(getByTestId('loading-indicator').props.color).toBe('red'); + }); + + it('applies custom size', () => { + const { getByTestId } = render( + + ); + expect(getByTestId('loading-indicator').props.size).toBe('large'); + }); +}); diff --git a/packages/components/src/system/base-elements.ts b/packages/components/src/system/base-elements.ts index dd918d6e..dd4f720f 100644 --- a/packages/components/src/system/base-elements.ts +++ b/packages/components/src/system/base-elements.ts @@ -7,6 +7,7 @@ import { TouchableOpacity as RNTouchableOpacity, TouchableWithoutFeedback as RNTouchableWithoutFeedback, View as RNView, + ActivityIndicator as RNActivityIndicator, } from 'react-native'; /** @@ -21,6 +22,7 @@ export const baseRNElements = { TouchableHighlight: RNTouchableHighlight, TouchableOpacity: RNTouchableOpacity, TouchableWithoutFeedback: RNTouchableWithoutFeedback, + ActivityIndicator: RNActivityIndicator, } as const; export type BaseRNElements = keyof typeof baseRNElements;