@shiplypalestine/ui v1.0.99
Shiply UI Library
A comprehensive React Native UI component library with a modern design system.
Features
- 🎨 Theme support (Light/Dark mode)
- 🌐 RTL (Right-to-Left) support
- 📱 Cross-platform (iOS, Android, Web)
- 🎯 TypeScript support
- 🎨 NativeWind for styling
- 🔧 Customizable components
Installation
npm install @shiplypalestine/ui
# or
yarn add @shiplypalestine/ui
Required Setup
- Install peer dependencies:
npm install nativewind@^2.0.11 tailwindcss@^3.3.2 postcss@^8.4.23 autoprefixer@^10.4.14 @react-native-async-storage/async-storage
# or
yarn add nativewind@^2.0.11 tailwindcss@^3.3.2 postcss@^8.4.23 autoprefixer@^10.4.14 @react-native-async-storage/async-storage
- For React Native CLI projects, run:
npx pod-install ios
- For Expo projects, run:
npx expo prebuild
- Configure your
tailwind.config.js
:
module.exports = {
content: [
"./App.{js,jsx,ts,tsx}",
"./src/**/*.{js,jsx,ts,tsx}",
"./node_modules/@shiplypalestine/ui/**/*.{js,jsx,ts,tsx}"
],
theme: {
extend: {
colors: {
primary: {
DEFAULT: '#007AFF',
dark: '#0056B3',
},
secondary: {
DEFAULT: '#5856D6',
dark: '#3634A3',
},
accent: {
DEFAULT: '#FF2D55',
dark: '#D6002F',
},
background: {
light: '#FFFFFF',
dark: '#121212',
},
text: {
light: '#000000',
dark: '#FFFFFF',
},
},
},
},
plugins: [],
}
- Add the following to your
babel.config.js
:
module.exports = {
plugins: ["nativewind/babel"],
};
Usage
Theme Provider
Wrap your app with the ThemeProvider to enable theming and RTL support:
import { ThemeProvider } from '@shiplypalestine/ui';
import { SafeAreaProvider } from 'react-native-safe-area-context';
export default function App() {
return (
<SafeAreaProvider>
<ThemeProvider>
{/* Your app content */}
</ThemeProvider>
</SafeAreaProvider>
);
}
Note on Safe Area:
Components like Toast
use SafeAreaView
internally to avoid rendering under device notches or home indicators. For SafeAreaView
to work correctly, your entire application must be wrapped with a single <SafeAreaProvider>
at its root. Make sure you have react-native-safe-area-context
installed and configured as shown in the example above.
Components
Import and use components as needed:
import { Button, Input, Card, Modal, Select } from '@shiplypalestine/ui';
export function MyComponent() {
const [isModalVisible, setIsModalVisible] = useState(false);
const [inputValue, setInputValue] = useState('');
const [selectedOption, setSelectedOption] = useState('');
return (
<View style={{ padding: 16 }}>
{/* Button Component */}
<Button
title="Click Me"
onPress={() => setIsModalVisible(true)}
variant="primary"
size="medium"
/>
{/* Input Component */}
<Input
value={inputValue}
onChangeText={setInputValue}
placeholder="Enter text..."
label="Example Input"
/>
{/* Card Component */}
<Card title="Example Card">
<Text>This is a card component</Text>
</Card>
{/* Select Component */}
<Select
options={[
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
{ label: 'Option 3', value: '3' },
]}
onSelect={(value) => setSelectedOption(value)}
placeholder="Select an option"
/>
{/* Modal Component */}
<Modal
visible={isModalVisible}
onClose={() => setIsModalVisible(false)}
title="Example Modal"
>
<Text>This is a modal component</Text>
<Button
title="Close"
onPress={() => setIsModalVisible(false)}
variant="secondary"
/>
</Modal>
</View>
);
}
Style Customization
All components support both NativeWind classes and StyleSheet props for customization. This allows you to override default styles using either approach:
Using StyleSheet Props
import { Button } from '@shiplypalestine/ui';
import { StyleSheet } from 'react-native';
export function MyComponent() {
return (
<Button
title="Custom Button"
onPress={() => {}}
style={styles.button}
textStyle={styles.buttonText}
/>
);
}
const styles = StyleSheet.create({
button: {
backgroundColor: 'purple',
borderRadius: 20,
},
buttonText: {
fontSize: 18,
fontWeight: 'bold',
},
});
Using NativeWind Classes
import { Button } from '@shiplypalestine/ui';
export function MyComponent() {
return (
<Button
title="Custom Button"
onPress={() => {}}
className="bg-purple-500 rounded-full"
textClassName="text-lg font-bold"
/>
);
}
Available Components
Button
A customizable button component with multiple variants, states, and icon support.
Props:
title
: string (required)onPress
: () => void (required)variant
: 'primary' | 'secondary' | 'outline' (optional)size
: 'small' | 'medium' | 'large' (optional)disabled
: boolean (optional)loading
: boolean (optional)startIcon
: React.ReactNode (optional) - Icon to display before textendIcon
: React.ReactNode (optional) - Icon to display after texticonSpacing
: number (optional) - Space between icon and text (default: 8)style
: ViewStyle (optional)textStyle
: TextStyle (optional)className
: string (optional)textClassName
: string (optional)
Example usage:
import { Button } from '@shiplypalestine/ui';
// Basic usage
<Button title="Click Me" onPress={() => {}} />
// With icons
<Button
title="Next"
onPress={() => {}}
endIcon={<MyIcon name="arrow-right" />}
/>
<Button
title="Back"
onPress={() => {}}
startIcon={<MyIcon name="arrow-left" />}
/>
// With custom icon spacing
<Button
title="Download"
onPress={() => {}}
startIcon={<MyIcon name="download" />}
iconSpacing={12}
/>
Input
A customizable input component with label and error support.
Props:
label
: string (optional)error
: string (optional)disabled
: boolean (optional)multiline
: boolean (optional)containerStyle
: ViewStyle (optional)inputStyle
: TextStyle (optional)labelStyle
: TextStyle (optional)errorStyle
: TextStyle (optional)containerClassName
: string (optional)inputClassName
: string (optional)labelClassName
: string (optional)errorClassName
: string (optional)startComponent
: ReactNode (optional) - Component to display at the start of the inputstartComponentContainerStyle
: ViewStyle (optional) - Style for the startComponent containerstartComponentContainerClassName
: string (optional) - Classes for the startComponent containerforceRTL
: boolean (optional) - Force right-to-left text directionforceLTR
: boolean (optional) - Force left-to-right text direction- All other TextInput props are supported
Example usage:
import { Input } from '@shiplypalestine/ui';
// Basic usage
<Input placeholder="Enter text" />
// With label and error
<Input
label="Email"
error="Invalid email"
placeholder="Enter your email"
/>
// With start component (icon or other element)
<Input
label="Search"
placeholder="Search..."
startComponent={<SearchIcon size={20} color="#007AFF" />}
/>
// With start component in RTL mode
<Input
label="بحث"
placeholder="أدخل كلمة البحث..."
forceRTL={true}
startComponent={<SearchIcon size={20} color="#007AFF" />}
/>
// With custom styles
<Input
placeholder="Custom input"
containerStyle={{ marginBottom: 20 }}
inputStyle={{ fontSize: 16 }}
labelStyle={{ color: 'blue' }}
containerClassName="bg-gray-100"
inputClassName="border-2"
/>
// With styled start component
<Input
label="Username"
placeholder="Enter username"
startComponent={
<Text style={{ color: '#5856D6', fontWeight: 'bold' }}>@</Text>
}
startComponentContainerClassName="bg-gray-100 h-full rounded-l-lg border-r"
/>
// Disabled input
<Input
placeholder="Disabled input"
disabled
/>
// Multiline input
<Input
placeholder="Enter multiple lines"
multiline
/>
Card
A customizable card component with title and content.
Props:
title
: string (optional)children
: React.ReactNode (required)variant
: 'elevated' | 'outlined' | 'filled' (optional)padding
: 'none' | 'small' | 'medium' | 'large' (optional)onPress
: () => void (optional)style
: ViewStyle (optional)titleStyle
: TextStyle (optional)className
: string (optional)titleClassName
: string (optional)
Modal
A customizable modal component with title and close button.
Props:
visible
: boolean (required)onClose
: () => void (required)title
: string (optional)children
: React.ReactNode (required)showCloseButton
: boolean (optional)animationType
: 'fade' | 'slide' (optional)containerStyle
: ViewStyle (optional)contentStyle
: ViewStyle (optional)titleStyle
: TextStyle (optional)closeButtonStyle
: ViewStyle (optional)closeButtonTextStyle
: TextStyle (optional)containerClassName
: string (optional)contentClassName
: string (optional)titleClassName
: string (optional)closeButtonClassName
: string (optional)closeButtonTextClassName
: string (optional)
Select
A customizable select component with dropdown options.
Props:
options
: Array<{ label: string, value: string }> (required)onSelect
: (value: string) => void (required)placeholder
: string (optional)disabled
: boolean (optional)value
: string (optional) - Initial selected valuecontainerStyle
: ViewStyle (optional)triggerStyle
: ViewStyle (optional)triggerTextStyle
: TextStyle (optional)dropdownStyle
: ViewStyle (optional)optionStyle
: ViewStyle (optional)optionTextStyle
: TextStyle (optional)containerClassName
: string (optional)triggerClassName
: string (optional)triggerTextClassName
: string (optional)dropdownClassName
: string (optional)optionClassName
: string (optional)optionTextClassName
: string (optional)
Radio & RadioGroup
Customizable radio components with group support.
Props for Radio:
value
: string (required)label
: string (required)selected
: boolean (required)onSelect
: (value: string) => void (required)disabled
: boolean (optional)containerStyle
: ViewStyle (optional)radioStyle
: ViewStyle (optional)labelStyle
: TextStyle (optional)containerClassName
: string (optional)radioClassName
: string (optional)labelClassName
: string (optional)
Props for RadioGroup:
value
: string (required)onChange
: (value: string) => void (required)children
: React.ReactNode (required)containerStyle
: ViewStyle (optional)containerClassName
: string (optional)
Checkbox & CheckboxGroup
Customizable checkbox components with group support.
Props for Checkbox:
value
: string (required)label
: string (required)checked
: boolean (required)onCheck
: (values: string[]) => void (required)disabled
: boolean (optional)containerStyle
: ViewStyle (optional)checkboxStyle
: ViewStyle (optional)labelStyle
: TextStyle (optional)checkmarkStyle
: TextStyle (optional)containerClassName
: string (optional)checkboxClassName
: string (optional)labelClassName
: string (optional)checkmarkClassName
: string (optional)
Props for CheckboxGroup:
value
: stringonChange
: (values: string[]) => void (required)children
: React.ReactNode (required)containerStyle
: ViewStyle (optional)containerClassName
: string (optional)
Progress
A customizable progress component.
Props:
value
: number (required)max
: number (optional)variant
: 'linear' | 'circular' (optional)color
: 'primary' | 'secondary' | 'accent' (optional)size
: 'small' | 'medium' | 'large' (optional)showValue
: boolean (optional)containerStyle
: ViewStyle (optional)trackStyle
: ViewStyle (optional)progressStyle
: ViewStyle (optional)valueStyle
: TextStyle (optional)containerClassName
: string (optional)trackClassName
: string (optional)progressClassName
: string (optional)valueClassName
: string (optional)
Spinner
A customizable spinner component.
Props:
size
: 'small' | 'medium' | 'large' (optional)color
: 'primary' | 'secondary' | 'accent' | 'white' (optional)fullScreen
: boolean (optional)containerStyle
: ViewStyle (optional)spinnerStyle
: ViewStyle (optional)containerClassName
: string (optional)spinnerClassName
: string (optional)
Switch
A customizable switch component.
Props:
value
: boolean (required)onValueChange
: (value: boolean) => void (required)disabled
: boolean (optional)style
: ViewStyle (optional)className
: string (optional)trackColorFalse
: string (optional)trackColorTrue
: string (optional)thumbColor
: string (optional)
Toast
A flexible toast notification system that supports multiple usage patterns: context-based, hook-based, and direct function calls.
Usage Patterns
- Using the
useToast
hook (Recommended):
import { useToast } from '@shiplypalestine/ui';
function MyComponent() {
const { showToast } = useToast();
const handleSuccess = () => {
showToast({
message: 'Operation completed successfully!',
type: 'success',
duration: 3000,
position: 'top'
});
};
return (
<Button
title="Show Success Toast"
onPress={handleSuccess}
/>
);
}
- Using the
ToastProvider
:
import { ToastProvider } from '@shiplypalestine/ui';
function App() {
return (
<ToastProvider>
{/* Your app content */}
</ToastProvider>
);
}
- Using the direct
showToast
function (Deprecated):
import { showToast } from '@shiplypalestine/ui';
function MyComponent() {
const handleError = () => {
showToast({
message: 'An error occurred',
type: 'error'
});
};
return (
<Button
title="Show Error Toast"
onPress={handleError}
/>
);
}
Toast Options
The showToast
function accepts the following options:
message
: string (required) - The message to displaytype
: 'success' | 'error' | 'info' | 'warning' (optional) - The type of toastduration
: number (optional) - Duration in milliseconds (default: 3000)position
: 'top' | 'bottom' (optional) - Position of the toast (default: 'top')
Toast Types
success
: Green background, used for successful operationserror
: Red background, used for error messageswarning
: Yellow background, used for warningsinfo
: Blue background, used for general information
Example Usage
import { useToast } from '@shiplypalestine/ui';
function MyComponent() {
const { showToast } = useToast();
// Success toast
const handleSuccess = () => {
showToast({
message: 'Data saved successfully!',
type: 'success'
});
};
// Error toast
const handleError = () => {
showToast({
message: 'Failed to save data',
type: 'error'
});
};
// Warning toast
const handleWarning = () => {
showToast({
message: 'Please fill all required fields',
type: 'warning'
});
};
// Info toast
const handleInfo = () => {
showToast({
message: 'New features available',
type: 'info'
});
};
// Custom duration and position
const handleCustom = () => {
showToast({
message: 'Custom toast',
type: 'success',
duration: 5000,
position: 'bottom'
});
};
return (
<View>
<Button title="Success" onPress={handleSuccess} />
<Button title="Error" onPress={handleError} />
<Button title="Warning" onPress={handleWarning} />
<Button title="Info" onPress={handleInfo} />
<Button title="Custom" onPress={handleCustom} />
</View>
);
}
Typography
A flexible text component that automatically handles text wrapping and supports various text styles.
import { Typography } from '@shiplypalestine/ui';
// Basic usage with automatic text wrapping
<Typography>
This text will automatically wrap when it reaches the end of its container.
</Typography>
// Different variants
<Typography variant="body">Body text</Typography>
<Typography variant="subtitle">Subtitle text</Typography>
<Typography variant="caption">Caption text</Typography>
<Typography variant="overline">OVERLINE TEXT</Typography>
// Different weights
<Typography weight="normal">Normal weight</Typography>
<Typography weight="medium">Medium weight</Typography>
<Typography weight="semibold">Semibold weight</Typography>
<Typography weight="bold">Bold weight</Typography>
// Text truncation with ellipsis
<Typography numberOfLines={2}>
This text will be truncated after 2 lines with an ellipsis.
</Typography>
// Different colors
<Typography color="primary">Primary color</Typography>
<Typography color="secondary">Secondary color</Typography>
<Typography color="accent">Accent color</Typography>
<Typography color="muted">Muted color</Typography>
Props
variant
: 'body' | 'caption' | 'overline' | 'subtitle' (default: 'body')weight
: 'normal' | 'medium' | 'semibold' | 'bold' (default: 'normal')color
: 'primary' | 'secondary' | 'accent' | 'default' | 'muted' | 'white' (default: 'default')align
: 'left' | 'center' | 'right' (default: 'left')numberOfLines
: number - Limits text to specified number of lines with ellipsisellipsizeMode
: 'head' | 'middle' | 'tail' | 'clip' (default: 'tail')style
: Custom styles for the textclassName
: NativeWind classes
Heading
A customizable heading component for titles and section headers.
Props:
children
: React.ReactNode (required)level
: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' (optional)color
: 'primary' | 'secondary' | 'accent' | 'default' | 'muted' | 'white' (optional)align
: 'left' | 'center' | 'right' (optional)style
: TextStyle (optional)className
: string (optional)
Accordion & AccordionItem
Customizable accordion components for expandable content sections.
Props for Accordion:
children
: React.ReactNode (required)containerStyle
: ViewStyle (optional)containerClassName
: string (optional)
Props for AccordionItem:
title
: string (required)children
: React.ReactNode (required)initiallyExpanded
: boolean (optional)containerStyle
: ViewStyle (optional)headerStyle
: ViewStyle (optional)contentStyle
: ViewStyle (optional)titleStyle
: TextStyle (optional)containerClassName
: string (optional)headerClassName
: string (optional)contentClassName
: string (optional)titleClassName
: string (optional)
IconButton
A customizable icon button component that accepts any React element as an icon.
Props:
icon
: React.ReactNode (required) - Any React element to display as the icononPress
: () => void (required)size
: 'small' | 'medium' | 'large' (optional)color
: 'primary' | 'secondary' | 'accent' | 'default' | 'muted' | 'white' (optional)variant
: 'solid' | 'outline' | 'ghost' (optional)disabled
: boolean (optional)loading
: boolean (optional)style
: ViewStyle (optional)className
: string (optional)
Example usage:
import { IconButton } from '@shiplypalestine/ui';
import { Feather } from '@expo/vector-icons';
// With Expo Icons
<IconButton
icon={<Feather name="heart" size={20} color="white" />}
onPress={() => {}}
color="primary"
/>
// With custom component
<IconButton
icon={<Text style={{ color: 'white', fontSize: 16 }}>⭐</Text>}
onPress={() => {}}
color="secondary"
/>
// With SVG icon
<IconButton
icon={<MySvgIcon width={20} height={20} color="white" />}
onPress={() => {}}
color="accent"
/>
// Different variants
<IconButton
icon={<Feather name="bell" size={20} color="white" />}
onPress={() => {}}
variant="solid"
color="primary"
/>
<IconButton
icon={<Feather name="bell" size={20} color="#007AFF" />}
onPress={() => {}}
variant="outline"
color="primary"
/>
FlatList & SectionList
Customizable list components with built-in empty state support and performance optimizations for large data sets.
Props for both FlatList and SectionList:
containerStyle
: ViewStyle (optional)contentContainerStyle
: ViewStyle (optional)containerClassName
: string (optional)contentContainerClassName
: string (optional)showEmptyState
: boolean (optional) - Default: trueemptyStateComponent
: React.ReactElement (optional)
Performance Props:
initialNumToRender
: number (optional) - Default: 10- Number of items to render in the initial batch
maxToRenderPerBatch
: number (optional) - Default: 10- Maximum number of items to render in each batch
windowSize
: number (optional) - Default: 5- Number of items to render outside the visible area
removeClippedSubviews
: boolean (optional) - Default: true- Remove items that are off-screen to save memory
updateCellsBatchingPeriod
: number (optional) - Default: 50- Time in ms between batch updates
onEndReachedThreshold
: number (optional) - Default: 0.5- How far from the end to trigger onEndReached
getItemLayout
: (data: any, index: number) => { length: number, offset: number, index: number } (optional)- Optimize rendering by providing item dimensions
Example usage with performance optimizations:
import { FlatList, SectionList } from '@shiplypalestine/ui';
// Optimized FlatList for large data sets
<FlatList
data={largeDataSet}
renderItem={({ item }) => (
<View className="p-2 bg-surface-light dark:bg-surface-dark rounded">
<Text className="text-text-light dark:text-text-dark">{item.title}</Text>
</View>
)}
keyExtractor={item => item.id}
// Performance optimizations
initialNumToRender={20}
maxToRenderPerBatch={20}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
onEndReachedThreshold={0.7}
getItemLayout={(data, index) => ({
length: 60, // Height of your item
offset: 60 * index,
index,
})}
/>
// Optimized SectionList for large data sets
<SectionList
sections={largeSectionData}
renderItem={({ item }) => (
<View className="p-2 bg-surface-light dark:bg-surface-dark rounded">
<Text className="text-text-light dark:text-text-dark">{item.title}</Text>
</View>
)}
renderSectionHeader={({ section }) => (
<View className="bg-primary-light dark:bg-primary-dark p-2 rounded-t">
<Text className="text-white font-bold">{section.title}</Text>
</View>
)}
keyExtractor={item => item.id}
// Performance optimizations
initialNumToRender={20}
maxToRenderPerBatch={20}
windowSize={10}
removeClippedSubviews={true}
updateCellsBatchingPeriod={100}
onEndReachedThreshold={0.7}
getItemLayout={(data, index) => ({
length: 60, // Height of your item
offset: 60 * index,
index,
})}
/>
Performance Tips:
1. Always provide getItemLayout
for fixed-height items
2. Use removeClippedSubviews
for large lists
3. Adjust windowSize
based on your list's complexity
4. Use initialNumToRender
and maxToRenderPerBatch
to control rendering batches
5. Consider using onEndReached
for infinite scrolling
6. Memoize your renderItem
function if it's complex
7. Use keyExtractor
with stable, unique keys
Box
A versatile container component that extends React Native's View with additional styling capabilities and layout features.
Props:
style
: ViewStyle (optional) - Custom stylesclassName
: string (optional) - NativeWind class namesbg
: string (optional) - Background colorrounded
: number | 'sm' | 'md' | 'lg' | 'full' (optional) - Border radiusborderWidth
: number (optional) - Border widthborderColor
: string (optional) - Border colorshadow
: object (optional) - Shadow propertiescolor
: string (optional)offset
: { width: number, height: number } (optional)opacity
: number (optional)radius
: number (optional)elevation
: number (optional)
flex
: number (optional) - Flex valueflexDirection
: 'row' | 'column' | 'row-reverse' | 'column-reverse' (optional)flexWrap
: 'wrap' | 'nowrap' | 'wrap-reverse' (optional)justifyContent
: 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly' (optional)alignItems
: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' (optional)alignSelf
: 'auto' | 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline' (optional)position
: 'absolute' | 'relative' (optional)top
: number (optional)right
: number (optional)bottom
: number (optional)left
: number (optional)p
: number (optional) - Paddingpx
: number (optional) - Horizontal paddingpy
: number (optional) - Vertical paddingpt
: number (optional) - Top paddingpr
: number (optional) - Right paddingpb
: number (optional) - Bottom paddingpl
: number (optional) - Left paddingm
: number (optional) - Marginmx
: number (optional) - Horizontal marginmy
: number (optional) - Vertical marginmt
: number (optional) - Top marginmr
: number (optional) - Right marginmb
: number (optional) - Bottom marginml
: number (optional) - Left marginw
: number | string (optional) - Widthh
: number | string (optional) - HeightminW
: number | string (optional) - Minimum widthminH
: number | string (optional) - Minimum heightmaxW
: number | string (optional) - Maximum widthmaxH
: number | string (optional) - Maximum heightoverflow
: 'visible' | 'hidden' | 'scroll' (optional)opacity
: number (optional)zIndex
: number (optional)transform
: ViewStyle'transform'
Example usage:
import { Box } from '@shiplypalestine/ui';
// Basic Box
<Box
bg="blue.500"
p={4}
rounded="md"
>
<Text className="text-white">Basic Box</Text>
</Box>
// Box with Shadow
<Box
bg="white"
p={4}
rounded="md"
shadow={{
color: '#000',
offset: { width: 0, height: 2 },
opacity: 0.25,
radius: 3.84,
elevation: 5,
}}
>
<Text>Box with shadow</Text>
</Box>
// Box with Flex Layout
<Box
flexDirection="row"
justifyContent="space-between"
alignItems="center"
p={4}
bg="gray.100"
rounded="md"
>
<Box bg="blue.500" p={2} rounded="sm">
<Text className="text-white">Item 1</Text>
</Box>
<Box bg="green.500" p={2} rounded="sm">
<Text className="text-white">Item 2</Text>
</Box>
<Box bg="red.500" p={2} rounded="sm">
<Text className="text-white">Item 3</Text>
</Box>
</Box>
// Box with Position
<Box
position="absolute"
top={10}
right={10}
bg="purple.500"
p={4}
rounded="full"
>
<Text className="text-white">Positioned Box</Text>
</Box>
// Box with Transform
<Box
bg="yellow.500"
p={4}
rounded="md"
transform={[{ rotate: '45deg' }]}
>
<Text>Rotated Box</Text>
</Box>
// Box with Overflow
<Box
h={100}
w={200}
overflow="scroll"
bg="gray.100"
p={4}
rounded="md"
>
<Text>Scrollable content</Text>
</Box>
CenterBox
A layout component that centers its children both horizontally and vertically.
import { CenterBox } from '@shiplypalestine/ui';
// Basic usage
<CenterBox>
<Text>Centered Content</Text>
</CenterBox>
// With flex
<CenterBox flex={1}>
<Text>Centered with flex</Text>
</CenterBox>
// Full-width and full-height
<CenterBox fullWidth fullHeight>
<Text>Centered in the entire container</Text>
</CenterBox>
// With NativeWind classes
<CenterBox className="bg-blue-100 p-4 rounded-lg">
<Text>Centered with NativeWind</Text>
</CenterBox>
Props
style
: Custom styles for the containerclassName
: NativeWind classesflex
: Flex value (default: 0)fullWidth
: Whether to take full width (default: false)fullHeight
: Whether to take full height (default: false)
Divider
A horizontal line component that can be used to separate content.
Props:
style
: Custom styles for the DividerclassName
: NativeWind classescolor
: Line color (defaults to theme border color)thickness
: Line thickness in pixels (default: 1)margin
: Vertical margin in pixels (default: 8)
Label
A status-based label component that displays text with a colored background.
Props:
text
: The text to display (required)status
: The status type ('success' | 'error' | 'warning' | 'info')style
: Custom styles for the containertextStyle
: Custom styles for the textclassName
: NativeWind classes for the containertextClassName
: NativeWind classes for the text
RTL Support
The Shiply UI Library includes comprehensive support for right-to-left (RTL) languages such as Arabic, Hebrew, and Persian. This allows you to create applications that cater to global audiences with proper text and layout direction.
Setting Up RTL Support
The library provides an RTLProvider
that automatically handles RTL detection based on the current language:
import { RTLProvider } from '@shiplypalestine/ui';
export default function App() {
return (
<RTLProvider>
{/* Your app content */}
</RTLProvider>
);
}
Using the RTL Hooks and Components
RTL Hook
Use the useRTL
hook to access RTL state and controls:
import { useRTL } from '@shiplypalestine/ui';
function MyComponent() {
const { isRTL, toggleRTL, setRTL } = useRTL();
return (
<View>
<Text>Current direction: {isRTL ? 'RTL' : 'LTR'}</Text>
<Button title="Toggle Direction" onPress={toggleRTL} />
<Button title="Set to RTL" onPress={() => setRTL(true)} />
<Button title="Set to LTR" onPress={() => setRTL(false)} />
</View>
);
}
RTLView Component
Use the RTLView
component to create containers that automatically handle text direction:
import { RTLView } from '@shiplypalestine/ui';
function MyComponent() {
return (
<RTLView>
<Text>This text will follow the current RTL setting</Text>
</RTLView>
);
}
You can also force a specific direction regardless of the global setting:
<RTLView forceLTR={true}>
<Text>This will always be left-to-right</Text>
</RTLView>
RTL-Aware Input Component
The Input
component includes built-in RTL support with properties to control text direction:
import { Input } from '@shiplypalestine/ui';
function MyForm() {
return (
<View>
{/* Automatically follows the global RTL setting */}
<Input label="Username" placeholder="Enter username" />
{/* Force RTL regardless of global setting */}
<Input
label="الاسم"
placeholder="أدخل الاسم"
forceRTL={true}
/>
{/* Force LTR regardless of global setting */}
<Input
label="Email"
placeholder="Enter email"
forceLTR={true}
/>
</View>
);
}
Language Selection
The library includes a LanguageSelector
component that integrates with the RTL system:
import { LanguageSelector } from '@shiplypalestine/ui';
function SettingsScreen() {
return (
<View>
<LanguageSelector
onLanguageChange={(language) => {
console.log(`Language changed to ${language}`);
// The RTL direction will automatically update based on the language
}}
/>
</View>
);
}
Best Practices for RTL Support
Use Relative Positioning: Always use
start
andend
instead ofleft
andright
in your styles.Avoid Hardcoded Directions: Don't hardcode text alignment or flex direction; use the RTL utilities.
Test with RTL Languages: Always test your UI with RTL languages to ensure proper alignment and visual formatting.
Accommodate Text Expansion: RTL languages often require more horizontal space, so design with flexibility.
Icons and Images: Make sure directional icons (arrows, back buttons) are flipped in RTL mode.
Tabs
The Tabs component provides a way to organize content into different views that can be switched between.
Basic Usage
import { Tabs } from '@shiplypalestine/ui';
function TabsExample() {
return (
<Tabs
tabs={[
{
key: 'tab1',
label: 'Tab 1',
content: <Text>Content for Tab 1</Text>,
},
{
key: 'tab2',
label: 'Tab 2',
content: <Text>Content for Tab 2</Text>,
},
{
key: 'tab3',
label: 'Tab 3',
content: <Text>Content for Tab 3</Text>,
},
]}
onTabChange={(tabKey) => console.log(`Tab changed to ${tabKey}`)}
/>
);
}
Tabs with Icons
import { Tabs } from '@shiplypalestine/ui';
import { HomeIcon, ProfileIcon, SettingsIcon } from './icons';
function TabsWithIconsExample() {
return (
<Tabs
tabs={[
{
key: 'home',
label: 'Home',
icon: <HomeIcon />,
content: <Text>Home content</Text>,
},
{
key: 'profile',
label: 'Profile',
icon: <ProfileIcon />,
content: <Text>Profile content</Text>,
},
{
key: 'settings',
label: 'Settings',
icon: <SettingsIcon />,
content: <Text>Settings content</Text>,
},
]}
/>
);
}
Custom Styled Tabs
import { Tabs } from '@shiplypalestine/ui';
function StyledTabsExample() {
return (
<Tabs
tabs={[
{
key: 'photos',
label: 'Photos',
content: <Text>Photos content</Text>,
},
{
key: 'videos',
label: 'Videos',
content: <Text>Videos content</Text>,
},
{
key: 'files',
label: 'Files',
content: <Text>Files content</Text>,
},
]}
tabsContainerClassName="bg-gray-100 dark:bg-gray-800 rounded-t-lg"
tabButtonClassName="flex-1"
activeTabButtonClassName="bg-white dark:bg-gray-700"
contentContainerClassName="bg-white dark:bg-gray-700 rounded-b-lg"
/>
);
}
Props
Tabs Component Props
Prop | Type | Description |
---|---|---|
tabs | TabItemProps[] | Array of tab items |
initialTabKey | string | Key of the initially active tab |
containerStyle | ViewStyle | Style for the main container |
tabsContainerStyle | ViewStyle | Style for the tabs container |
tabButtonStyle | ViewStyle | Style for the tab buttons |
activeTabButtonStyle | ViewStyle | Style for the active tab button |
tabTextStyle | TextStyle | Style for the tab text |
activeTabTextStyle | TextStyle | Style for the active tab text |
contentContainerStyle | ViewStyle | Style for the content container |
containerClassName | string | NativeWind class for the main container |
tabsContainerClassName | string | NativeWind class for the tabs container |
tabButtonClassName | string | NativeWind class for the tab buttons |
activeTabButtonClassName | string | NativeWind class for the active tab button |
tabTextClassName | string | NativeWind class for the tab text |
activeTabTextClassName | string | NativeWind class for the active tab text |
contentContainerClassName | string | NativeWind class for the content container |
onTabChange | (tabKey: string) => void | Callback fired when a tab is selected |
TabItemProps Interface
Property | Type | Description |
---|---|---|
key | string | Unique identifier for the tab |
label | string | Display label for the tab |
icon | ReactNode | Optional icon for the tab |
content | ReactNode | Content to be displayed when tab is active |
PhoneNumber
A component for international phone number input that combines country code selection with a phone number input field.
Props:
value
: string (required) - The current full phone number valueonChangeText
: (value: string) => void (required) - Callback for when the phone number changescountryCodes
: CountryCode - Array of country codes to select frominitialCountryCode
: string (optional) - Initial country code to selectlabel
: string (optional) - Label for the inputerror
: string (optional) - Error messageplaceholder
: string (optional) - Placeholder for the phone number inputrequired
: boolean (optional) - Whether the field is requireddisabled
: boolean (optional) - Whether the field is disabledcontainerStyle
: ViewStyle (optional) - Style for the containerinputStyle
: TextStyle (optional) - Style for the inputlabelStyle
: TextStyle (optional) - Style for the labelerrorStyle
: TextStyle (optional) - Style for the error messagedropdownStyle
: ViewStyle (optional) - Style for the dropdowncountryCodeStyle
: TextStyle (optional) - Style for the country code textcountryCodeContainerStyle
: ViewStyle (optional) - Style for the country code containercontainerClassName
: string (optional) - Class for the containerinputClassName
: string (optional) - Class for the inputlabelClassName
: string (optional) - Class for the labelerrorClassName
: string (optional) - Class for the error messagedropdownClassName
: string (optional) - Class for the dropdowncountryCodeClassName
: string (optional) - Class for the country code textcountryCodeContainerClassName
: string (optional) - Class for the country code containerforceRTL
: boolean (optional) - Force right-to-left text directionforceLTR
: boolean (optional) - Force left-to-right text direction
Example usage:
import { PhoneNumber, CountryCode } from '@shiplypalestine/ui';
// Define country codes
const countryCodes: CountryCode[] = [
{ code: '+1', name: 'United States', flag: '🇺🇸' },
{ code: '+44', name: 'United Kingdom', flag: '🇬🇧' },
{ code: '+33', name: 'France', flag: '🇫🇷' },
// Add more country codes...
];
// Basic usage
function PhoneNumberExample() {
const [phoneNumber, setPhoneNumber] = useState('');
return (
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
label="Phone Number"
placeholder="Enter phone number"
/>
);
}
// With initial country code
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
initialCountryCode="+44"
label="UK Phone Number"
placeholder="Enter UK phone number"
/>
// With error state
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
label="Phone Number"
error="Invalid phone number"
/>
// With RTL support
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
label="رقم الهاتف"
placeholder="أدخل رقم الهاتف"
forceRTL={true}
/>
// Required field
<PhoneNumber
value={phoneNumber}
onChangeText={setPhoneNumber}
countryCodes={countryCodes}
label="Phone Number"
required={true}
/>
CountryCode Interface
interface CountryCode {
code: string; // The country dial code, e.g., '+1'
name: string; // The country name, e.g., 'United States'
flag?: string; // Optional emoji flag, e.g., '🇺🇸'
}
Autocomplete
A high-performance autocomplete component that supports large datasets, API integration, and customizable styling.
Props
Prop | Type | Required | Description |
---|---|---|---|
value | string | Yes | The current input value |
onChangeText | (text: string) => void | Yes | Callback when the input text changes |
onSelect | (item: AutocompleteItem) => void | Yes | Callback when an item is selected |
fetchData | (query: string) => Promise<AutocompleteItem[]> | Yes | Function to fetch data based on the query |
label | string | No | Label text for the input |
placeholder | string | No | Placeholder text for the input |
error | string | No | Error message to display |
required | boolean | No | Whether the field is required |
disabled | boolean | No | Whether the input is disabled |
forceRTL | boolean | No | Force RTL layout |
containerClassName | string | No | Custom class for the container |
inputClassName | string | No | Custom class for the input |
listClassName | string | No | Custom class for the results list |
itemClassName | string | No | Custom class for list items |
AutocompleteItem Interface
interface AutocompleteItem {
id: string;
label: string;
value: string;
}
Example Usage
import { Autocomplete } from 'shiply-ui';
const MyComponent = () => {
const [value, setValue] = useState('');
const [selectedItem, setSelectedItem] = useState<AutocompleteItem | null>(null);
const fetchData = async (query: string): Promise<AutocompleteItem[]> => {
// Your API call here
const response = await fetch(`/api/search?q=${query}`);
return response.json();
};
return (
<Autocomplete
value={value}
onChangeText={setValue}
onSelect={setSelectedItem}
fetchData={fetchData}
label="Search"
placeholder="Type to search..."
/>
);
};
Features
- Debounced search to prevent excessive API calls
- Virtualized list for handling large datasets
- Loading states and error handling
- Keyboard navigation support
- RTL support
- Customizable styling
- Clear button with loading indicator
ActionSheet
A bottom sheet component that slides up from the bottom of the screen, commonly used for presenting a set of actions or options.
import { ActionSheet } from 'shiply-ui';
// Basic usage
<ActionSheet
visible={isVisible}
onClose={() => setIsVisible(false)}
title="Options"
>
<View>
<Text>Content goes here</Text>
</View>
</ActionSheet>
// With default search
<ActionSheet
visible={isVisible}
onClose={() => setIsVisible(false)}
title="Search Options"
showSearch
searchPlaceholder="Search items..."
onSearch={(query) => {
console.log('Searching for:', query);
}}
searchValue={searchQuery}
isLoading={isLoading}
searchInputProps={{
autoCapitalize: 'none',
autoCorrect: false,
}}
>
<View>
<Text>Search results go here</Text>
</View>
</ActionSheet>
// With custom search input
<ActionSheet
visible={isVisible}
onClose={() => setIsVisible(false)}
title="Custom Search"
showSearch
searchInputComponent={
<Input
placeholder="Search with custom input..."
value={searchQuery}
onChangeText={setSearchQuery}
placeholderType="search"
startComponent={
<View style={{ width: 20, height: 20, backgroundColor: 'blue', borderRadius: 10 }} />
}
/>
}
isLoading={isLoading}
>
<View>
<Text>Search results go here</Text>
</View>
</ActionSheet>
Props
Prop | Type | Default | Description |
---|---|---|---|
visible | boolean | false | Controls the visibility of the action sheet |
onClose | () => void | - | Callback function called when the action sheet is closed |
title | string | - | Optional title displayed at the top of the action sheet |
children | React.ReactNode | - | Content to be displayed in the action sheet |
showSearch | boolean | false | Whether to show the search input |
searchPlaceholder | string | 'Search...' | Placeholder text for the search input |
onSearch | (query: string) => void | - | Callback function called when the search query changes |
searchValue | string | '' | Current value of the search input |
isLoading | boolean | false | Whether to show a loading indicator |
searchInputProps | TextInputProps | - | Additional props to pass to the default search input |
searchInputComponent | React.ReactNode | - | Custom search input component to use instead of the default |
Skeleton
A flexible skeleton placeholder component for loading states. Supports shimmer animation, shape variants, and full style customization.
Note: You must install react-native-linear-gradient
in your project for shimmer animation to work:
npm install react-native-linear-gradient
# or
yarn add react-native-linear-gradient
Usage
import { Skeleton } from '@shiplypalestine/ui';
// Rectangle skeleton (default)
<Skeleton width={120} height={16} />
// Rounded skeleton
<Skeleton width={120} height={16} variant="rounded" />
// Circle skeleton (e.g. avatar)
<Skeleton width={40} height={40} variant="circle" />
// Custom border radius
<Skeleton width={100} height={20} borderRadius={8} />
// No animation
<Skeleton width={120} height={16} animation="none" />
// Full width skeleton (shimmer will be disabled if width is not a number)
<Skeleton width="100%" height={16} />
Props
Prop | Type | Default | Description |
---|---|---|---|
width | number | string | '100%' | Width of the skeleton |
height | number | string | 16 | Height of the skeleton |
borderRadius | number | - | Border radius (overrides variant) |
variant | 'rect' | 'circle' | 'rounded' | 'rect' | Shape of the skeleton |
style | ViewStyle | - | Custom style for the container |
className | string | - | NativeWind classes for the container |
animation | 'shimmer' | 'none' | 'shimmer' | Animation type |
- If
variant
iscircle
, the skeleton will be perfectly round. - If
variant
isrounded
, the skeleton will have a default border radius of 12 unless overridden. - If
width
is not a number, shimmer animation will be disabled and a warning will be shown in development.
Development
- Clone the repository
- Install dependencies:
npm install
- Start the development server: `
### Link
A customizable link component for navigation or actions. Supports custom styles and classes for both the container and text.
#### Usage
```tsx
import { Link } from '@shiplypalestine/ui';
// Basic usage
<Link onPress={() => {}}>Default Link</Link>
// With custom styles
<Link
onPress={() => {}}
style={{ margin: 8 }}
textStyle={{ color: 'red', fontWeight: 'bold' }}
>
Custom Styled Link
</Link>
// With NativeWind/Tailwind classes
<Link
onPress={() => {}}
className="rounded"
textClassName="text-lg underline text-green-600"
>
Tailwind Link
</Link>
Props
Prop | Type | Default | Description |
---|---|---|---|
onPress | () => void | required | Function to call when link is pressed |
children | React.ReactNode | required | Link content |
variant | 'default' | 'primary' | 'secondary' | 'default' | Color variant |
disabled | boolean | false | Disable the link |
underline | boolean | false | Underline the link text |
size | 'small' | 'medium' | 'large' | 'medium' | Text size |
style | ViewStyle | - | Custom style for the container |
textStyle | TextStyle | - | Custom style for the text |
className | string | - | NativeWind classes for the container |
textClassName | string | - | NativeWind classes for the text |
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago