1.0.99 • Published 1 month ago

@shiplypalestine/ui v1.0.99

Weekly downloads
-
License
-
Repository
-
Last release
1 month ago

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

  1. 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
  1. For React Native CLI projects, run:
npx pod-install ios
  1. For Expo projects, run:
npx expo prebuild
  1. 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: [],
}
  1. 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 text
  • endIcon: React.ReactNode (optional) - Icon to display after text
  • iconSpacing: 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 input
  • startComponentContainerStyle: ViewStyle (optional) - Style for the startComponent container
  • startComponentContainerClassName: string (optional) - Classes for the startComponent container
  • forceRTL: boolean (optional) - Force right-to-left text direction
  • forceLTR: 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 value
  • containerStyle: 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: string
  • onChange: (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

  1. 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} 
    />
  );
}
  1. Using the ToastProvider:
import { ToastProvider } from '@shiplypalestine/ui';

function App() {
  return (
    <ToastProvider>
      {/* Your app content */}
    </ToastProvider>
  );
}
  1. 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 display
  • type: 'success' | 'error' | 'info' | 'warning' (optional) - The type of toast
  • duration: 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 operations
  • error: Red background, used for error messages
  • warning: Yellow background, used for warnings
  • info: 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 ellipsis
  • ellipsizeMode: 'head' | 'middle' | 'tail' | 'clip' (default: 'tail')
  • style: Custom styles for the text
  • className: 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 icon
  • onPress: () => 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: true
  • emptyStateComponent: 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 styles
  • className: string (optional) - NativeWind class names
  • bg: string (optional) - Background color
  • rounded: number | 'sm' | 'md' | 'lg' | 'full' (optional) - Border radius
  • borderWidth: number (optional) - Border width
  • borderColor: string (optional) - Border color
  • shadow: object (optional) - Shadow properties
    • color: string (optional)
    • offset: { width: number, height: number } (optional)
    • opacity: number (optional)
    • radius: number (optional)
    • elevation: number (optional)
  • flex: number (optional) - Flex value
  • flexDirection: '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) - Padding
  • px: number (optional) - Horizontal padding
  • py: number (optional) - Vertical padding
  • pt: number (optional) - Top padding
  • pr: number (optional) - Right padding
  • pb: number (optional) - Bottom padding
  • pl: number (optional) - Left padding
  • m: number (optional) - Margin
  • mx: number (optional) - Horizontal margin
  • my: number (optional) - Vertical margin
  • mt: number (optional) - Top margin
  • mr: number (optional) - Right margin
  • mb: number (optional) - Bottom margin
  • ml: number (optional) - Left margin
  • w: number | string (optional) - Width
  • h: number | string (optional) - Height
  • minW: number | string (optional) - Minimum width
  • minH: number | string (optional) - Minimum height
  • maxW: number | string (optional) - Maximum width
  • maxH: number | string (optional) - Maximum height
  • overflow: '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 container
  • className: NativeWind classes
  • flex: 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 Divider
  • className: NativeWind classes
  • color: 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 container
  • textStyle: Custom styles for the text
  • className: NativeWind classes for the container
  • textClassName: 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

  1. Use Relative Positioning: Always use start and end instead of left and right in your styles.

  2. Avoid Hardcoded Directions: Don't hardcode text alignment or flex direction; use the RTL utilities.

  3. Test with RTL Languages: Always test your UI with RTL languages to ensure proper alignment and visual formatting.

  4. Accommodate Text Expansion: RTL languages often require more horizontal space, so design with flexibility.

  5. 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

PropTypeDescription
tabsTabItemProps[]Array of tab items
initialTabKeystringKey of the initially active tab
containerStyleViewStyleStyle for the main container
tabsContainerStyleViewStyleStyle for the tabs container
tabButtonStyleViewStyleStyle for the tab buttons
activeTabButtonStyleViewStyleStyle for the active tab button
tabTextStyleTextStyleStyle for the tab text
activeTabTextStyleTextStyleStyle for the active tab text
contentContainerStyleViewStyleStyle for the content container
containerClassNamestringNativeWind class for the main container
tabsContainerClassNamestringNativeWind class for the tabs container
tabButtonClassNamestringNativeWind class for the tab buttons
activeTabButtonClassNamestringNativeWind class for the active tab button
tabTextClassNamestringNativeWind class for the tab text
activeTabTextClassNamestringNativeWind class for the active tab text
contentContainerClassNamestringNativeWind class for the content container
onTabChange(tabKey: string) => voidCallback fired when a tab is selected

TabItemProps Interface

PropertyTypeDescription
keystringUnique identifier for the tab
labelstringDisplay label for the tab
iconReactNodeOptional icon for the tab
contentReactNodeContent 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 value
  • onChangeText: (value: string) => void (required) - Callback for when the phone number changes
  • countryCodes: CountryCode - Array of country codes to select from
  • initialCountryCode: string (optional) - Initial country code to select
  • label: string (optional) - Label for the input
  • error: string (optional) - Error message
  • placeholder: string (optional) - Placeholder for the phone number input
  • required: boolean (optional) - Whether the field is required
  • disabled: boolean (optional) - Whether the field is disabled
  • containerStyle: ViewStyle (optional) - Style for the container
  • inputStyle: TextStyle (optional) - Style for the input
  • labelStyle: TextStyle (optional) - Style for the label
  • errorStyle: TextStyle (optional) - Style for the error message
  • dropdownStyle: ViewStyle (optional) - Style for the dropdown
  • countryCodeStyle: TextStyle (optional) - Style for the country code text
  • countryCodeContainerStyle: ViewStyle (optional) - Style for the country code container
  • containerClassName: string (optional) - Class for the container
  • inputClassName: string (optional) - Class for the input
  • labelClassName: string (optional) - Class for the label
  • errorClassName: string (optional) - Class for the error message
  • dropdownClassName: string (optional) - Class for the dropdown
  • countryCodeClassName: string (optional) - Class for the country code text
  • countryCodeContainerClassName: string (optional) - Class for the country code container
  • forceRTL: boolean (optional) - Force right-to-left text direction
  • forceLTR: 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

PropTypeRequiredDescription
valuestringYesThe current input value
onChangeText(text: string) => voidYesCallback when the input text changes
onSelect(item: AutocompleteItem) => voidYesCallback when an item is selected
fetchData(query: string) => Promise<AutocompleteItem[]>YesFunction to fetch data based on the query
labelstringNoLabel text for the input
placeholderstringNoPlaceholder text for the input
errorstringNoError message to display
requiredbooleanNoWhether the field is required
disabledbooleanNoWhether the input is disabled
forceRTLbooleanNoForce RTL layout
containerClassNamestringNoCustom class for the container
inputClassNamestringNoCustom class for the input
listClassNamestringNoCustom class for the results list
itemClassNamestringNoCustom 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

PropTypeDefaultDescription
visiblebooleanfalseControls the visibility of the action sheet
onClose() => void-Callback function called when the action sheet is closed
titlestring-Optional title displayed at the top of the action sheet
childrenReact.ReactNode-Content to be displayed in the action sheet
showSearchbooleanfalseWhether to show the search input
searchPlaceholderstring'Search...'Placeholder text for the search input
onSearch(query: string) => void-Callback function called when the search query changes
searchValuestring''Current value of the search input
isLoadingbooleanfalseWhether to show a loading indicator
searchInputPropsTextInputProps-Additional props to pass to the default search input
searchInputComponentReact.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

PropTypeDefaultDescription
widthnumber | string'100%'Width of the skeleton
heightnumber | string16Height of the skeleton
borderRadiusnumber-Border radius (overrides variant)
variant'rect' | 'circle' | 'rounded''rect'Shape of the skeleton
styleViewStyle-Custom style for the container
classNamestring-NativeWind classes for the container
animation'shimmer' | 'none''shimmer'Animation type
  • If variant is circle, the skeleton will be perfectly round.
  • If variant is rounded, 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

  1. Clone the repository
  2. Install dependencies: npm install
  3. 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

PropTypeDefaultDescription
onPress() => voidrequiredFunction to call when link is pressed
childrenReact.ReactNoderequiredLink content
variant'default' | 'primary' | 'secondary''default'Color variant
disabledbooleanfalseDisable the link
underlinebooleanfalseUnderline the link text
size'small' | 'medium' | 'large''medium'Text size
styleViewStyle-Custom style for the container
textStyleTextStyle-Custom style for the text
classNamestring-NativeWind classes for the container
textClassNamestring-NativeWind classes for the text
1.0.99

1 month ago

1.0.97

1 month ago

1.0.96

1 month ago

1.0.95

1 month ago

1.0.94

1 month ago

1.0.93

1 month ago

1.0.92

1 month ago

1.0.91

1 month ago

1.0.90

1 month ago

1.0.89

1 month ago

1.0.88

1 month ago

1.0.87

1 month ago

1.0.86

1 month ago

1.0.85

1 month ago

1.0.84

1 month ago

1.0.83

1 month ago

1.0.82

1 month ago

1.0.81

1 month ago

1.0.80

1 month ago

1.0.79

1 month ago

1.0.78

1 month ago

1.0.77

2 months ago

1.0.76

2 months ago

1.0.75

2 months ago

1.0.74

2 months ago

1.0.73

2 months ago

1.0.72

2 months ago

1.0.71

2 months ago

1.0.70

2 months ago

1.0.68

2 months ago

1.0.67

2 months ago

1.0.66

2 months ago

1.0.65

2 months ago

1.0.64

2 months ago

1.0.63

2 months ago

1.0.62

2 months ago

1.0.61

2 months ago

1.0.60

2 months ago

1.0.59

2 months ago

1.0.58

2 months ago

1.0.57

2 months ago

1.0.56

2 months ago

1.0.55

3 months ago

1.0.54

3 months ago

1.0.53

3 months ago

1.0.52

3 months ago

1.0.51

3 months ago

1.0.50

3 months ago

1.0.49

3 months ago

1.0.48

3 months ago

1.0.47

3 months ago

1.0.46

3 months ago

1.0.45

3 months ago

1.0.44

3 months ago

1.0.43

3 months ago

1.0.42

3 months ago

1.0.41

3 months ago

1.0.40

3 months ago

1.0.39

3 months ago

1.0.37

3 months ago

1.0.36

3 months ago

1.0.35

3 months ago

1.0.34

3 months ago

1.0.33

3 months ago

1.0.32

3 months ago

1.0.31

3 months ago

1.0.30

3 months ago

1.0.29

3 months ago

1.0.28

3 months ago

1.0.27

3 months ago

1.0.26

3 months ago

1.0.25

3 months ago

1.0.24

3 months ago

1.0.23

3 months ago

1.0.22

3 months ago

1.0.21

3 months ago

1.0.20

3 months ago

1.0.19

3 months ago

1.0.18

3 months ago

1.0.16

4 months ago

1.0.15

4 months ago

1.0.14

4 months ago

1.0.13

4 months ago

1.0.11

4 months ago

1.0.9

4 months ago

1.0.8

4 months ago

1.0.7

4 months ago

1.0.6

4 months ago

1.0.5

4 months ago

1.0.4

4 months ago

1.0.3

4 months ago

1.0.2

4 months ago

1.0.1

4 months ago

1.0.0

4 months ago