npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@shiplypalestine/ui

v1.2.10

Published

A comprehensive React Native UI component library with a modern design system.

Downloads

449

Readme

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.

Hooks

useDisclose

A utility hook for managing open/close state of components like modals, action sheets, and dropdowns.

import { useDisclose } from '@shiplypalestine/ui';

export function MyComponent() {
  const { isOpen, onOpen, onClose, onToggle } = useDisclose();

  return (
    <View>
      <Button title="Open Modal" onPress={onOpen} />
      
      <Modal visible={isOpen} onClose={onClose}>
        <Text>Modal content</Text>
        <Button title="Close" onPress={onClose} />
      </Modal>
    </View>
  );
}

Hook API:

  • isOpen: boolean - Current open/close state
  • onOpen: () => void - Function to open
  • onClose: () => void - Function to close
  • onToggle: () => void - Function to toggle state

Initial State:

// Start with closed state (default)
const { isOpen, onOpen, onClose } = useDisclose();

// Start with open state
const { isOpen, onOpen, onClose } = useDisclose(true);

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
/>

TextArea

A multi-line text input component with advanced features like auto-growing, character counting, and customizable styling. Extends TextInput functionality with TextArea-specific props.

import { TextArea } from '@shiplypalestine/ui';

// Basic usage
<TextArea
  value={value}
  onChangeText={setValue}
  placeholder="Enter your message here..."
  label="Message"
/>

// With auto-growing
<TextArea
  value={value}
  onChangeText={setValue}
  placeholder="This will grow as you type..."
  label="Auto-growing Message"
  autoGrow={true}
  minRows={3}
  maxRows={8}
/>

// With character count
<TextArea
  value={value}
  onChangeText={setValue}
  placeholder="Type something (max 200 characters)..."
  label="Limited Message"
  maxLength={200}
  showCharacterCount={true}
  rows={4}
/>

// With error state
<TextArea
  value={value}
  onChangeText={setValue}
  placeholder="Enter your message..."
  label="Message"
  error="This field is required"
/>

// With start component
<TextArea
  value={value}
  onChangeText={setValue}
  placeholder="Enter your message..."
  label="Message with Icon"
  startComponent={<Text style={{ color: 'blue' }}>📝</Text>}
/>

// RTL support
<TextArea
  value={value}
  onChangeText={setValue}
  placeholder="اكتب رسالتك هنا..."
  label="رسالة"
  forceRTL={true}
  rows={3}
/>

// Custom styling
<TextArea
  value={value}
  onChangeText={setValue}
  placeholder="Custom styled textarea..."
  label="Custom Style"
  containerStyle={{ backgroundColor: '#F9FAFB', padding: 16, borderRadius: 12 }}
  inputStyle={{ fontSize: 18, fontStyle: 'italic' }}
  labelStyle={{ color: 'blue', fontWeight: 'bold' }}
  rows={5}
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | string | '' | The current value of the textarea | | onChangeText | (text: string) => void | - | Callback when text changes | | placeholder | string | - | Placeholder text | | label | string | - | Label text displayed above the textarea | | error | string | - | Error message to display | | disabled | boolean | false | Whether the textarea is disabled | | rows | number | 4 | Number of visible rows | | maxRows | number | 10 | Maximum number of rows when auto-growing | | minRows | number | 2 | Minimum number of rows when auto-growing | | autoGrow | boolean | false | Whether the textarea should grow with content | | showCharacterCount | boolean | false | Whether to show character count | | maxLength | number | - | Maximum number of characters allowed | | resize | 'none' \| 'vertical' \| 'horizontal' \| 'both' | 'vertical' | Resize behavior (web only) | | forceRTL | boolean | false | Force right-to-left text direction | | forceLTR | boolean | false | Force left-to-right text direction |

Style Props

| Prop | Type | Description | |------|------|-------------| | style | ViewStyle | Custom style for the container | | className | string | NativeWind classes for the container | | containerStyle | ViewStyle | Custom style for the main container | | containerClassName | string | NativeWind classes for the main container | | inputStyle | TextStyle | Custom style for the text input | | inputClassName | string | NativeWind classes for the text input | | labelStyle | TextStyle | Custom style for the label | | labelClassName | string | NativeWind classes for the label | | errorStyle | TextStyle | Custom style for the error message | | errorClassName | string | NativeWind classes for the error message | | characterCountStyle | TextStyle | Custom style for the character count | | characterCountClassName | string | NativeWind classes for the character count |

Start/End Component Props

| Prop | Type | Description | |------|------|-------------| | startComponent | React.ReactNode | Component to display at the start of the textarea | | startComponentContainerStyle | ViewStyle | Style for the start component container | | startComponentContainerClassName | string | Classes for the start component container | | endComponent | React.ReactNode | Component to display at the end of the textarea | | endComponentContainerStyle | ViewStyle | Style for the end component container | | endComponentContainerClassName | string | Classes for the end component container |

Features

  • Multi-line Support: Built-in multiline text input
  • Auto-growing: Automatically resize based on content
  • Character Counting: Display character count with max length
  • RTL Support: Right-to-left text direction support
  • Start/End Components: Add icons or other components
  • Custom Styling: Full style customization support
  • Error Handling: Built-in error state and messaging
  • Accessibility: Proper accessibility support
  • Cross-platform: Works on web, iOS, and Android

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[] (required)
  • 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'] (optional)

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

| 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 value
  • onChangeText: (value: string) => void (required) - Callback for when the phone number changes
  • countryCodes: CountryCode[] (required) - 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

| 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. Supports both web and mobile platforms with proper positioning and z-index handling.

import { ActionSheet } from 'shiply-ui';

// Basic usage
<ActionSheet
  visible={isVisible}
  onClose={() => setIsVisible(false)}
  title="Options"
>
  <View>
    <Text>Content goes here</Text>
  </View>
</ActionSheet>

// With custom styles
<ActionSheet
  visible={isVisible}
  onClose={() => setIsVisible(false)}
  title="Custom Styled Options"
  containerStyle={{ backgroundColor: 'rgba(0, 0, 0, 0.7)' }}
  contentStyle={{ 
    backgroundColor: '#F7FAFC',
    borderTopLeftRadius: 20,
    borderTopRightRadius: 20,
  }}
  titleStyle={{ 
    color: '#2D3748',
    fontSize: 20,
    fontWeight: 'bold',
  }}
  searchContainerStyle={{ 
    backgroundColor: '#EDF2F7',
    borderRadius: 8,
    margin: 16,
  }}
  searchInputStyle={{ 
    backgroundColor: '#FFFFFF',
    borderRadius: 6,
  }}
>
  <View>
    <Text>Custom styled content</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 | | emptyMessage | string | 'No items found' | Message to display when no content is provided | | minHeight | number | 500 | Minimum height of the action sheet |

Style Props

| Prop | Type | Description | |------|------|-------------| | style | ViewStyle | Custom style for the content container | | className | string | NativeWind classes for the content container | | containerStyle | ViewStyle | Custom style for the main container | | containerClassName | string | NativeWind classes for the main container | | contentStyle | ViewStyle | Custom style for the action sheet content | | contentClassName | string | NativeWind classes for the action sheet content | | overlayStyle | ViewStyle | Custom style for the overlay background | | overlayClassName | string | NativeWind classes for the overlay background | | titleStyle | TextStyle | Custom style for the title text | | titleClassName | string | NativeWind classes for the title text | | searchContainerStyle | ViewStyle | Custom style for the search container | | searchContainerClassName | string | NativeWind classes for the search container | | searchInputStyle | TextStyle | Custom style for the search input | | searchInputClassName | string | NativeWind classes for the search input |

Platform Support

  • Web: Uses position: fixed with proper z-index for overlay positioning
  • Mobile: Uses React Native Modal with slide-up animation
  • Cross-platform: Consistent API and behavior across platforms

Overlay

A flexible overlay component that creates a backdrop and can wrap other components like spinners, modals, or notifications. Supports multiple positions and customizable styling.

import { Overlay } from '@shiplypalestine/ui';

// Basic overlay with centered content
<Overlay
  isOpen={isVisible}
  onClose={() => setIsVisible(false)}
  position="center"
>
  <View style={{ backgroundColor: 'white', padding: 20, borderRadius: 8 }}>
    <Text>Overlay Content</Text>
  </View>
</Overlay>

// Overlay with spinner for loading states
<Overlay
  isOpen={isLoading}
  position="center"
  dismissible={false}
  closeOnBackdropPress={false}
>
  <View style={{ backgroundColor: 'white', padding: 32, borderRadius: 16, alignItems: 'center' }}>
    <Spinner size="large" color="primary" />
    <Text style={{ marginTop: 16 }}>Loading...</Text>
  </View>
</Overlay>

// Top position notification
<Overlay
  isOpen={showNotification}
  onClose={() => setShowNotification(false)}
  position="top"
  backdropOpacity={0.4}
>
  <View style={{ backgroundColor: 'white', padding: 16, borderRadius: 8 }}>
    <Text>Top notification message</Text>
  </View>
</Overlay>

// Bottom position with custom styling and unmountOnExit
<Overlay
  isOpen={isVisible}
  onClose={() => setIsVisible(false)}
  position="bottom"
  backdropColor="rgba(0, 0, 0, 0.3)"
  contentStyle={{ marginBottom: 20 }}
  unmountOnExit={true}
>
  <View style={{ backgroundColor: 'white', padding: 20, borderRadius: 12 }}>
    <Text>Bottom positioned content</Text>
  </View>
</Overlay>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | isOpen | boolean | false | Controls the visibility of the overlay | | onClose | () => void | - | Callback function called when the overlay is closed | | children | React.ReactNode | - | Content to be displayed in the overlay | | dismissible | boolean | true | Whether the overlay can be dismissed | | closeOnBackdropPress | boolean | true | Whether tapping the backdrop closes the overlay | | unmountOnExit | boolean | false | Whether to unmount the component when closed | | animationType | 'fade' \| 'slide' \| 'none' | 'fade' | Animation type for mobile modal | | position | 'center' \| 'top' \| 'bottom' \| 'left' \| 'right' | 'center' | Position of the content within the overlay | | backdropColor | string | 'rgba(0, 0, 0, 0.5)' | Color of the backdrop | | backdropOpacity | number | 0.5 | Opacity of the backdrop | | zIndex | number | 9999 | Z-index for web positioning |

Style Props

| Prop | Type | Description | |------|------|-------------| | style | ViewStyle | Custom style for the content container | | className | string | NativeWind classes for the content container | | containerStyle | ViewStyle | Custom style for the main container | | containerClassName | string | NativeWind classes for the main container | | backdropStyle | ViewStyle | Custom style for the backdrop | | backdropClassName | string | NativeWind classes for the backdrop | | contentStyle | ViewStyle | Custom style for the content wrapper | | contentClassName | string | NativeWind classes for the content wrapper |

Position Options

  • center: Content is centered both horizontally and vertically
  • top: Content appears at the top of the screen with margin
  • bottom: Content appears at the bottom of the screen with margin
  • left: Content appears at the left side of the screen with margin
  • right: Content appears at the right side of the screen with margin

Use Cases

  1. Loading States: Wrap a Spinner component for loading overlays
  2. Notifications: Use top/bottom positions for toast-like notifications
  3. Modals: Create custom modal dialogs with centered content
  4. Full-screen Overlays: Use for complex overlays that need full screen coverage
  5. Side Panels: Use left/right positions for slide-out panels

Platform Support

  • Web: Uses position: fixed with proper z-index for overlay positioning
  • Mobile: Uses React Native Modal with customizable animation types
  • Cross-platform: Consistent API and behavior across platforms

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

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

Icon Component

A flexible icon wrapper for any icon library (e.g., Feather, MaterialIcons, Ionicons, etc.).

Props

| Prop | Type | Required | Description | |--------|--------|----------|-------------| | as | React.ReactNode | Yes | The icon element (e.g., ) | | style | object | No | Custom style for the wrapper | | className | string | No | NativeWind/Tailwind classes for the wrapper | | ...rest| any | No | Any other props for the wrapper |

Usage

import { Icon } from '@shiplypalestine/ui';
import { Feather, MaterialIcons, Ionicons } from '@expo/vector-icons';

// Feather icon
<Icon as={<Feather name="home" color="#2563EB" size={32} />} />

// MaterialIcons icon
<Icon as={<MaterialIcons name="favorite" color="#EF4444" size={32} />} />

// Ionicons icon
<Icon as={<Ionicons name="receipt" size={32} color="#2563EB" />} />

// With custom style and Tailwind/NativeWind classes
<Icon as={<Feather name="user" color="#22C55E" size={32} />} className="rounded-full bg-gray-100 p-2" style={{ margin: 8 }} />
  • The as prop is the icon element itself (not a component type or string).
  • You can use any icon component from libraries like @expo/vector-icons or react-native-vector-icons.
  • All other props are applied to the wrapper <View>.

Image Component

A flexible image component that supports fallback alt text, size, and NativeWind/Tailwind classes.

Props

| Prop | Type | Required | Description | |-----------|---------------------|----------|-------------| | size | number | No | Sets both width and height of the image | | alt | string | No | Fallback text to show if image fails to load | | source | object | Yes | The image source (same as React Native Image) | | className | string | No | NativeWind/Tailwind classes for styling | | style | object | No | Custom style | | ...rest | any | No | Any other native Image props |

Usage

import { Image } from '@shiplypalestine/ui';

// Basic usage
<Image
  size={150}
  alt="fallback text"
  source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
/>

// With fallback/alt text
<Image
  size={100}
  alt="Logo not found"
  source={{ uri: 'https://invalid-url-to-trigger-fallback.png' }}
/>

// With Tailwind/NativeWind classes
<Image
  size={60}
  alt="Avatar"
  source={{ uri: 'https://reactnative.dev/img/tiny_logo.png' }}
  className="rounded-full border border-blue-500"
/>
  • If the image fails to load, the alt text will be shown in a styled fallback box.
  • The size prop sets both width and height. You can also use style or className for more control.
  • All native Image props are supported.