diff --git a/README.md b/README.md index 6fbcd31..ccbff34 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +## Fork by kota113 +This fork includes enhancements that have not yet been merged into the upstream repository. +Please see the [Pull Requests](https://github.com/kota113/ui/pulls?q=is%3Apr) for details. + +--- # BNA UI 🚀 ![BNA UI Header](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/bna-ui-header.png) diff --git a/docs/components.md b/docs/components.md new file mode 100644 index 0000000..26cfadc --- /dev/null +++ b/docs/components.md @@ -0,0 +1,26889 @@ +# BNA UI Components Documentation + +This documentation is auto-generated from the component registry. + +## Quick Start + +```bash +npx bna-ui init +cd bna-app +npx bna-ui add +``` + +## Table of Contents + +- [General](#general) + - [accordion](#accordion) + - [action-sheet](#action-sheet) + - [alert](#alert) + - [alert-dialog](#alert-dialog) + - [area-chart](#area-chart) + - [audio-player](#audio-player) + - [audio-recorder](#audio-recorder) + - [audio-waveform](#audio-waveform) + - [avatar](#avatar) + - [avoid-keyboard](#avoid-keyboard) + - [badge](#badge) + - [bar-chart](#bar-chart) + - [bottom-sheet](#bottom-sheet) + - [bubble-chart](#bubble-chart) + - [button](#button) + - [camera](#camera) + - [camera-preview](#camera-preview) + - [candlestick-chart](#candlestick-chart) + - [card](#card) + - [carousel](#carousel) + - [chart-container](#chart-container) + - [checkbox](#checkbox) + - [collapsible](#collapsible) + - [color-picker](#color-picker) + - [column-chart](#column-chart) + - [combobox](#combobox) + - [combobox-demo](#combobox-demo) + - [combobox-disabled](#combobox-disabled) + - [combobox-form](#combobox-form) + - [combobox-groups](#combobox-groups) + - [combobox-large](#combobox-large) + - [combobox-multiple](#combobox-multiple) + - [combobox-search](#combobox-search) + - [date-picker](#date-picker) + - [doughnut-chart](#doughnut-chart) + - [file-picker](#file-picker) + - [gallery](#gallery) + - [heatmap-chart](#heatmap-chart) + - [hello-wave](#hello-wave) + - [icon](#icon) + - [image](#image) + - [input](#input) + - [input-otp](#input-otp) + - [line-chart](#line-chart) + - [link](#link) + - [media-picker](#media-picker) + - [mode-toggle](#mode-toggle) + - [onboarding](#onboarding) + - [parallax-scrollview](#parallax-scrollview) + - [picker](#picker) + - [pie-chart](#pie-chart) + - [polar-area-chart](#polar-area-chart) + - [popover](#popover) + - [progress](#progress) + - [progress-ring-chart](#progress-ring-chart) + - [radar-chart](#radar-chart) + - [radial-bar-chart](#radial-bar-chart) + - [radio](#radio) + - [scatter-chart](#scatter-chart) + - [scroll-view](#scroll-view) + - [searchbar](#searchbar) + - [separator](#separator) + - [share](#share) + - [sheet](#sheet) + - [skeleton](#skeleton) + - [spinner](#spinner) + - [stacked-area-chart](#stacked-area-chart) + - [stacked-bar-chart](#stacked-bar-chart) + - [switch](#switch) + - [table](#table) + - [tabs](#tabs) + - [text](#text) + - [toast](#toast) + - [toggle](#toggle) + - [treemap-chart](#treemap-chart) + - [video](#video) + - [view](#view) + +--- + +## General + +### accordion + +A vertically stacked set of interactive headings that each reveal a section of content. + +**Installation:** +```bash +npx bna-ui add accordion +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** text, view, icon + +**Preview:** + +![accordion preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-29-2025 06-09-54_1.mov) + +#### Basic Usage + +```tsx +import { Accordion } from '@/components/ui/accordion'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### accordion-demo + +A basic accordion with collapsible sections + +```tsx +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; +import { Text } from '@/components/ui/text'; +import React from 'react'; + +export function AccordionDemo() { + return ( + + + Is it accessible? + + Yes. It adheres to the WAI-ARIA design pattern. + + + + Is it styled? + + + Yes. It comes with default styles that matches the other components' + aesthetic. + + + + + Is it animated? + + + Yes. It's animated by default, but you can disable it if you prefer. + + + + + ); +} + +``` + +##### accordion-single + +An accordion that allows only one item to be open at a time + +```tsx +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; +import { Text } from '@/components/ui/text'; +import React from 'react'; + +export function AccordionSingle() { + return ( + + + What is React Native? + + + React Native is a framework for building native mobile applications + using React. It allows you to create mobile apps for iOS and Android + using JavaScript and React components. + + + + + What is Expo? + + + Expo is a platform for making universal native apps that run on + Android, iOS, and the web. It provides a set of tools and services + built around React Native. + + + + + What is TypeScript? + + + TypeScript is a programming language developed by Microsoft. It is a + strict syntactical superset of JavaScript and adds optional static + type checking to the language. + + + + + ); +} + +``` + +##### accordion-multiple + +An accordion that allows multiple items to be open simultaneously + +```tsx +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; +import { Text } from '@/components/ui/text'; +import React from 'react'; + +export function AccordionMultiple() { + return ( + + + Frontend Technologies + + + Modern frontend development includes React, Vue, Angular, and many + other frameworks that help build interactive user interfaces. + + + + + Backend Technologies + + + Backend development involves server-side technologies like Node.js, + Python, Java, and databases to handle data and business logic. + + + + + Mobile Development + + + Mobile development can be done natively with Swift/Kotlin or with + cross-platform solutions like React Native, Flutter, or Xamarin. + + + + + DevOps & Cloud + + + DevOps practices and cloud platforms like AWS, Azure, and GCP help + deploy, scale, and maintain applications efficiently. + + + + + ); +} + +``` + +##### accordion-controlled + +An accordion with controlled state management + +```tsx +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AccordionControlled() { + const [value, setValue] = React.useState(''); + + return ( + + + Currently open: {value || 'None'} + + + + Settings + + + Configure your application preferences, notifications, and account + settings here. + + + + + Privacy + + + Manage your privacy settings, data sharing preferences, and + visibility controls. + + + + + Security + + + Set up two-factor authentication, change passwords, and review + security logs. + + + + + + ); +} + +``` + +##### accordion-faq + +An accordion formatted as a frequently asked questions section + +```tsx +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; +import { Text } from '@/components/ui/text'; +import React from 'react'; + +export function AccordionFAQ() { + return ( + + + How long does shipping take? + + + Standard shipping typically takes 3-5 business days. Express + shipping is available for 1-2 business days delivery. + + + + + What is your return policy? + + + We offer a 30-day return policy for all items in original condition. + Return shipping is free for defective items. + + + + + Do you offer warranty? + + + Yes, all products come with a 1-year manufacturer warranty. Extended + warranty options are available at checkout. + + + + + How can I contact support? + + + You can reach our support team via email at support@example.com or + through our live chat feature available 24/7. + + + + + ); +} + +``` + +##### accordion-non-collapsible + +An accordion where at least one item must always remain open + +```tsx +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; +import { Text } from '@/components/ui/text'; +import React from 'react'; + +export function AccordionNonCollapsible() { + return ( + + + Step 1: Planning + + + Start by defining your project requirements and creating a detailed + plan. This includes wireframing and technical specifications. + + + + + Step 2: Development + + + Begin the development process by setting up your environment and + implementing the core features according to your plan. + + + + + Step 3: Testing + + + Thoroughly test your application across different devices and + scenarios to ensure it works as expected. + + + + + Step 4: Deployment + + + Deploy your application to production and monitor its performance. + Set up analytics and error tracking. + + + + + ); +} + +``` + +##### accordion-styled + +An accordion with custom styling and icons + +```tsx +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function AccordionStyled() { + const card = useThemeColor({}, 'card'); + + return ( + + + + 🚀 Features + + + • Cross-platform compatibility{'\n'}• TypeScript support{'\n'}• + Theme system integration{'\n'}• Customizable animations + + + + + ⚡ Performance + + + • Optimized rendering{'\n'}• Minimal re-renders{'\n'}• Smooth + animations{'\n'}• Memory efficient + + + + + ♿ Accessibility + + + • Screen reader support{'\n'}• Keyboard navigation{'\n'}• Focus + management{'\n'}• ARIA attributes + + + + + + ); +} + +``` +--- + +### action-sheet + +A native-feeling action sheet component that provides a menu of options triggered from the bottom of the screen. + +**Installation:** +```bash +npx bna-ui add action-sheet +``` + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![action-sheet preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-29-2025 06-18-08_1.MP4) + +#### Basic Usage + +```tsx +import { Action-sheet } from '@/components/ui/action-sheet'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### action-sheet-demo + +A basic action sheet with multiple options + +```tsx +import { ActionSheet } from '@/components/ui/action-sheet'; +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function ActionSheetDemo() { + const [visible, setVisible] = useState(false); + + const options = [ + { + title: 'Edit', + onPress: () => console.log('Edit pressed'), + }, + { + title: 'Share', + onPress: () => console.log('Share pressed'), + }, + { + title: 'Delete', + onPress: () => console.log('Delete pressed'), + destructive: true, + }, + ]; + + return ( + + + setVisible(false)} + title='Choose an action' + message='Select one of the options below' + options={options} + /> + + ); +} + +``` + +##### action-sheet-icons + +An action sheet with icons next to each option + +```tsx +import { ActionSheet } from '@/components/ui/action-sheet'; +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { Download, Edit, Share, Trash2 } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ActionSheetIcons() { + const [visible, setVisible] = useState(false); + + const options = [ + { + title: 'Edit', + onPress: () => console.log('Edit pressed'), + icon: , + }, + { + title: 'Share', + onPress: () => console.log('Share pressed'), + icon: , + }, + { + title: 'Download', + onPress: () => console.log('Download pressed'), + icon: , + }, + { + title: 'Delete', + onPress: () => console.log('Delete pressed'), + destructive: true, + icon: , + }, + ]; + + return ( + <> + + setVisible(false)} + title='File Actions' + options={options} + /> + + ); +} + +``` + +##### action-sheet-destructive + +An action sheet featuring destructive actions with appropriate styling + +```tsx +import { ActionSheet } from '@/components/ui/action-sheet'; +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { AlertTriangle, Trash2 } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ActionSheetDestructive() { + const [visible, setVisible] = useState(false); + + const options = [ + { + title: 'Remove from Library', + onPress: () => console.log('Remove from library'), + destructive: true, + }, + { + title: 'Delete Permanently', + onPress: () => console.log('Delete permanently'), + destructive: true, + icon: , + }, + { + title: 'Report Content', + onPress: () => console.log('Report content'), + destructive: true, + icon: , + }, + ]; + + return ( + <> + + setVisible(false)} + title='Are you sure?' + message='These actions cannot be undone' + options={options} + /> + + ); +} + +``` + +##### action-sheet-disabled + +An action sheet with some disabled options + +```tsx +import { ActionSheet } from '@/components/ui/action-sheet'; +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { Copy, Edit, Share, Trash2 } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ActionSheetDisabled() { + const [visible, setVisible] = useState(false); + + const options = [ + { + title: 'Edit', + onPress: () => console.log('Edit pressed'), + icon: , + }, + { + title: 'Copy', + onPress: () => console.log('Copy pressed'), + icon: , + disabled: true, + }, + { + title: 'Share', + onPress: () => console.log('Share pressed'), + icon: , + disabled: true, + }, + { + title: 'Delete', + onPress: () => console.log('Delete pressed'), + destructive: true, + icon: , + }, + ]; + + return ( + <> + + setVisible(false)} + title='Document Actions' + message='Some actions are not available' + options={options} + /> + + ); +} + +``` + +##### action-sheet-styled + +An action sheet with custom styling and branding + +```tsx +import { ActionSheet } from '@/components/ui/action-sheet'; +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { Bookmark, Heart, Send, Star } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ActionSheetStyled() { + const [visible, setVisible] = useState(false); + + const options = [ + { + title: 'Add to Favorites', + onPress: () => console.log('Add to favorites'), + icon: , + }, + { + title: 'Rate this Item', + onPress: () => console.log('Rate item'), + icon: , + }, + { + title: 'Save for Later', + onPress: () => console.log('Save for later'), + icon: , + }, + { + title: 'Share with Friends', + onPress: () => console.log('Share with friends'), + icon: , + }, + ]; + + return ( + <> + + setVisible(false)} + title='✨ Quick Actions' + message='Choose how you want to interact with this item' + options={options} + cancelButtonTitle='Maybe Later' + style={{ + borderTopLeftRadius: 24, + borderTopRightRadius: 24, + }} + /> + + ); +} + +``` + +##### action-sheet-long + +An action sheet with many options that scrolls + +```tsx +import { ActionSheet } from '@/components/ui/action-sheet'; +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { + Archive, + Bookmark, + Copy, + Download, + Edit, + EyeOff, + Flag, + Heart, + Pin, + Send, + Share, + Star, + Trash2, +} from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ActionSheetLong() { + const [visible, setVisible] = useState(false); + + const options = [ + { + title: 'Edit Document', + onPress: () => console.log('Edit'), + icon: , + }, + { + title: 'Share', + onPress: () => console.log('Share'), + icon: , + }, + { + title: 'Download', + onPress: () => console.log('Download'), + icon: , + }, + { + title: 'Copy Link', + onPress: () => console.log('Copy link'), + icon: , + }, + { + title: 'Archive', + onPress: () => console.log('Archive'), + icon: , + }, + { + title: 'Pin to Top', + onPress: () => console.log('Pin'), + icon: , + }, + { + title: 'Add to Favorites', + onPress: () => console.log('Favorite'), + icon: , + }, + { + title: 'Rate & Review', + onPress: () => console.log('Rate'), + icon: , + }, + { + title: 'Bookmark', + onPress: () => console.log('Bookmark'), + icon: , + }, + { + title: 'Send Message', + onPress: () => console.log('Send message'), + icon: , + }, + { + title: 'Hide from Feed', + onPress: () => console.log('Hide'), + icon: , + }, + { + title: 'Report Issue', + onPress: () => console.log('Report'), + icon: , + }, + { + title: 'Delete', + onPress: () => console.log('Delete'), + destructive: true, + icon: , + }, + ]; + + return ( + <> + + setVisible(false)} + title='All Actions' + message='Scroll to see all available options' + options={options} + /> + + ); +} + +``` + +##### action-sheet-hook + +Using the useActionSheet hook for easier management + +```tsx +import { useActionSheet } from '@/components/ui/action-sheet'; +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { View } from '@/components/ui/view'; +import { Camera, FileText, Image, Mic } from 'lucide-react-native'; +import React from 'react'; + +export function ActionSheetHook() { + const { show, ActionSheet } = useActionSheet(); + + const showMediaOptions = () => { + show({ + title: 'Add Media', + message: 'Choose the type of media to add', + options: [ + { + title: 'Take Photo', + onPress: () => console.log('Take photo'), + icon: , + }, + { + title: 'Choose from Gallery', + onPress: () => console.log('Choose from gallery'), + icon: , + }, + { + title: 'Record Audio', + onPress: () => console.log('Record audio'), + icon: , + }, + { + title: 'Add Document', + onPress: () => console.log('Add document'), + icon: , + }, + ], + }); + }; + + const showConfirmation = () => { + show({ + title: 'Confirm Action', + message: 'This action cannot be undone', + options: [ + { + title: 'Yes, Continue', + onPress: () => console.log('Confirmed'), + destructive: true, + }, + ], + }); + }; + + return ( + + + + {ActionSheet} + + ); +} + +``` +--- + +### alert + +Display important messages to users with both visual inline alerts and native system alerts. + +**Installation:** +```bash +npx bna-ui add alert +``` + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![alert preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-29-2025 06-25-10_1.mov) + +#### Basic Usage + +```tsx +import { Alert } from '@/components/ui/alert'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### alert-demo + +A basic native alert with two buttons + +```tsx +import { createTwoButtonAlert } from '@/components/ui/alert'; +import { Button } from '@/components/ui/button'; +import React from 'react'; + +export function AlertDemo() { + const handleTwoButtonAlert = () => { + createTwoButtonAlert({ + title: 'Two Button Alert', + message: 'This is a two-button alert example', + buttons: [ + { + text: 'Cancel', + onPress: () => console.log('Cancel Pressed'), + style: 'cancel', + }, + { + text: 'OK', + onPress: () => console.log('OK Pressed'), + }, + ], + }); + }; + + return ; +} + +``` + +##### alert-three-button-demo + +A native alert with three button options + +```tsx +import { createThreeButtonAlert } from '@/components/ui/alert'; +import { Button } from '@/components/ui/button'; +import React from 'react'; + +export function AlertThreeButtonDemo() { + const handleThreeButtonAlert = () => { + createThreeButtonAlert({ + title: 'Three Button Alert', + message: 'This is a three-button alert example', + buttons: [ + { + text: 'Ask me later', + onPress: () => console.log('Ask me later pressed'), + }, + { + text: 'Cancel', + onPress: () => console.log('Cancel Pressed'), + style: 'cancel', + }, + { + text: 'OK', + onPress: () => console.log('OK Pressed'), + }, + ], + }); + }; + + return ( + + ); +} + +``` + +##### alert-success-demo + +Success alert with positive messaging + +```tsx +import { showSuccessAlert } from '@/components/ui/alert'; +import { Button } from '@/components/ui/button'; +import React from 'react'; + +export function AlertSuccessDemo() { + const handleSuccessAlert = () => { + showSuccessAlert( + 'Success!', + 'Your action was completed successfully.', + () => console.log('Success acknowledged') + ); + }; + + return ( + + ); +} + +``` + +##### alert-error-demo + +Error alert with destructive styling + +```tsx +import { showErrorAlert } from '@/components/ui/alert'; +import { Button } from '@/components/ui/button'; +import React from 'react'; + +export function AlertErrorDemo() { + const handleErrorAlert = () => { + showErrorAlert('Error', 'Something went wrong. Please try again.', () => + console.log('Error acknowledged') + ); + }; + + return ( + + ); +} + +``` + +##### alert-confirm-demo + +Confirmation alert for destructive actions + +```tsx +import { showConfirmAlert } from '@/components/ui/alert'; +import { Button } from '@/components/ui/button'; +import React from 'react'; + +export function AlertConfirmDemo() { + const handleConfirmAlert = () => { + showConfirmAlert( + 'Confirm Action', + 'Are you sure you want to proceed with this action?', + () => console.log('Action confirmed'), + () => console.log('Action cancelled') + ); + }; + + return ( + + ); +} + +``` + +##### alert-custom-demo + +Custom native alert with multiple options + +```tsx +import { showNativeAlert } from '@/components/ui/alert'; +import { Button } from '@/components/ui/button'; +import React from 'react'; + +export function AlertCustomDemo() { + const handleCustomAlert = () => { + showNativeAlert({ + title: 'Custom Alert', + message: 'This is a custom native alert with multiple options', + buttons: [ + { + text: 'Option 1', + onPress: () => console.log('Option 1 selected'), + }, + { + text: 'Option 2', + onPress: () => console.log('Option 2 selected'), + }, + { + text: 'Cancel', + onPress: () => console.log('Cancelled'), + style: 'cancel', + }, + ], + }); + }; + + return ; +} + +``` + +##### alert-visual-demo + +Inline visual alerts that appear within your content + +```tsx +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AlertVisualDemo() { + return ( + + + Visual Alert + + This is a default visual alert that appears inline with your content. + + + + + Visual alerts appear inline with your content, while native alerts + appear as system dialogs on top of your app. + + + ); +} + +``` + +##### alert-visual-destructive-demo + +Destructive visual alerts for error messages + +```tsx +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AlertVisualDestructiveDemo() { + return ( + + + Destructive Alert + + This is a destructive visual alert for error messages. + + + + + Visual alerts appear inline with your content, while native alerts + appear as system dialogs on top of your app. + + + ); +} + +``` + +##### alert-advanced-demo + +Advanced alert usage patterns and combinations + +```tsx +import { + Alert, + AlertDescription, + AlertTitle, + showConfirmAlert, + showNativeAlert, +} from '@/components/ui/alert'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function AlertAdvancedDemo() { + const [showVisualAlert, setShowVisualAlert] = useState(false); + const [alertType, setAlertType] = useState<'success' | 'error' | null>(null); + + const handleAsyncOperation = async () => { + // Show loading state + setShowVisualAlert(true); + setAlertType(null); + + try { + // Simulate async operation + await new Promise((resolve) => setTimeout(resolve, 2000)); + + // Show success + setAlertType('success'); + + // Auto hide after 3 seconds + setTimeout(() => { + setShowVisualAlert(false); + setAlertType(null); + }, 3000); + } catch (error) { + setAlertType('error'); + } + }; + + const handleDeleteWithConfirmation = () => { + showConfirmAlert( + 'Delete Item', + 'This action cannot be undone. Are you sure you want to delete this item?', + () => { + // First confirmation passed, show second confirmation for critical action + showConfirmAlert( + 'Final Confirmation', + 'This will permanently delete the item and all associated data. This cannot be undone.', + () => { + console.log('Item deleted'); + }, + () => { + console.log('Delete cancelled at final step'); + } + ); + }, + () => { + console.log('Delete cancelled'); + } + ); + }; + + const handleComplexAlert = () => { + showNativeAlert({ + title: 'Choose Your Action', + message: 'What would you like to do with this item?', + buttons: [ + { + text: 'Edit', + onPress: () => console.log('Edit selected'), + }, + { + text: 'Share', + onPress: () => console.log('Share selected'), + }, + { + text: 'Archive', + onPress: () => console.log('Archive selected'), + }, + { + text: 'Delete', + onPress: () => handleDeleteWithConfirmation(), + style: 'destructive', + }, + { + text: 'Cancel', + onPress: () => console.log('Cancelled'), + style: 'cancel', + }, + ], + }); + }; + + return ( + + {/* Dynamic Visual Alert */} + {showVisualAlert && ( + + + {alertType === null && 'Processing...'} + {alertType === 'success' && 'Success!'} + {alertType === 'error' && 'Error'} + + + {alertType === null && 'Please wait while we process your request.'} + {alertType === 'success' && + 'Your operation completed successfully.'} + {alertType === 'error' && 'Something went wrong. Please try again.'} + + + )} + + + + + + + + + + + These demo demonstrate advanced patterns like chained confirmations, + dynamic visual alerts, and complex multi-option native alerts. + + + ); +} + +``` + +##### alert-dialog-demo + +A basic alert dialog with confirmation buttons + +```tsx +import React from 'react'; +import { AlertDialog, useAlertDialog } from '@/components/ui/alert-dialog'; +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; + +export default function AlertDialogDemo() { + const dialog = useAlertDialog(); + + return ( + + + + { + console.log('Account deleted'); + dialog.close(); + }} + onCancel={dialog.close} + /> + + ); +} +``` + +##### alert-dialog-destructive + +An alert dialog for destructive actions like delete + +```tsx +import React from 'react'; +import { AlertDialog, useAlertDialog } from '@/components/ui/alert-dialog'; +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; + +export default function AlertDialogDestructiveDemo() { + const dialog = useAlertDialog(); + + return ( + + + + { + console.log('Item deleted'); + dialog.close(); + }} + onCancel={dialog.close} + /> + + ); +} +``` + +##### alert-dialog-custom + +A custom styled alert dialog with different appearance + +```tsx +import React from 'react'; +import { AlertDialog, useAlertDialog } from '@/components/ui/alert-dialog'; +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; +import { Text } from '@/components/ui/text'; + +export default function AlertDialogCustomDemo() { + const dialog = useAlertDialog(); + + return ( + + + + { + console.log('Continued'); + dialog.close(); + }} + onCancel={dialog.close} + style={{ borderRadius: 24 }} + > + + + Custom Content + + + This dialog contains custom content instead of using the title and description props. + + + + + ); +} +``` +--- + +### alert-dialog + +A modal dialog that interrupts the user with important content and expects a response. + +**Installation:** +```bash +npx bna-ui add alert-dialog +``` + +**External Dependencies:** react-native-reanimated + +**Registry Dependencies:** card, button, text, view + +**Preview:** + +![alert-dialog preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/alert-dialog-preview.mov) + +#### Basic Usage + +```tsx +import { Alert-dialog } from '@/components/ui/alert-dialog'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### alert-dialog-demo + +A basic alert dialog with confirmation buttons + +```tsx +import React from 'react'; +import { AlertDialog, useAlertDialog } from '@/components/ui/alert-dialog'; +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; + +export default function AlertDialogDemo() { + const dialog = useAlertDialog(); + + return ( + + + + { + console.log('Account deleted'); + dialog.close(); + }} + onCancel={dialog.close} + /> + + ); +} +``` + +##### alert-dialog-destructive + +An alert dialog for destructive actions like delete + +```tsx +import React from 'react'; +import { AlertDialog, useAlertDialog } from '@/components/ui/alert-dialog'; +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; + +export default function AlertDialogDestructiveDemo() { + const dialog = useAlertDialog(); + + return ( + + + + { + console.log('Item deleted'); + dialog.close(); + }} + onCancel={dialog.close} + /> + + ); +} +``` + +##### alert-dialog-custom + +A custom styled alert dialog with different appearance + +```tsx +import React from 'react'; +import { AlertDialog, useAlertDialog } from '@/components/ui/alert-dialog'; +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; +import { Text } from '@/components/ui/text'; + +export default function AlertDialogCustomDemo() { + const dialog = useAlertDialog(); + + return ( + + + + { + console.log('Continued'); + dialog.close(); + }} + onCancel={dialog.close} + style={{ borderRadius: 24 }} + > + + + Custom Content + + + This dialog contains custom content instead of using the title and description props. + + + + + ); +} +``` +--- + +### area-chart + +A customizable area chart component with gradient fills and smooth animations. + +**Installation:** +```bash +npx bna-ui add area-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Registry Dependencies:** line-chart + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![area-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 15-15-47_1.mov) + +#### Basic Usage + +```tsx +import { Area-chart } from '@/components/ui/area-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### area-chart-demo + +An area chart with gradient fill and smooth animations + +```tsx +import { AreaChart } from '@/components/charts/area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { x: 'Jan', y: 100, label: 'January' }, + { x: 'Feb', y: 120, label: 'February' }, + { x: 'Mar', y: 90, label: 'March' }, + { x: 'Apr', y: 140, label: 'April' }, + { x: 'May', y: 110, label: 'May' }, + { x: 'Jun', y: 130, label: 'June' }, +]; + +export function AreaChartDemo() { + return ( + + + + ); +} + +``` + +##### area-chart-interactive + +An interactive area chart with touch gestures + +```tsx +import { AreaChart } from '@/components/charts/area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { x: 'Jan', y: 4000, label: 'January' }, + { x: 'Feb', y: 3000, label: 'February' }, + { x: 'Mar', y: 5000, label: 'March' }, + { x: 'Apr', y: 4500, label: 'April' }, + { x: 'May', y: 6000, label: 'May' }, + { x: 'Jun', y: 7200, label: 'June' }, + { x: 'Jul', y: 6800, label: 'July' }, +]; + +export function AreaChartInteractive() { + return ( + + + + ); +} + +``` + +##### area-chart-styled + +A customized area chart with custom styling + +```tsx +import { AreaChart } from '@/components/charts/area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const sampleData = [ + { x: 'Week 1', y: 850, label: 'Week 1' }, + { x: 'Week 2', y: 1200, label: 'Week 2' }, + { x: 'Week 3', y: 980, label: 'Week 3' }, + { x: 'Week 4', y: 1450, label: 'Week 4' }, + { x: 'Week 5', y: 1100, label: 'Week 5' }, + { x: 'Week 6', y: 1650, label: 'Week 6' }, +]; + +export function AreaChartStyled() { + const borderColor = useThemeColor({}, 'border'); + const backgroundColor = useThemeColor({}, 'card'); + + return ( + + + + ); +} + +``` + +##### area-chart-large + +An area chart with large data + +```tsx +import { AreaChart } from '@/components/charts/area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { x: 'Jan', y: 65000, label: 'January' }, + { x: 'Feb', y: 80000, label: 'February' }, + { x: 'Mar', y: 75000, label: 'March' }, + { x: 'Apr', y: 95000, label: 'April' }, + { x: 'May', y: 110000, label: 'May' }, + { x: 'Jun', y: 125000, label: 'June' }, + { x: 'Jul', y: 140000, label: 'July' }, + { x: 'Aug', y: 135000, label: 'August' }, + { x: 'Sep', y: 150000, label: 'September' }, + { x: 'Oct', y: 165000, label: 'October' }, + { x: 'Nov', y: 180000, label: 'November' }, + { x: 'Dec', y: 195000, label: 'December' }, +]; + +export function AreaChartLarge() { + return ( + + + + ); +} + +``` +--- + +### audio-player + +A feature-rich audio player component with waveform visualization, playback controls, and seeking capabilities for music, podcasts, and voice recordings. + +**Installation:** +```bash +npx bna-ui add audio-player +``` + +**External Dependencies:** expo-audio, lucide-react-native + +**Registry Dependencies:** button, progress, text, audio-waveform + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Props Interface + +```typescript +export interface AudioPlayerProps { + source: AudioSource; + style?: ViewStyle; + showControls?: boolean; + showWaveform?: boolean; + showTimer?: boolean; + showProgressBar?: boolean; + autoPlay?: boolean; + onPlaybackStatusUpdate?: (status: any) => void; +} +``` + +**Preview:** + +![audio-player preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-29-2025 20-06-28_1.MP4) + +#### Basic Usage + +```tsx +import { Audio-player } from '@/components/ui/audio-player'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### audio-player-demo + +A complete audio player with all features enabled + +```tsx +import { AudioPlayer } from '@/components/ui/audio-player'; + +export function AudioPlayerDemo() { + // Sample audio URL - replace with your actual audio source + const sampleAudioUrl = + 'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav'; + + return ( + { + console.log('Playback status:', status); + }} + /> + ); +} + +``` + +##### audio-player-minimal + +A minimal audio player with only essential controls + +```tsx +import { AudioPlayer } from '@/components/ui/audio-player'; + +export function AudioPlayerMinimal() { + const sampleAudioUrl = + 'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav'; + + return ( + + ); +} + +``` + +##### audio-player-waveform + +Audio player focused on waveform visualization + +```tsx +import { AudioPlayer } from '@/components/ui/audio-player'; + +export function AudioPlayerWaveform() { + const sampleAudioUrl = + 'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav'; + + return ( + + ); +} + +``` + +##### audio-player-progress + +Audio player using only a progress bar for seeking + +```tsx +import { AudioPlayer } from '@/components/ui/audio-player'; + +export function AudioPlayerProgress() { + const sampleAudioUrl = + 'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav'; + + return ( + + ); +} + +``` + +##### audio-player-autoplay + +Audio player that starts playing automatically when loaded + +```tsx +import { AudioPlayer } from '@/components/ui/audio-player'; + +export function AudioPlayerAutoplay() { + const sampleAudioUrl = + 'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav'; + + return ( + { + if (status.isLoaded && status.playing) { + console.log('Auto-playing audio'); + } + }} + /> + ); +} + +``` + +##### audio-player-styled + +An audio player with custom styling and theming + +```tsx +import { AudioPlayer } from '@/components/ui/audio-player'; +import { useThemeColor } from '@/hooks/useThemeColor'; + +export function AudioPlayerStyled() { + const blue = useThemeColor({}, 'indigo'); + + const sampleAudioUrl = + 'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav'; + + return ( + + ); +} + +``` + +##### audio-player-music + +Audio player with music-focused UI including album art and track info + +```tsx +import { AudioPlayer } from '@/components/ui/audio-player'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { + Heart, + MoreHorizontal, + Shuffle, + SkipBack, + SkipForward, +} from 'lucide-react-native'; +import React, { useState } from 'react'; +import { Image, StyleSheet } from 'react-native'; + +export function AudioPlayerMusic() { + const [isLiked, setIsLiked] = useState(false); + const sampleAudioUrl = + 'https://www.thesoundarchive.com/ringtones/old-phone-ringing.wav'; + + return ( + + {/* Album Art */} + + + + + {/* Track Information */} + + + Midnight Waves + + + Ocean Sounds Orchestra + + + + {/* Action Buttons */} + + + + + + {/* Audio Player */} + + + {/* Additional Controls */} + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + musicPlayer: { + backgroundColor: '#F2F2F7', + borderRadius: 16, + padding: 20, + shadowColor: '#000', + shadowOffset: { + width: 0, + height: 4, + }, + shadowOpacity: 0.1, + shadowRadius: 12, + elevation: 8, + }, + albumArtContainer: { + alignItems: 'center', + marginBottom: 16, + }, + albumArt: { + width: 120, + height: 120, + borderRadius: 12, + }, + trackInfo: { + alignItems: 'center', + marginBottom: 12, + }, + trackTitle: { + fontSize: 18, + fontWeight: '600', + marginBottom: 4, + color: '#1a1a1a', + }, + artistName: { + fontSize: 14, + color: '#666', + }, + actionButtons: { + flexDirection: 'row', + justifyContent: 'center', + gap: 8, + marginBottom: 16, + }, + actionButton: { + width: 36, + height: 36, + }, + playerContainer: { + backgroundColor: 'transparent', + margin: 0, + padding: 0, + marginBottom: 16, + }, + additionalControls: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + gap: 8, + }, + controlButton: { + width: 36, + height: 36, + }, + spacer: { + width: 40, + }, +}); + +``` +--- + +### audio-recorder + +A comprehensive audio recording component with real-time waveform visualization, quality settings, and built-in playback capabilities. + +**Installation:** +```bash +npx bna-ui add audio-recorder +``` + +**External Dependencies:** expo-audio, lucide-react-native + +**Registry Dependencies:** audio-player, audio-waveform, button, text + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Props Interface + +```typescript +export interface AudioRecorderProps { + style?: ViewStyle; + quality?: 'high' | 'low'; + showWaveform?: boolean; + showTimer?: boolean; + maxDuration?: number; // in seconds + onRecordingComplete?: (uri: string) => void; + onRecordingStart?: () => void; + onRecordingStop?: () => void; + customRecordingOptions?: RecordingOptions; +} +``` + +**Preview:** + +![audio-recorder preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-29-2025 20-42-54_1.MP4) + +#### Basic Usage + +```tsx +import { Audio-recorder } from '@/components/ui/audio-recorder'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### audio-recorder-demo + +Full-featured audio recorder with real-time waveform and playback + +```tsx +import { AudioRecorder } from '@/components/ui/audio-recorder'; + +export function AudioRecorderDemo() { + const handleRecordingComplete = (uri: string) => { + console.log('Recording saved to:', uri); + }; + + const handleRecordingStart = () => { + console.log('Recording started'); + }; + + const handleRecordingStop = () => { + console.log('Recording stopped'); + }; + + return ( + + ); +} + +``` + +##### audio-recorder-voice + +Optimized recorder for quick voice notes with time limit + +```tsx +import { AudioRecorder } from '@/components/ui/audio-recorder'; + +export function AudioRecorderVoice() { + const handleRecordingComplete = (uri: string) => { + // Here you could add the voice note to a list or send it + console.log('Voice note saved:', uri); + }; + + return ( + + ); +} + +``` + +##### audio-recorder-hq + +High-quality recorder for music or professional audio + +```tsx +import { AudioRecorder } from '@/components/ui/audio-recorder'; +import { RecordingPresets } from 'expo-audio'; + +export function AudioRecorderHQ() { + const handleRecordingComplete = (uri: string) => { + console.log('HQ recording saved:', uri); + }; + + return ( + + ); +} + +``` + +##### audio-recorder-minimal + +Minimal recorder without waveform visualization + +```tsx +import { AudioRecorder } from '@/components/ui/audio-recorder'; + +export function AudioRecorderMinimal() { + const handleRecordingComplete = (uri: string) => { + console.log('Your audio has been recorded.', uri); + }; + + return ( + + ); +} + +``` + +##### audio-recorder-styled + +Audio recorder with custom styling and branding + +```tsx +import { AudioRecorder } from '@/components/ui/audio-recorder'; + +export function AudioRecorderStyled() { + const handleRecordingComplete = (uri: string) => { + console.log('🎵 Recording Complete', uri); + }; + + return ( + + ); +} + +``` + +##### audio-recorder-callbacks + +Recorder with comprehensive callback handling + +```tsx +import { AudioRecorder } from '@/components/ui/audio-recorder'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useState } from 'react'; + +export function AudioRecorderCallbacks() { + const [status, setStatus] = useState('Ready to record'); + const [recordingCount, setRecordingCount] = useState(0); + + const handleRecordingStart = () => { + setStatus('🔴 Recording in progress...'); + console.log('Recording started'); + }; + + const handleRecordingStop = () => { + setStatus('✅ Recording stopped'); + console.log('Recording stopped'); + }; + + const handleRecordingComplete = (uri: string) => { + setRecordingCount((prev) => prev + 1); + setStatus(`📁 Recording #${recordingCount + 1} saved`); + + // Reset status after 3 seconds + setTimeout(() => setStatus('Ready to record'), 3000); + }; + + const getOrdinalSuffix = (num: number) => { + const lastDigit = num % 10; + const lastTwoDigits = num % 100; + + if (lastTwoDigits >= 11 && lastTwoDigits <= 13) return 'th'; + if (lastDigit === 1) return 'st'; + if (lastDigit === 2) return 'nd'; + if (lastDigit === 3) return 'rd'; + return 'th'; + }; + + return ( + + + Status: {status} + + + + + {recordingCount > 0 && ( + + Total recordings: {recordingCount} + + )} + + ); +} + +``` + +##### audio-recorder-cloud + +Recorder with cloud storage integration + +```tsx +import { AudioRecorder } from '@/components/ui/audio-recorder'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; +import { ActivityIndicator } from 'react-native'; + +export function AudioRecorderCloud() { + const [uploading, setUploading] = useState(false); + const [uploadProgress, setUploadProgress] = useState(0); + + const simulateCloudUpload = async (uri: string): Promise => { + return new Promise((resolve) => { + setUploading(true); + setUploadProgress(0); + + const interval = setInterval(() => { + setUploadProgress((prev) => { + if (prev >= 100) { + clearInterval(interval); + setUploading(false); + resolve( + `https://cloud-storage.example.com/audio/${Date.now()}.m4a` + ); + return 100; + } + return prev + 10; + }); + }, 200); + }); + }; + + const handleRecordingComplete = async (uri: string) => { + try { + const cloudUrl = await simulateCloudUpload(uri); + + console.log(`Recording uploaded to cloud storage!\n\nURL: ${cloudUrl}`); + + setUploadProgress(0); + } catch (error) { + console.log('Failed to upload recording to cloud storage.'); + + setUploading(false); + setUploadProgress(0); + } + }; + + return ( + + + + {uploading && ( + + + + Uploading... {uploadProgress}% + + + )} + + ); +} + +``` + +##### audio-recorder-interview + +Long-form recorder optimized for interviews and meetings + +```tsx +import { AudioRecorder } from '@/components/ui/audio-recorder'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { RecordingPresets } from 'expo-audio'; +import React, { useState } from 'react'; +import { Alert, StyleSheet } from 'react-native'; + +export function AudioRecorderInterview() { + const [interviewTitle, setInterviewTitle] = useState(''); + const [isRecording, setIsRecording] = useState(false); + + const handleRecordingStart = () => { + setIsRecording(true); + const title = `Interview ${new Date().toLocaleDateString()}`; + setInterviewTitle(title); + }; + + const handleRecordingStop = () => { + setIsRecording(false); + }; + + const handleRecordingComplete = (uri: string) => { + Alert.alert( + '🎤 Interview Complete', + `"${interviewTitle}" has been recorded and saved.\n\nDuration: Available in file metadata\nQuality: High (48kHz, Stereo)`, + [{ text: 'Save & Exit' }] + ); + }; + + return ( + + + + {interviewTitle || 'Ready for Interview'} + + + + + {isRecording ? '🔴 LIVE' : '⏸️ READY'} + + + + + + + + Maximum duration: 2 hours • High quality stereo recording + + + ); +} + +const styles = StyleSheet.create({ + interviewHeader: { + marginBottom: 16, + alignItems: 'center', + }, + interviewTitle: { + marginBottom: 8, + textAlign: 'center', + }, + statusBadge: { + paddingHorizontal: 12, + paddingVertical: 4, + borderRadius: 12, + backgroundColor: '#f3f4f6', + }, + recordingBadge: { + backgroundColor: '#fef2f2', + }, + statusText: { + fontSize: 12, + fontWeight: '600', + color: '#6b7280', + }, + recordingText: { + color: '#dc2626', + }, +}); + +``` +--- + +### audio-waveform + +A customizable audio waveform visualization component with playback progress and interactive seeking capabilities. + +**Installation:** +```bash +npx bna-ui add audio-waveform +``` + +**Registry Dependencies:** text, view + +#### Props Interface + +```typescript +export interface AudioWaveformProps { + data?: number[]; // Audio amplitude data + isPlaying?: boolean; + progress?: number; // 0-100 + onSeek?: (position: number) => void; + onSeekStart?: () => void; + onSeekEnd?: () => void; + style?: ViewStyle; + height?: number; + barCount?: number; + barWidth?: number; + barGap?: number; + activeColor?: string; + inactiveColor?: string; + animated?: boolean; + showProgress?: boolean; + interactive?: boolean; // New prop to enable seeking +} +``` + +**Preview:** + +![audio-waveform preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-29-2025 21-16-42_1.MP4) + +#### Basic Usage + +```tsx +import { Audio-waveform } from '@/components/ui/audio-waveform'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### audio-waveform-demo + +A basic audio waveform with playback controls and progress tracking + +```tsx +import { AudioWaveform } from '@/components/ui/audio-waveform'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useEffect, useState } from 'react'; + +export function AudioWaveformDemo() { + const [isPlaying, setIsPlaying] = useState(false); + const [progress, setProgress] = useState(0); + + // Simulate audio playback progress + useEffect(() => { + let interval: number; + if (isPlaying) { + interval = setInterval(() => { + setProgress((prev) => { + if (prev >= 100) { + setIsPlaying(false); + return 0; + } + return prev + 2; + }); + }, 100); + } + return () => clearInterval(interval); + }, [isPlaying]); + + const handleSeek = (position: number) => { + setProgress(position); + }; + + const togglePlayback = () => { + setIsPlaying(!isPlaying); + }; + + return ( + + + + + + {Math.round(progress)}% Complete + + + ); +} + +``` + +##### audio-waveform-recording + +An animated waveform for recording visualization with real-time audio levels + +```tsx +import { AudioWaveform } from '@/components/ui/audio-waveform'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useEffect, useState } from 'react'; + +export function AudioWaveformRecording() { + const [isRecording, setIsRecording] = useState(false); + const [recordingData, setRecordingData] = useState([]); + const [duration, setDuration] = useState(0); + + // Simulate recording with real-time audio levels + useEffect(() => { + let interval: number; + if (isRecording) { + interval = setInterval(() => { + // Generate random audio level (simulating microphone input) + const newLevel = Math.max( + 0.1, + Math.random() * 0.9 + Math.sin(Date.now() / 200) * 0.2 + ); + + setRecordingData((prev) => { + const newData = [...prev, newLevel]; + // Keep only the last 50 data points + return newData.slice(-50); + }); + + setDuration((prev) => prev + 0.1); + }, 100); + } + return () => clearInterval(interval); + }, [isRecording]); + + const toggleRecording = () => { + if (!isRecording) { + setRecordingData([]); + setDuration(0); + } + setIsRecording(!isRecording); + }; + + const formatDuration = (seconds: number) => { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, '0')}`; + }; + + return ( + + + + + + + + {formatDuration(duration)} + + + {recordingData.length > 0 && !isRecording && ( + + Tap play to preview your recording + + )} + + ); +} + +``` + +##### audio-waveform-interactive + +A waveform with touch-based seeking functionality and custom audio data + +```tsx +import { AudioWaveform } from '@/components/ui/audio-waveform'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useEffect, useState } from 'react'; + +export function AudioWaveformInteractive() { + const [isPlaying, setIsPlaying] = useState(false); + const [progress, setProgress] = useState(0); + const [isSeeking, setIsSeeking] = useState(false); + + // Sample audio data - more realistic pattern + const audioData = [ + 0.2, 0.4, 0.3, 0.6, 0.8, 0.5, 0.7, 0.9, 0.4, 0.3, 0.5, 0.7, 0.6, 0.8, 0.9, + 0.7, 0.5, 0.3, 0.4, 0.6, 0.8, 0.9, 0.7, 0.5, 0.4, 0.6, 0.8, 0.7, 0.5, 0.3, + 0.4, 0.6, 0.9, 0.8, 0.6, 0.4, 0.2, 0.3, 0.5, 0.7, + ]; + + // Auto-play simulation + useEffect(() => { + let interval: number; + if (isPlaying && !isSeeking) { + interval = setInterval(() => { + setProgress((prev) => { + if (prev >= 100) { + setIsPlaying(false); + return 100; + } + return prev + 1; + }); + }, 100); + } + return () => clearInterval(interval); + }, [isPlaying, isSeeking]); + + const handleSeek = (position: number) => { + setProgress(position); + }; + + const handleSeekStart = () => { + setIsSeeking(true); + }; + + const handleSeekEnd = () => { + setIsSeeking(false); + }; + + const togglePlayback = () => { + setIsPlaying(!isPlaying); + }; + + const formatTime = (percentage: number) => { + const totalSeconds = 180; // 3 minutes total + const currentSeconds = (percentage / 100) * totalSeconds; + const minutes = Math.floor(currentSeconds / 60); + const seconds = Math.floor(currentSeconds % 60); + return `${minutes}:${seconds.toString().padStart(2, '0')}`; + }; + + return ( + + + + + + {formatTime(progress)} + + + 3:00 + + + + + + + + + {isSeeking && ( + + Seeking... + + )} + + + ); +} + +``` + +##### audio-waveform-styled + +Multiple themed waveforms with custom colors, dimensions, and styles + +```tsx +import { AudioWaveform } from '@/components/ui/audio-waveform'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function AudioWaveformStyled() { + const [isPlaying1, setIsPlaying1] = useState(false); + const [isPlaying2, setIsPlaying2] = useState(false); + const [isPlaying3, setIsPlaying3] = useState(false); + const [progress1, setProgress1] = useState(35); + const [progress2, setProgress2] = useState(60); + const [progress3, setProgress3] = useState(80); + + const musicData = [ + 0.1, 0.3, 0.5, 0.4, 0.6, 0.8, 0.7, 0.9, 0.6, 0.4, 0.5, 0.7, 0.8, 0.9, 0.7, + 0.5, 0.3, 0.4, 0.6, 0.8, 0.9, 0.7, 0.5, 0.4, 0.6, 0.8, 0.7, 0.5, 0.3, 0.4, + ]; + + const voiceData = [ + 0.2, 0.4, 0.3, 0.5, 0.4, 0.6, 0.5, 0.7, 0.4, 0.3, 0.5, 0.4, 0.6, 0.5, 0.4, + 0.3, 0.4, 0.5, 0.6, 0.4, 0.3, 0.5, 0.4, 0.3, 0.4, 0.5, 0.4, 0.3, 0.2, 0.3, + ]; + + const podcastData = [ + 0.3, 0.5, 0.4, 0.6, 0.5, 0.4, 0.6, 0.7, 0.5, 0.4, 0.6, 0.5, 0.7, 0.6, 0.5, + 0.4, 0.5, 0.6, 0.7, 0.5, 0.4, 0.6, 0.5, 0.4, 0.5, 0.6, 0.5, 0.4, 0.3, 0.4, + ]; + + return ( + + {/* Music Style - Vibrant gradient colors */} + + + 🎵 Music Track + + + + + 2:15 / 3:45 + + + + {/* Voice Message Style - Clean and minimal */} + + + 🎙️ Voice Message + + + + + 0:45 + + + + {/* Podcast Style - Professional dark theme */} + + + 🎧 Podcast Episode + + + + + + + + 45:30 / 58:15 + + + + ); +} + +``` + +##### audio-waveform-realtime + +A waveform that updates with real-time audio data and configurable patterns + +```tsx +import { AudioWaveform } from '@/components/ui/audio-waveform'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useEffect, useState } from 'react'; + +export function AudioWaveformRealtime() { + const [isActive, setIsActive] = useState(false); + const [realtimeData, setRealtimeData] = useState([]); + const [frequency, setFrequency] = useState(1); + const [amplitude, setAmplitude] = useState(0.5); + + // Simulate real-time audio data with different patterns + useEffect(() => { + let interval: number; + if (isActive) { + interval = setInterval(() => { + const time = Date.now() / 1000; + + // Generate different wave patterns based on frequency and amplitude + let newLevel = 0; + + if (frequency === 1) { + // Sine wave + newLevel = Math.abs(Math.sin(time * 2) * amplitude); + } else if (frequency === 2) { + // Multiple frequencies combined + newLevel = Math.abs( + ((Math.sin(time * 3) + + Math.sin(time * 1.5) + + Math.sin(time * 0.8)) / + 3) * + amplitude + ); + } else { + // Random with trend + newLevel = Math.max( + 0, + Math.min(1, Math.random() * amplitude + Math.sin(time * 0.5) * 0.3) + ); + } + + setRealtimeData((prev) => { + const newData = [...prev, newLevel]; + // Keep only the last 60 data points for smooth animation + return newData.slice(-60); + }); + }, 50); + } + return () => clearInterval(interval); + }, [isActive, frequency, amplitude]); + + const resetData = () => { + setRealtimeData([]); + }; + + const patternButtons = [ + { id: 1, label: 'Sine Wave', color: '#007AFF' }, + { id: 2, label: 'Complex', color: '#34C759' }, + { id: 3, label: 'Random', color: '#FF9500' }, + ]; + + return ( + + + + Real-time Audio Visualization + + + + + + + {realtimeData.length} data points + + + {isActive ? 'Live' : 'Stopped'} + + + + + {/* Controls */} + + + + + + + {/* Pattern Selection */} + + + Wave Pattern: + + + {patternButtons.map((pattern) => ( + + ))} + + + + {/* Amplitude Control */} + + + Amplitude: {Math.round(amplitude * 100)}% + + + + + + + + + + + + + + Simulates real-time audio input with different wave patterns + + + + ); +} + +``` + +##### audio-waveform-compact + +Compact waveforms suitable for chat messages and minimal interfaces + +```tsx +import { AudioWaveform } from '@/components/ui/audio-waveform'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function AudioWaveformCompact() { + const [isPlaying1, setIsPlaying1] = useState(false); + const [isPlaying2, setIsPlaying2] = useState(false); + const [isPlaying3, setIsPlaying3] = useState(false); + const [progress1, setProgress1] = useState(20); + const [progress2, setProgress2] = useState(45); + const [progress3, setProgress3] = useState(70); + + // Compact chat message data + const messageData1 = [ + 0.3, 0.5, 0.4, 0.6, 0.3, 0.4, 0.5, 0.3, 0.4, 0.6, 0.5, 0.3, 0.4, 0.5, 0.3, + ]; + const messageData2 = [ + 0.2, 0.4, 0.6, 0.5, 0.3, 0.5, 0.4, 0.6, 0.3, 0.4, 0.5, 0.4, 0.3, 0.2, 0.3, + ]; + const messageData3 = [ + 0.4, 0.6, 0.5, 0.7, 0.4, 0.3, 0.5, 0.6, 0.4, 0.5, 0.6, 0.5, 0.4, 0.3, 0.4, + ]; + + const MessageBubble = ({ + data, + isPlaying, + setIsPlaying, + progress, + setProgress, + duration, + sent = false, + }: { + data: number[]; + isPlaying: boolean; + setIsPlaying: (playing: boolean) => void; + progress: number; + setProgress: (progress: number) => void; + duration: string; + sent?: boolean; + }) => ( + + + + + + + + + {duration} + + + ); + + return ( + + + + + + + + + + ); +} + +``` +--- + +### avatar + +An image element with a fallback for representing the user. + +**Installation:** +```bash +npx bna-ui add avatar +``` + +**External Dependencies:** expo-image + +**Registry Dependencies:** text, view, image + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![avatar preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/IMG_5446.PNG) + +#### Basic Usage + +```tsx +import { Avatar } from '@/components/ui/avatar'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### avatar-demo + +A basic avatar with image and fallback text + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import React from 'react'; + +export function AvatarDemo() { + return ( + + + AB + + ); +} + +``` + +##### avatar-sizes + +Avatars in different sizes + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AvatarSizes() { + return ( + + + + AB + + + + + AB + + + + + AB + + + + + AB + + + + + AB + + + ); +} + +``` + +##### avatar-fallback + +Avatars with fallback text when no image is provided + +```tsx +import { Avatar, AvatarFallback } from '@/components/ui/avatar'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AvatarFallbackDemo() { + return ( + + + JD + + + + AB + + + + MK + + + + SL + + + ); +} + +``` + +##### avatar-styled + +Avatars with custom styling and colors + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AvatarStyled() { + return ( + + + + + AB + + + + + + BNA + + + + + + + EX + + + + ); +} + +``` + +##### avatar-group + +Multiple avatars arranged in a group layout + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AvatarGroup() { + return ( + + + + AB + + + + + AB + + + + + EX + + + + + +5 + + + + ); +} + +``` + +##### avatar-status + +Avatars with online/offline status indicators + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AvatarStatus() { + return ( + + + + + AB + + + + + + + BNA + + + + + + + + EX + + + + + ); +} + +``` + +##### avatar-bordered + +Avatars with custom borders and shadows + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AvatarBordered() { + return ( + + + + AB + + + + BNA + + + + + EX + + + ); +} + +``` +--- + +### avoid-keyboard + +A component that automatically adjusts its height to avoid keyboard overlap with smooth animations and cross-platform support. + +**Installation:** +```bash +npx bna-ui add avoid-keyboard +``` + +**External Dependencies:** react-native-reanimated + +**Required Hooks:** useKeyboardHeight + +**Preview:** + +![avoid-keyboard preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-25-2025 14-49-51_1.MP4) + +#### Basic Usage + +```tsx +import { Avoid-keyboard } from '@/components/ui/avoid-keyboard'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### avoid-keyboard-demo + +Simple keyboard avoidance with default settings + +```tsx +import { AvoidKeyboard } from '@/components/ui/avoid-keyboard'; +import { Input } from '@/components/ui/input'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useKeyboardHeight } from '@/hooks/useKeyboardHeight'; +import React from 'react'; + +export function AvoidKeyboardDemo() { + const { keyboardHeight, isKeyboardVisible, keyboardAnimationDuration } = + useKeyboardHeight(); + + return ( + + + Basic Keyboard Avoidance + + + + Tap the input below to see the keyboard avoidance in action. The content + will smoothly move up to keep the input visible. + + + {/* Spacer to push input toward bottom */} + + + Keyboard Height: {keyboardHeight} + Keyboard Visible: {isKeyboardVisible ? 'Yes' : 'No'} + Animation Duration: {keyboardAnimationDuration}ms + + + + + {/* This will create space to avoid the keyboard */} + + + ); +} + +``` + +##### avoid-keyboard-offset + +Add extra spacing above the keyboard + +```tsx +import { AvoidKeyboard } from '@/components/ui/avoid-keyboard'; +import { Input } from '@/components/ui/input'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function AvoidKeyboardOffset() { + return ( + + + With Extra Offset + + + + This example adds 40px of extra spacing above the keyboard for better + visual separation. + + + {/* Spacer to push input toward bottom */} + + + + + {/* Add 40px extra spacing above keyboard */} + + + ); +} + +``` + +##### avoid-keyboard-duration + +Customize animation timing for different effects + +```tsx +import { AvoidKeyboard } from '@/components/ui/avoid-keyboard'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function AvoidKeyboardDuration() { + const [duration, setDuration] = useState(0); + + const durations = [ + { label: 'Default', value: 0 }, + { label: 'Fast (100ms)', value: 100 }, + { label: 'Slow (500ms)', value: 500 }, + { label: 'Very Slow (1000ms)', value: 1000 }, + ]; + + return ( + + + Custom Animation Duration + + + + Choose different animation speeds to see how it affects the keyboard + avoidance: + + + + {durations.map((item) => ( + + ))} + + + + Current duration: {duration}ms extra + + + {/* Spacer to push input toward bottom */} + + + + + {/* Use custom duration */} + + + ); +} + +``` + +##### avoid-keyboard-chat + +Real-world chat interface example + +```tsx +import { AvoidKeyboard } from '@/components/ui/avoid-keyboard'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { Send, SendHorizonal } from 'lucide-react-native'; +import React, { useState } from 'react'; +import { FlatList, Pressable } from 'react-native'; + +interface Message { + id: string; + text: string; + isUser: boolean; + timestamp: Date; +} + +export function AvoidKeyboardChat() { + const card = useThemeColor({}, 'card'); + const blue = useThemeColor({}, 'blue'); + + const [messages, setMessages] = useState([ + { + id: '1', + text: 'Hey! How are you doing?', + isUser: false, + timestamp: new Date(Date.now() - 300000), + }, + { + id: '2', + text: "Hi there! I'm doing great, thanks for asking!", + isUser: true, + timestamp: new Date(Date.now() - 240000), + }, + { + id: '3', + text: "That's wonderful to hear! Any exciting plans for today?", + isUser: false, + timestamp: new Date(Date.now() - 180000), + }, + { + id: '4', + text: "Actually yes! I'm working on some new React Native components.", + isUser: true, + timestamp: new Date(Date.now() - 120000), + }, + ]); + const [inputText, setInputText] = useState(''); + + const sendMessage = () => { + if (inputText.trim()) { + const newMessage: Message = { + id: Date.now().toString(), + text: inputText.trim(), + isUser: true, + timestamp: new Date(), + }; + setMessages((prev) => [...prev, newMessage]); + setInputText(''); + + // Simulate response after a delay + setTimeout(() => { + const responses = [ + 'That sounds interesting!', + 'Tell me more about that.', + "Cool! How's it going?", + 'Nice work!', + ]; + const response: Message = { + id: (Date.now() + 1).toString(), + text: responses[Math.floor(Math.random() * responses.length)], + isUser: false, + timestamp: new Date(), + }; + setMessages((prev) => [...prev, response]); + }, 1000); + } + }; + + const renderMessage = ({ item }: { item: Message }) => ( + + + + {item.text} + + + {item.timestamp.toLocaleTimeString([], { + hour: '2-digit', + minute: '2-digit', + })} + + + + ); + + return ( + + {/* Header */} + + Chat Demo + + Real-time chat with keyboard avoidance + + + + {/* Messages */} + item.id} + style={{ flex: 1 }} + contentContainerStyle={{ padding: 16 }} + showsVerticalScrollIndicator={false} + /> + + {/* Input Area */} + + + + + + + + {/* Keyboard avoidance with extra space for better UX */} + + + ); +} + +``` + +##### avoid-keyboard-form + +Form with multiple inputs and keyboard avoidance + +```tsx +import { AvoidKeyboard } from '@/components/ui/avoid-keyboard'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Mail, Lock, User, Phone, MessageSquare } from 'lucide-react-native'; +import React, { useState } from 'react'; +import { ScrollView } from 'react-native'; + +export function AvoidKeyboardForm() { + const [formData, setFormData] = useState({ + first: '', + last: '', + email: '', + phone: '', + password: '', + confrim: '', + message: '', + }); + + const [errors, setErrors] = useState>({}); + + const validateForm = () => { + const newErrors: Record = {}; + + if (!formData.first.trim()) { + newErrors.first = 'Name is required'; + } + + if (!formData.email.trim()) { + newErrors.email = 'Email is required'; + } else if (!/\S+@\S+\.\S+/.test(formData.email)) { + newErrors.email = 'Please enter a valid email'; + } + + if (!formData.password.trim()) { + newErrors.password = 'Password is required'; + } else if (formData.password.length < 6) { + newErrors.password = 'Password must be at least 6 characters'; + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = () => { + if (validateForm()) { + // Form is valid + console.log('Form submitted:', formData); + // Reset form + setFormData({ + first: '', + last: '', + email: '', + phone: '', + password: '', + confrim: '', + message: '', + }); + setErrors({}); + } + }; + + const updateField = (field: keyof typeof formData, value: string) => { + setFormData((prev) => ({ ...prev, [field]: value })); + // Clear error when user starts typing + if (errors[field]) { + setErrors((prev) => ({ ...prev, [field]: '' })); + } + }; + + return ( + + {/* Header */} + + + Registration Form + + + Fill out the form below. Notice how the keyboard avoidance keeps + inputs visible. + + + + {/* Form Content */} + + updateField('first', value)} + error={errors.first} + /> + + updateField('last', value)} + error={errors.last} + /> + + updateField('email', value)} + error={errors.email} + keyboardType='email-address' + autoCapitalize='none' + /> + + updateField('email', value)} + error={errors.email} + keyboardType='email-address' + autoCapitalize='none' + /> + + updateField('phone', value)} + error={errors.phone} + keyboardType='phone-pad' + /> + + updateField('password', value)} + error={errors.password} + secureTextEntry + /> + + updateField('confrim', value)} + error={errors.confrim} + secureTextEntry + /> + + + + + By creating an account, you agree to our Terms of Service and Privacy + Policy. + + + + {/* Keyboard avoidance for the form */} + + + ); +} + +``` + +##### avoid-keyboard-playground + +Playground to test different configurations + +```tsx +import { AvoidKeyboard } from '@/components/ui/avoid-keyboard'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useKeyboardHeight } from '@/hooks/useKeyboardHeight'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { Settings, Keyboard, Smartphone } from 'lucide-react-native'; +import React, { useState } from 'react'; +import { ScrollView, Switch } from 'react-native'; + +export function AvoidKeyboardPlayground() { + const [message, setMessage] = useState(''); + const [offset, setOffset] = useState(20); + const [duration, setDuration] = useState(0); + const [showStats, setShowStats] = useState(false); + + const card = useThemeColor({}, 'card'); + + // Get keyboard stats for debugging + const { keyboardHeight, isKeyboardVisible, keyboardAnimationDuration } = + useKeyboardHeight(); + + const presetOffsets = [0, 10, 20, 40, 60]; + const presetDurations = [0, 100, 250, 500]; + + return ( + + {/* Controls */} + + {/* Header */} + + + AvoidKeyboard Playground + + + Test different configurations and see real-time keyboard stats + + + + {/* Debug Stats Toggle */} + + + + Show Keyboard Stats + + + + + {/* Keyboard Stats */} + {showStats && ( + + + + + Keyboard Status + + + + Visible: {isKeyboardVisible ? '✅ Yes' : '❌ No'} + + + Height: {keyboardHeight}px + + + Animation Duration: {keyboardAnimationDuration}ms + + + )} + + {/* Offset Controls */} + + + Offset Configuration + + + Extra space above keyboard: {offset}px + + + + {presetOffsets.map((value) => ( + + ))} + + + + {/* Duration Controls */} + + + Animation Duration + + + Extra animation time: {duration}ms + + + {presetDurations.map((value) => ( + + ))} + + + + {/* Usage Examples */} + + + Code Example + + + + {` 0 ? ` offset={${offset}}` : ''}${ + duration > 0 ? ` duration={${duration}}` : '' + } />`} + + + + + {/* Spacer to push test input to bottom */} + + + + {/* Test Input Area */} + + + + + Test Input + + + + + + + + + + + + {/* The actual AvoidKeyboard component */} + + + ); +} + +``` +--- + +### badge + +A small status descriptor for UI elements. + +**Installation:** +```bash +npx bna-ui add badge +``` + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![badge preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/IMG_5453.PNG) + +#### Basic Usage + +```tsx +import { Badge } from '@/components/ui/badge'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### badge-demo + +Basic badges showing all available variants + +```tsx +import { Badge } from '@/components/ui/badge'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BadgeDemo() { + return ( + + Default + Secondary + Destructive + Outline + Success + + ); +} + +``` + +##### badge-icons + +Badges with icons and custom content + +```tsx +import { Badge } from '@/components/ui/badge'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BadgeIcons() { + return ( + + ★ Featured + + + + + Verified + + + + + + + Alert + + + + + + 🔔 + Notification + + + + ); +} + +``` + +##### badge-notifications + +Small notification badges for counters and status + +```tsx +import { Badge } from '@/components/ui/badge'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BadgeNotifications() { + return ( + + {/* Small notification counters */} + + + Messages + + 3 + + + + + Notifications + + 12 + + + + + {/* Dot indicators */} + + + Online + + + + + + + Away + + + + + + + Offline + + + + + + + ); +} + +``` + +##### badge-styled + +Badges with custom colors and styling + +```tsx +import { Badge } from '@/components/ui/badge'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BadgeStyled() { + return ( + + {/* Custom colors */} + + Purple + + + + Cyan + + + + Orange + + + {/* Gradient-like effect with shadow */} + + Pink + + + {/* Bordered with custom style */} + + Green + + + ); +} + +``` + +##### badge-interactive + +Badges that can be pressed or dismissed + +```tsx +import { Badge } from '@/components/ui/badge'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; +import { TouchableOpacity } from 'react-native'; + +export function BadgeInteractive() { + const [tags, setTags] = useState(['React', 'TypeScript', 'Expo', 'Mobile']); + const [selectedCategory, setSelectedCategory] = useState('All'); + + const categories = ['All', 'Work', 'Personal', 'Important']; + + const removeTag = (tagToRemove: string) => { + setTags(tags.filter((tag) => tag !== tagToRemove)); + }; + + return ( + + {/* Dismissible tags */} + + + Tags (tap to remove): + + + {tags.map((tag) => ( + removeTag(tag)}> + + + {tag} + × + + + + ))} + + + + {/* Selectable categories */} + + Categories: + + {categories.map((category) => ( + setSelectedCategory(category)} + > + + {category} + + + ))} + + + + {/* Toggle badges */} + + + Filter Options: + + + + Active + + + Completed + + + Archived + + + + + ); +} + +``` + +##### badge-sizes + +Badges in different sizes + +```tsx +import { Badge } from '@/components/ui/badge'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BadgeSizes() { + return ( + + {/* Extra Small */} + + Extra Small: + + XS + + + New + + + + {/* Small */} + + Small: + + Small + + + Beta + + + + {/* Default */} + + Default: + Default + Outline + + + {/* Large */} + + Large: + + Large + + + Important + + + + {/* Extra Large */} + + Extra Large: + + XL Badge + + + + ); +} + +``` + +##### badge-status + +Badges used as status indicators + +```tsx +import { Badge } from '@/components/ui/badge'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BadgeStatus() { + const users = [ + { name: 'John Doe', status: 'online' }, + { name: 'Jane Smith', status: 'away' }, + { name: 'Bob Johnson', status: 'offline' }, + { name: 'Alice Brown', status: 'busy' }, + ]; + + const orders = [ + { id: '#1234', status: 'pending' }, + { id: '#1235', status: 'processing' }, + { id: '#1236', status: 'shipped' }, + { id: '#1237', status: 'delivered' }, + { id: '#1238', status: 'cancelled' }, + ]; + + const getStatusBadge = (status: string) => { + switch (status) { + case 'online': + return Online; + case 'away': + return ( + + Away + + ); + case 'busy': + return Busy; + case 'offline': + return Offline; + case 'pending': + return ( + + Pending + + ); + case 'processing': + return ( + + Processing + + ); + case 'shipped': + return ( + + Shipped + + ); + case 'delivered': + return Delivered; + case 'cancelled': + return Cancelled; + default: + return Unknown; + } + }; + + return ( + + {/* User Status */} + + + User Status + + + {users.map((user, index) => ( + + {user.name} + {getStatusBadge(user.status)} + + ))} + + + + {/* Order Status */} + + + Order Status + + + {orders.map((order, index) => ( + + Order {order.id} + {getStatusBadge(order.status)} + + ))} + + + + {/* Priority Levels */} + + + Priority Levels + + + + High Priority + + + Medium Priority + + + Low Priority + + + + + ); +} + +``` +--- + +### bar-chart + +A customizable bar chart component with smooth animations and rounded corners. + +**Installation:** +```bash +npx bna-ui add bar-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![bar-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 15-22-22_1.mov) + +#### Basic Usage + +```tsx +import { Bar-chart } from '@/components/ui/bar-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### bar-chart-demo + +A basic bar chart with smooth animations and rounded corners + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { BarChart } from '@/components/charts/bar-chart'; +import React from 'react'; + +const sampleData = [ + { label: 'Jan', value: 65, color: '#3b82f6' }, + { label: 'Feb', value: 78, color: '#ef4444' }, + { label: 'Mar', value: 52, color: '#10b981' }, + { label: 'Apr', value: 91, color: '#f59e0b' }, + { label: 'May', value: 73, color: '#8b5cf6' }, + { label: 'Jun', value: 85, color: '#06b6d4' }, +]; + +export function BarChartDemo() { + return ( + + + + ); +} + +``` + +##### bar-chart-sample + +A sample bar chart with custom colors + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { BarChart } from '@/components/charts/bar-chart'; +import React from 'react'; + +const sampleData = [ + { label: 'Product A', value: 120, color: '#3b82f6' }, + { label: 'Product B', value: 98, color: '#ef4444' }, + { label: 'Product C', value: 86, color: '#10b981' }, + { label: 'Product D', value: 74, color: '#f59e0b' }, + { label: 'Product E', value: 65, color: '#8b5cf6' }, +]; + +export function BarChartSample() { + return ( + + + + ); +} + +``` + +##### bar-chart-minimal + +A minimal bar chart without labels + +```tsx +import { BarChart } from '@/components/charts/bar-chart'; +import React from 'react'; + +const sampleData = [ + { label: 'A', value: 30 }, + { label: 'B', value: 50 }, + { label: 'C', value: 25 }, + { label: 'D', value: 70 }, + { label: 'E', value: 45 }, + { label: 'F', value: 60 }, +]; + +export function BarChartMinimal() { + return ( + + ); +} + +``` +--- + +### bottom-sheet + +A modal sheet component that slides up from the bottom with gesture support and snap points. + +**Installation:** +```bash +npx bna-ui add bottom-sheet +``` + +**External Dependencies:** react-native-gesture-handler, react-native-reanimated + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![bottom-sheet preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-29-2025 22-49-58_1.MP4) + +#### Basic Usage + +```tsx +import { Bottom-sheet } from '@/components/ui/bottom-sheet'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### bottom-sheet-demo + +A basic bottom sheet with gesture support and snap points + +```tsx +import { BottomSheet, useBottomSheet } from '@/components/ui/bottom-sheet'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BottomSheetDemo() { + const { isVisible, open, close } = useBottomSheet(); + + return ( + + + + + + Welcome to Bottom Sheet + + This is a basic bottom sheet that supports gesture interactions. You + can drag it up and down to different snap points, or swipe down + quickly to dismiss it. + + + + + + ); +} + +``` + +##### bottom-sheet-title + +Bottom sheet with a title header + +```tsx +import { BottomSheet, useBottomSheet } from '@/components/ui/bottom-sheet'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BottomSheetTitle() { + const { isVisible, open, close } = useBottomSheet(); + + return ( + + + + + + + This bottom sheet includes a title in the header area. The title is + centered and uses the theme's title text style. + + + + + + ); +} + +``` + +##### bottom-sheet-snap-points + +Bottom sheet with custom snap point configurations + +```tsx +import { BottomSheet, useBottomSheet } from '@/components/ui/bottom-sheet'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BottomSheetSnapPoints() { + const { isVisible, open, close } = useBottomSheet(); + + return ( + + + + + + Multiple Snap Points + + This sheet has four different snap points: 20%, 50%, 80%, and 95% of + screen height. Try dragging to see how it snaps to each position. + + + Available heights: + • 20% - Peek view + • 50% - Medium height + • 80% - Large view + • 95% - Nearly fullscreen + + + + + ); +} + +``` + +##### bottom-sheet-form + +Bottom sheet containing form elements and inputs + +```tsx +import { BottomSheet, useBottomSheet } from '@/components/ui/bottom-sheet'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function BottomSheetForm() { + const { isVisible, open, close } = useBottomSheet(); + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + + const handleSubmit = () => { + // Handle form submission + console.log('Form submitted:', { name, email }); + close(); + }; + + return ( + + + + + + + Name + + + + + Email + + + + + + + + + + + ); +} + +``` + +##### bottom-sheet-list + +Bottom sheet with scrollable list content + +```tsx +import { BottomSheet, useBottomSheet } from '@/components/ui/bottom-sheet'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; +import { FlatList, TouchableOpacity } from 'react-native'; + +const items = [ + { id: '1', title: 'Photos', subtitle: '1,234 items' }, + { id: '2', title: 'Videos', subtitle: '56 items' }, + { id: '3', title: 'Documents', subtitle: '89 items' }, + { id: '4', title: 'Audio', subtitle: '23 items' }, + { id: '5', title: 'Downloads', subtitle: '12 items' }, + { id: '6', title: 'Archives', subtitle: '4 items' }, +]; + +export function BottomSheetList() { + const { isVisible, open, close } = useBottomSheet(); + + const renderItem = ({ item }: { item: (typeof items)[0] }) => ( + console.log('Selected:', item.title)} + > + + {item.title} + + + {item.subtitle} + + + ); + + return ( + + + + + item.id} + showsVerticalScrollIndicator={false} + /> + + + ); +} + +``` + +##### bottom-sheet-no-dismiss + +Bottom sheet that cannot be dismissed by tapping backdrop + +```tsx +import { BottomSheet, useBottomSheet } from '@/components/ui/bottom-sheet'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function BottomSheetNoDismiss() { + const { isVisible, open, close } = useBottomSheet(); + + return ( + + + + + + + This bottom sheet cannot be dismissed by tapping the backdrop. You + must use one of the action buttons below. + + + This is useful for critical confirmations or required actions. + + + + + + + + + ); +} + +``` + +##### bottom-sheet-styled + +Bottom sheet with custom styling and colors + +```tsx +import { BottomSheet, useBottomSheet } from '@/components/ui/bottom-sheet'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +export function BottomSheetStyled() { + const { isVisible, open, close } = useBottomSheet(); + const accentColor = useThemeColor({}, 'blue'); + + return ( + + + + + + + + Premium Feature + + + This bottom sheet has custom styling including a colored border + and accent-colored content areas. + + + + + + + + ); +} + +``` + +##### bottom-sheet-menu + +Bottom sheet used as a menu with action items + +```tsx +import { BottomSheet, useBottomSheet } from '@/components/ui/bottom-sheet'; +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { TouchableOpacity } from 'react-native'; + +const menuItems = [ + { id: 'edit', title: 'Edit', icon: '✏️' }, + { id: 'share', title: 'Share', icon: '📤' }, + { id: 'copy', title: 'Copy Link', icon: '🔗' }, + { id: 'bookmark', title: 'Bookmark', icon: '🔖' }, + { id: 'delete', title: 'Delete', icon: '🗑️', destructive: true }, +]; + +export function BottomSheetMenu() { + const { isVisible, open, close } = useBottomSheet(); + + const textColor = useThemeColor({}, 'text'); + const destructiveColor = useThemeColor({}, 'destructive'); + + const handleMenuAction = (action: string) => { + console.log('Menu action:', action); + close(); + }; + + return ( + + + + + + {menuItems.map((item, index) => ( + handleMenuAction(item.id)} + > + {item.icon} + + {item.title} + + + ))} + + + + ); +} + +``` +--- + +### bubble-chart + +A customizable bubble chart component with animations, size mapping, and interactive features. + +**Installation:** +```bash +npx bna-ui add bubble-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![bubble-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 15-40-44_1.mov) + +#### Basic Usage + +```tsx +import { Bubble-chart } from '@/components/ui/bubble-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### bubble-chart-demo + +A basic bubble chart with animated bubbles and grid lines + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { BubbleChart } from '@/components/charts/bubble-chart'; +import React from 'react'; + +const sampleData = [ + { x: 10, y: 20, size: 15, label: 'A' }, + { x: 25, y: 30, size: 25, label: 'B' }, + { x: 40, y: 15, size: 30, label: 'C' }, + { x: 35, y: 45, size: 20, label: 'D' }, + { x: 60, y: 25, size: 18, label: 'E' }, + { x: 50, y: 40, size: 22, label: 'F' }, + { x: 15, y: 35, size: 28, label: 'G' }, + { x: 70, y: 50, size: 16, label: 'H' }, +]; + +export function BubbleChartDemo() { + return ( + + + + ); +} + +``` + +##### bubble-chart-sample + +A sample bubble chart + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { BubbleChart } from '@/components/charts/bubble-chart'; +import React from 'react'; + +const sampleData = [ + { x: 20, y: 30, size: 45, label: 'Sales', color: '#FF6B6B' }, + { x: 35, y: 25, size: 35, label: 'Marketing', color: '#4ECDC4' }, + { x: 50, y: 40, size: 25, label: 'Dev', color: '#45B7D1' }, + { x: 65, y: 35, size: 30, label: 'Support', color: '#96CEB4' }, + { x: 40, y: 50, size: 20, label: 'HR', color: '#FFEAA7' }, + { x: 25, y: 45, size: 15, label: 'Finance', color: '#DDA0DD' }, +]; + +export function BubbleChartSample() { + return ( + + + + ); +} + +``` + +##### bubble-chart-styled + +A customized bubble chart with custom styling + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { BubbleChart } from '@/components/charts/bubble-chart'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const styledData = [ + { x: 15, y: 25, size: 40, label: 'Q1', color: '#FF6B6B' }, + { x: 30, y: 35, size: 50, label: 'Q2', color: '#4ECDC4' }, + { x: 45, y: 30, size: 35, label: 'Q3', color: '#45B7D1' }, + { x: 60, y: 45, size: 45, label: 'Q4', color: '#96CEB4' }, + { x: 25, y: 50, size: 25, label: 'Bonus', color: '#FFEAA7' }, +]; + +export function BubbleChartStyled() { + const backgroundColor = useThemeColor({}, 'card'); + + return ( + + + + ); +} + +``` + +##### bubble-chart-minimal + +A minimal bubble chart without labels or grid + +```tsx +import { BubbleChart } from '@/components/charts/bubble-chart'; +import React from 'react'; + +const minimalData = [ + { x: 20, y: 30, size: 25 }, + { x: 40, y: 45, size: 35 }, + { x: 60, y: 25, size: 20 }, + { x: 35, y: 55, size: 30 }, + { x: 70, y: 40, size: 15 }, +]; + +export function BubbleChartMinimal() { + return ( + + ); +} + +``` +--- + +### button + +A versatile button component with multiple variants, sizes, and interactive animations. + +**Installation:** +```bash +npx bna-ui add button +``` + +**External Dependencies:** expo-haptics, lucide-react-native, react-native-reanimated + +**Registry Dependencies:** text, icon, spinner + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Props Interface + +```typescript +export interface ButtonProps extends Omit { + label?: string; + children?: React.ReactNode; + animation?: boolean; + haptic?: boolean; + icon?: React.ComponentType; + onPress?: () => void; + variant?: ButtonVariant; + size?: ButtonSize; + disabled?: boolean; + loading?: boolean; + loadingVariant?: SpinnerVariant; + style?: ViewStyle | ViewStyle[]; + textStyle?: TextStyle; +} +``` + +#### Types + +```typescript +export type ButtonVariant = + | 'default' + | 'destructive' + | 'success' + | 'outline' + | 'secondary' + | 'ghost' + | 'link' + +export type ButtonSize = 'default' | 'sm' | 'lg' | 'icon' +``` + +**Preview:** + +![button preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 00-44-50_1.MP4) + +#### Basic Usage + +```tsx +import React from 'react'; +import { Button } from '@/components/ui/button'; +import { Card } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { View } from '@/components/ui/view'; + +export default function HomeScreen() { + return ( + + + + + + + ); +} +``` + + +#### Advanced Examples + +##### button-demo + +A basic button with default styling + +```tsx +import { Button } from '@/components/ui/button'; +import React from 'react'; + +export function ButtonDemo() { + return ( + + ); +} + +``` + +##### button-variants + +Buttons with different visual styles + +```tsx +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ButtonVariants() { + return ( + + + + + + + + + + + + ); +} + +``` + +##### button-sizes + +Buttons in different sizes + +```tsx +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ButtonSizes() { + return ( + + + + + + ); +} + +``` + +##### button-with-icons + +Buttons with leading icons + +```tsx +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; +import { Download, Mail, Plus, Search } from 'lucide-react-native'; +import React from 'react'; + +export function ButtonWithIcons() { + return ( + + + + + + + ); +} + +``` + +##### button-icon-only + +Icon-only buttons for compact layouts + +```tsx +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; +import { + Heart, + MessageCircle, + MoreHorizontal, + Settings, + Share, +} from 'lucide-react-native'; +import React from 'react'; + +export function ButtonIconOnly() { + return ( + + + + + + + ); +} + +``` + +##### button-disabled + +Buttons in disabled state + +```tsx +import { Button } from '@/components/ui/button'; +import { View } from '@/components/ui/view'; +import { Lock } from 'lucide-react-native'; +import React from 'react'; + +export function ButtonDisabled() { + return ( + + + + + + + + + + + + ); +} + +``` + +##### button-animation + +Buttons with and without animations + +```tsx +import { Button } from '@/components/ui/button'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ButtonAnimation() { + return ( + + + + With Animation (default) + + + + + + + Without Animation + + + + + ); +} + +``` + +#### Props Examples + +```tsx +// Different variants + + +``` + +```tsx +// Different sizes + + +```#### Practical Examples + +```tsx +// Form submission button + +``` + +```tsx +// Icon button + + + + ); +} +``` + + +#### Advanced Examples + +##### card-demo + +A complete card with header, content, and footer sections + +```tsx +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Text } from '@/components/ui/text'; +import React from 'react'; + +export function CardDemo() { + return ( + + + Card Title + + This is a description of the card content. It provides additional + context about what this card contains. + + + + + This is the main content area of the card. You can put any content + here including text, images, forms, or other components. + + + + + + + + ); +} + +``` + +##### card-simple + +A minimal card with just content + +```tsx +import { Card, CardContent } from '@/components/ui/card'; +import { Text } from '@/components/ui/text'; +import React from 'react'; + +export function CardSimple() { + return ( + + + + A simple card with just content. Perfect for displaying basic + information or messages. + + + + ); +} + +``` + +##### card-with-image + +Card featuring an image with content below + +```tsx +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; +import { Image } from 'react-native'; + +export function CardWithImage() { + return ( + + + + + + Beautiful Landscape + + A stunning view captured in the mountains during golden hour. + + + + + This image showcases the beauty of nature with its vibrant colors and + serene atmosphere. + + + + + + + + ); +} + +``` + +##### card-with-form + +Interactive card containing a login form + +```tsx +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; +import { TextInput } from 'react-native'; + +export function CardWithForm() { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const borderColor = useThemeColor({}, 'border'); + const backgroundColor = useThemeColor({}, 'background'); + const textColor = useThemeColor({}, 'text'); + + return ( + + + Sign In + + Enter your credentials to access your account. + + + + + + Email + + + + Password + + + + + + + + + + ); +} + +``` + +##### card-stats + +Grid of cards displaying key metrics and statistics + +```tsx +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function CardStats() { + const stats = [ + { title: 'Total Users', value: '12,543', change: '+12%' }, + { title: 'Revenue', value: '$45,231', change: '+8%' }, + { title: 'Orders', value: '1,234', change: '+23%' }, + { title: 'Growth', value: '15.3%', change: '+4%' }, + ]; + + return ( + + {stats.map((stat, index) => ( + + + {stat.title} + + + + {stat.value} + + + {stat.change} from last month + + + + ))} + + ); +} + +``` + +##### card-notification + +Card designed for displaying notifications with actions + +```tsx +import { Button } from '@/components/ui/button'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Bell } from 'lucide-react-native'; +import React from 'react'; + +export function CardNotification() { + return ( + + + + + + + + New Notification + 2 minutes ago + + + + + + You have a new message from John Doe. Click to view the full + conversation and respond. + + + + + + + + ); +} + +``` + +##### card-pricing + +Professional pricing cards with feature lists and CTAs + +```tsx +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from '@/components/ui/card'; +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Check } from 'lucide-react-native'; +import React from 'react'; + +export function CardPricing() { + const plans = [ + { + name: 'Basic', + price: '$9', + description: 'Perfect for individuals', + features: ['1 Project', '5GB Storage', 'Email Support'], + popular: false, + }, + { + name: 'Pro', + price: '$29', + description: 'Best for small teams', + features: [ + '10 Projects', + '100GB Storage', + // 'Priority Support', + // 'Advanced Analytics', + ], + popular: true, + }, + { + name: 'Enterprise', + price: '$99', + description: 'For large organizations', + features: [ + 'Unlimited Projects', + '1TB Storage', + // '24/7 Support', + // 'Custom Integrations', + ], + popular: false, + }, + ]; + + return ( + + {plans.map((plan, index) => ( + + + + {plan.popular && ( + + + POPULAR + + + )} + {plan.name} + {plan.description} + + {plan.price} + + /month + + + + + + + {plan.features.map((feature, featureIndex) => ( + + + {feature} + + ))} + + + {/* + + */} + + ))} + + ); +} + +``` +--- + +### carousel + +A flexible carousel component with support for auto-play, indicators, arrows, and custom layouts. + +**Installation:** +```bash +npx bna-ui add carousel +``` + +**External Dependencies:** expo-blur, lucide-react-native, react-native-gesture-handler + +**Registry Dependencies:** text + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![carousel preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-11-52_1.MP4) + +#### Basic Usage + +```tsx +import { Carousel } from '@/components/ui/carousel'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### carousel-demo + +A basic carousel with auto-play and indicators + +```tsx +import { Carousel, CarouselItem } from '@/components/ui/carousel'; +import { Text } from '@/components/ui/text'; + +export function CarouselDemo() { + return ( + + {slides.map((slide) => ( + + {slide.title} + + {slide.content} + + + ))} + + ); +} + +const slides = [ + { + id: 1, + title: 'Full Width Slide 1', + content: 'This slide takes the full width of the container', + }, + { + id: 2, + title: 'Full Width Slide 2', + content: 'Perfect for hero sections and main content', + }, + { + id: 3, + title: 'Full Width Slide 3', + content: 'Uses paging for smooth navigation', + }, + { + id: 4, + title: 'Full Width Slide 4', + content: 'Default behavior - no spacing needed', + }, +]; + +``` + +##### carousel-arrows + +Carousel with navigation arrows and indicators + +```tsx +import { Carousel, CarouselItem } from '@/components/ui/carousel'; +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { Award, Heart, Star, Zap } from 'lucide-react-native'; + +export function CarouselArrows() { + const slides = [ + { + icon: Heart, + title: 'Loved by Users', + description: 'Thousands of happy customers worldwide trust our products.', + color: '#fee2e2', + bg: '#FF6B6B', + }, + { + icon: Zap, + title: 'Lightning Fast', + description: 'Optimized for performance with smooth animations.', + color: '#f3e8ff', + bg: '#8b5cf6', + }, + { + icon: Star, + title: 'Premium Quality', + description: 'Built with the highest standards and attention to detail.', + color: '#f59e0b', + bg: '#fef3c7', + }, + { + icon: Award, + title: 'Award Winning', + description: 'Recognized for excellence in design and functionality.', + color: '#d1fae5', + bg: '#4ECDC4', + }, + ]; + + return ( + + {slides.map((slide, index) => { + const name = slide.icon; + return ( + + + + {slide.title} + + + {slide.description} + + + ); + })} + + ); +} + +``` + +##### carousel-custom-width + +Carousel with custom item width and spacing + +```tsx +import { Carousel, CarouselItem } from '@/components/ui/carousel'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; +import { Dimensions } from 'react-native'; + +const { width: screenWidth } = Dimensions.get('window'); + +export function CarouselCustomWidth() { + const cardColor = useThemeColor({}, 'card'); + const textColor = useThemeColor({}, 'text'); + + const products = [ + { id: 1, name: 'Wireless Headphones', price: '$99.99', rating: '4.8' }, + { id: 2, name: 'Smart Watch', price: '$199.99', rating: '4.9' }, + { id: 3, name: 'Bluetooth Speaker', price: '$79.99', rating: '4.7' }, + { id: 4, name: 'Phone Case', price: '$24.99', rating: '4.6' }, + { id: 5, name: 'Wireless Charger', price: '$39.99', rating: '4.8' }, + ]; + + return ( + + + {products.map((product) => ( + + + + + {product.name} + + + ⭐ {product.rating} rating + + + + {product.price} + + + + ))} + + + + Swipe to see more products → + + + ); +} + +``` + +##### carousel-images + +Image carousel with auto-play and loop + +```tsx +import { Carousel, CarouselItem } from '@/components/ui/carousel'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { BORDER_RADIUS } from '@/theme/globals'; +import { Image } from 'expo-image'; +import React from 'react'; + +export function CarouselImages() { + const images = [ + { + uri: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=300&fit=crop', + title: 'Mountain Landscape', + description: 'Breathtaking mountain views', + }, + { + uri: 'https://images.unsplash.com/photo-1439066615861-d1af74d74000?w=400&h=300&fit=crop', + title: 'Forest Path', + description: 'Peaceful forest walking trail', + }, + { + uri: 'https://images.unsplash.com/photo-1501436513145-30f24e19fcc4?w=400&h=300&fit=crop', + title: 'Ocean Sunset', + description: 'Golden hour by the sea', + }, + { + uri: 'https://images.unsplash.com/photo-1500375592092-40eb2168fd21?w=400&h=300&fit=crop', + title: 'Desert Dunes', + description: 'Vast desert landscape', + }, + ]; + + return ( + + {images.map((image, index) => ( + + + + + + {image.title} + + + {image.description} + + + + + ))} + + ); +} + +``` + +##### carousel-cards + +Card-based carousel with custom content + +```tsx +import { Carousel, CarouselItem } from '@/components/ui/carousel'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { Calendar, MapPin, User } from 'lucide-react-native'; +import React from 'react'; + +export function CarouselCards() { + const cardColor = useThemeColor({}, 'card'); + const textColor = useThemeColor({}, 'text'); + const primaryColor = useThemeColor({}, 'primary'); + + const events = [ + { + title: 'Tech Summit', + location: 'New York, NY', + date: 'Apr 8-10, 2024', + attendees: 2100, + category: 'Technology', + color: '#10b981', + }, + { + title: 'Creative Workshop', + location: 'Los Angeles, CA', + date: 'May 22-23, 2024', + attendees: 850, + category: 'Creative', + color: '#f59e0b', + }, + { + title: 'Design Conference 2024', + location: 'San Francisco, CA', + date: 'Mar 15-17, 2024', + attendees: 1250, + category: 'Design', + color: '#3b82f6', + }, + ]; + + return ( + + {events.map((event, index) => ( + + + {/* Category Badge */} + + + {event.category} + + + + {/* Event Title */} + + {event.title} + + + {/* Event Details */} + + + + + {event.location} + + + + + + {event.date} + + + + + + {event.attendees.toLocaleString()} attendees + + + + + {/* Action Button */} + + + Learn More + + + + + ))} + + ); +} + +``` + +##### carousel-no-indicators + +Carousel without indicators, arrows only + +```tsx +import { Carousel, CarouselItem } from '@/components/ui/carousel'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { Minus, TrendingDown, TrendingUp } from 'lucide-react-native'; +import React from 'react'; + +export function CarouselNoIndicators() { + const textColor = useThemeColor({}, 'text'); + + const stats = [ + { + title: 'Revenue', + value: '$24,500', + change: '+12.5%', + trend: 'up', + color: '#10b981', + }, + { + title: 'Users', + value: '8,432', + change: '+5.2%', + trend: 'up', + color: '#3b82f6', + }, + { + title: 'Orders', + value: '1,248', + change: '-2.1%', + trend: 'down', + color: '#ef4444', + }, + { + title: 'Conversion', + value: '3.8%', + change: '0.0%', + trend: 'neutral', + color: '#6b7280', + }, + ]; + + const getTrendIcon = (trend: string) => { + switch (trend) { + case 'up': + return TrendingUp; + case 'down': + return TrendingDown; + default: + return Minus; + } + }; + + return ( + + {stats.map((stat, index) => { + const TrendIcon = getTrendIcon(stat.trend); + return ( + + + + {stat.title} + + + + {stat.value} + + + + + + {stat.change} + + + + + ); + })} + + ); +} + +``` + +##### carousel-manual + +Manually controlled carousel with external buttons + +```tsx +import { Button } from '@/components/ui/button'; +import { Carousel, CarouselItem, CarouselRef } from '@/components/ui/carousel'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { ChevronLeft, ChevronRight, RotateCcw } from 'lucide-react-native'; +import React, { useRef, useState } from 'react'; + +export function CarouselManual() { + const totalSlides = 4; + const carouselRef = useRef(null); + const [currentIndex, setCurrentIndex] = useState(0); + + const lessons = [ + { + title: 'Introduction to React Native', + progress: 0, + duration: '15 min', + bg: '#9c27b0', + color: '#f3e5f5', + }, + { + title: 'Component Architecture', + progress: 45, + duration: '20 min', + bg: '#ff5722', + color: '#fff3e0', + }, + { + title: 'State Management', + progress: 75, + duration: '25 min', + bg: '#00bcd4', + color: '#e0f2f1', + }, + { + title: 'Navigation Patterns', + progress: 100, + duration: '18 min', + bg: '#ff4081', + color: '#fce4ec', + }, + ]; + + const handleGoToSlide = (index: number) => { + if (carouselRef.current?.goToSlide) { + carouselRef.current.goToSlide(index); + } + }; + + const handleGoToPrevious = () => { + if (currentIndex > 0) { + if (carouselRef.current?.goToPrevious) { + carouselRef.current.goToPrevious(); + } + } + }; + + const handleGoToNext = () => { + if (currentIndex < totalSlides - 1) { + if (carouselRef.current?.goToNext) { + carouselRef.current.goToNext(); + } + } + }; + + const handleReset = () => { + if (carouselRef.current?.goToSlide) { + carouselRef.current.goToSlide(0); + } + }; + + // Handle index changes from carousel (swipe gestures AND button presses) + const handleIndexChange = (index: number) => { + setCurrentIndex(index); + }; + + return ( + + {/* External Controls */} + + + ))} + + + + + ); +} + +``` +--- + +### chart-container + +A container component for wrapping charts with title, description, and consistent styling. + +**Installation:** +```bash +npx bna-ui add chart-container +``` + +**External Dependencies:** expo-image + +**Registry Dependencies:** text, view, image + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![chart-container preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 15-47-37_1.MOV) + +#### Basic Usage + +```tsx +import { Chart-container } from '@/components/ui/chart-container'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### chart-container-demo + +A basic chart container with title and description + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { LineChart } from '@/components/charts/line-chart'; +import React from 'react'; + +const sampleData = [ + { x: 'Jan', y: 100, label: 'January' }, + { x: 'Feb', y: 120, label: 'February' }, + { x: 'Mar', y: 90, label: 'March' }, + { x: 'Apr', y: 140, label: 'April' }, + { x: 'May', y: 110, label: 'May' }, +]; + +export function ChartContainerDemo() { + return ( + + + + ); +} + +``` + +##### chart-container-styled + +Chart container with custom styling + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { LineChart } from '@/components/charts/line-chart'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const sampleData = [ + { x: 'Q1', y: 65, label: 'Quarter 1' }, + { x: 'Q2', y: 80, label: 'Quarter 2' }, + { x: 'Q3', y: 75, label: 'Quarter 3' }, + { x: 'Q4', y: 95, label: 'Quarter 4' }, +]; + +export function ChartContainerStyled() { + const backgroundColor = useThemeColor({}, 'indigo'); + + return ( + + + + ); +} + +``` +--- + +### checkbox + +A control that allows the user to toggle between checked and not checked states. + +**Installation:** +```bash +npx bna-ui add checkbox +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![checkbox preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-16-46_1.MP4) + +#### Basic Usage + +```tsx +import { Checkbox } from '@/components/ui/checkbox'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### checkbox-demo + +A basic checkbox with label + +```tsx +import { Checkbox } from '@/components/ui/checkbox'; +import React, { useState } from 'react'; + +export function CheckboxDemo() { + const [checked, setChecked] = useState(false); + + return ( + + ); +} + +``` + +##### checkbox-states + +Checkboxes in different states: unchecked, checked, and disabled + +```tsx +import { Checkbox } from '@/components/ui/checkbox'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function CheckboxStates() { + const [checked1, setChecked1] = useState(false); + const [checked2, setChecked2] = useState(true); + const [checked3, setChecked3] = useState(false); + + return ( + + + Different States + + + + + + + + + ); +} + +``` + +##### checkbox-without-label + +A checkbox without a label + +```tsx +import { Checkbox } from '@/components/ui/checkbox'; +import React, { useState } from 'react'; + +export function CheckboxWithoutLabel() { + const [checked, setChecked] = useState(false); + + return ; +} + +``` + +##### checkbox-with-error + +A checkbox with error styling and message + +```tsx +import { Checkbox } from '@/components/ui/checkbox'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function CheckboxWithError() { + const [checked, setChecked] = useState(false); + + return ( + + + {!checked && ( + + You must accept the terms to continue + + )} + + ); +} + +``` + +##### checkbox-custom-styling + +Checkboxes with custom label styling + +```tsx +import { Checkbox } from '@/components/ui/checkbox'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function CheckboxCustomStyling() { + const [checked1, setChecked1] = useState(false); + const [checked2, setChecked2] = useState(true); + + return ( + + + + + + ); +} + +``` + +##### checkbox-group + +Multiple checkboxes working together as a group + +```tsx +import { Checkbox } from '@/components/ui/checkbox'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function CheckboxGroup() { + const [selectedItems, setSelectedItems] = useState([]); + + const items = [ + { id: 'notifications', label: 'Email notifications' }, + { id: 'marketing', label: 'Marketing emails' }, + { id: 'updates', label: 'Product updates' }, + { id: 'newsletter', label: 'Weekly newsletter' }, + ]; + + const handleItemChange = (itemId: string, checked: boolean) => { + if (checked) { + setSelectedItems((prev) => [...prev, itemId]); + } else { + setSelectedItems((prev) => prev.filter((id) => id !== itemId)); + } + }; + + return ( + + Subscription Preferences + + + {items.map((item) => ( + handleItemChange(item.id, checked)} + label={item.label} + /> + ))} + + + + Selected: {selectedItems.length} item(s) + + + ); +} + +``` +--- + +### collapsible + +An interactive component which can be expanded/collapsed to show and hide content. + +**Installation:** +```bash +npx bna-ui add collapsible +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** icon, text, view + +**Preview:** + +![collapsible preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-19-30_1.MP4) + +#### Basic Usage + +```tsx +import { Collapsible } from '@/components/ui/collapsible'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### collapsible-demo + +A basic collapsible component with title and content + +```tsx +import { Collapsible } from '@/components/ui/collapsible'; +import { Text } from '@/components/ui/text'; +import React from 'react'; + +export function CollapsibleDemo() { + return ( + + + React Native is an open-source UI software framework created by Meta. It + is used to develop applications for Android, iOS, macOS, tvOS, Web, + Windows and UWP by enabling developers to use React's framework along + with native platform capabilities. + + + ); +} + +``` + +##### collapsible-multiple + +Multiple collapsible components working independently + +```tsx +import { Collapsible } from '@/components/ui/collapsible'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function CollapsibleMultiple() { + return ( + + + + To get started with React Native, you'll need to set up your + development environment. This includes installing Node.js, React + Native CLI, and either Android Studio or Xcode. + + + + + + React Native provides many built-in components like View, Text, + ScrollView, TextInput, and more. You can also create custom components + to build your app's interface. + + + + + + For navigation between screens, React Navigation is the most popular + solution. It provides stack, tab, and drawer navigation patterns that + work seamlessly with React Native. + + + + ); +} + +``` + +##### collapsible-nested + +Collapsible components nested within each other + +```tsx +import { Collapsible } from '@/components/ui/collapsible'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function CollapsibleNested() { + return ( + + + Mobile development encompasses various platforms and technologies: + + + + + + iOS development primarily uses Swift or Objective-C with Xcode as + the main development environment. Apps are distributed through the + Apple App Store. + + + + + + Android development can be done with Java, Kotlin, or cross-platform + frameworks. Android Studio is the official IDE, and apps are + distributed through Google Play Store. + + + + + + Cross-platform frameworks like React Native, Flutter, and Xamarin + allow you to write code once and deploy to multiple platforms. + + + + + ); +} + +``` + +##### collapsible-with-content + +Collapsible containing interactive elements like checkboxes + +```tsx +import { Checkbox } from '@/components/ui/checkbox'; +import { Collapsible } from '@/components/ui/collapsible'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function CollapsibleWithContent() { + const [preferences, setPreferences] = useState({ + darkMode: false, + notifications: true, + analytics: false, + }); + + return ( + + + + + setPreferences((prev) => ({ ...prev, darkMode: checked })) + } + label='Enable dark mode' + /> + + + setPreferences((prev) => ({ ...prev, notifications: checked })) + } + label='Push notifications' + /> + + + setPreferences((prev) => ({ ...prev, analytics: checked })) + } + label='Analytics tracking' + /> + + + + + + Email: user@example.com + Member since: January 2024 + Subscription: Premium + + + + ); +} + +``` + +##### collapsible-faq + +Collapsible components styled as frequently asked questions + +```tsx +import { Collapsible } from '@/components/ui/collapsible'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function CollapsibleFAQ() { + const faqs = [ + { + question: 'How do I reset my password?', + answer: + "You can reset your password by clicking on the 'Forgot Password' link on the login page. Enter your email address and we'll send you instructions to reset your password.", + }, + { + question: 'Can I cancel my subscription anytime?', + answer: + 'Yes, you can cancel your subscription at any time from your account settings. Your subscription will remain active until the end of your current billing period.', + }, + { + question: 'Is my data secure?', + answer: + 'We take data security seriously. All data is encrypted in transit and at rest. We follow industry best practices and comply with relevant data protection regulations.', + }, + { + question: 'How do I contact support?', + answer: + 'You can reach our support team through the in-app chat, email us at support@example.com, or call our toll-free number during business hours.', + }, + ]; + + return ( + + + Frequently Asked Questions + + + + {faqs.map((faq, index) => ( + + {faq.answer} + + ))} + + + ); +} + +``` +--- + +### color-picker + +A color picker component with HSV color space selection and swatch display. + +**Installation:** +```bash +npx bna-ui add color-picker +``` + +**External Dependencies:** expo-linear-gradient, react-native-gesture-handler, react-native-reanimated, react-native-svg + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![color-picker preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-21-55_1.MP4) + +#### Basic Usage + +```tsx +import { Color-picker } from '@/components/ui/color-picker'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### color-picker-demo + +A basic color picker with swatch and modal selection + +```tsx +import { ColorPicker } from '@/components/ui/color-picker'; +import React, { useState } from 'react'; + +export function ColorPickerDemo() { + const [color, setColor] = useState('#ff0000'); + + return ( + { + console.log('Color selected:', selectedColor); + setColor(selectedColor); + }} + /> + ); +} + +``` + +##### color-picker-sizes + +Color pickers with different swatch sizes + +```tsx +import { ColorPicker } from '@/components/ui/color-picker'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function ColorPickerSizes() { + const [smallColor, setSmallColor] = useState('#ff6b6b'); + const [mediumColor, setMediumColor] = useState('#4ecdc4'); + const [largeColor, setLargeColor] = useState('#45b7d1'); + const [xlColor, setXlColor] = useState('#f9ca24'); + + return ( + + + + + + + ); +} + +``` + +##### color-picker-colors + +Color pickers with different initial colors + +```tsx +import { ColorPicker } from '@/components/ui/color-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function ColorPickerColors() { + const [redColor, setRedColor] = useState('#e74c3c'); + const [greenColor, setGreenColor] = useState('#2ecc71'); + const [blueColor, setBlueColor] = useState('#3498db'); + const [purpleColor, setPurpleColor] = useState('#9b59b6'); + const [orangeColor, setOrangeColor] = useState('#f39c12'); + + const colorData = [ + { name: 'Red', color: redColor, setter: setRedColor }, + { name: 'Green', color: greenColor, setter: setGreenColor }, + { name: 'Blue', color: blueColor, setter: setBlueColor }, + { name: 'Purple', color: purpleColor, setter: setPurpleColor }, + { name: 'Orange', color: orangeColor, setter: setOrangeColor }, + ]; + + return ( + + {colorData.map(({ name, color, setter }) => ( + + + {name} + + ))} + + ); +} + +``` + +##### color-picker-disabled + +Disabled color picker that cannot be opened + +```tsx +import { ColorPicker } from '@/components/ui/color-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ColorPickerDisabled() { + return ( + + + + Disabled Color Picker + + + ); +} + +``` + +##### color-picker-styled + +Color pickers with custom styling and layouts + +```tsx +import { ColorPicker } from '@/components/ui/color-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function ColorPickerStyled() { + const [primaryColor, setPrimaryColor] = useState('#007AFF'); + const [accentColor, setAccentColor] = useState('#FF3B30'); + + return ( + + {/* Rounded Square Style */} + + + Primary Color + + + {/* With Custom Border */} + + + + + Accent Color + + + ); +} + +``` + +##### color-picker-palette + +Multiple color pickers arranged as a color palette + +```tsx +import { ColorPicker } from '@/components/ui/color-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; + +export function ColorPickerPalette() { + const cardColor = useThemeColor({}, 'card'); + + const [colors, setColors] = useState([ + '#FF6B6B', + '#4ECDC4', + '#45B7D1', + '#F9CA24', + '#6C5CE7', + '#A29BFE', + '#FD79A8', + '#00B894', + ]); + + const updateColor = (index: number, newColor: string) => { + const newColors = [...colors]; + newColors[index] = newColor; + setColors(newColors); + }; + + return ( + + {colors.map((color, index) => ( + + updateColor(index, newColor)} + onColorSelect={(newColor) => updateColor(index, newColor)} + swatchSize={36} + /> + + {color.toUpperCase()} + + + ))} + + ); +} + +``` + +##### color-picker-labeled + +color-picker-labeled + +```tsx +import { ColorPicker } from '@/components/ui/color-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function ColorPickerLabeled() { + const [backgroundColor, setBackgroundColor] = useState('#ffffff'); + const [textColor, setTextColor] = useState('#333333'); + const [borderColor, setBorderColor] = useState('#e1e5e9'); + + return ( + + {/* Background Color */} + + + + + Background Color + + + {backgroundColor.toUpperCase()} + + + + + {/* Text Color */} + + + + + Text Color + + + {textColor.toUpperCase()} + + + + + {/* Border Color */} + + + + + Border Color + + + {borderColor.toUpperCase()} + + + + + {/* Preview */} + + + Preview with selected colors + + + + ); +} + +``` +--- + +### column-chart + +A customizable horizontal bar chart component with smooth animations and flexible styling. + +**Installation:** +```bash +npx bna-ui add column-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![column-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 19 5.46.51 PM.MOV) + +#### Basic Usage + +```tsx +import { Column-chart } from '@/components/ui/column-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### column-chart-demo + +A horizontal bar chart with smooth animations + +```tsx +import { ColumnChart } from '@/components/charts/column-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Sales', value: 120 }, + { label: 'Marketing', value: 98 }, + { label: 'Support', value: 86 }, + { label: 'Development', value: 140 }, + { label: 'Design', value: 75 }, + { label: 'HR', value: 65 }, +]; + +export function ColumnChartDemo() { + return ( + + + + ); +} + +``` + +##### column-chart-sample + +An sample column chart + +```tsx +import { ColumnChart } from '@/components/charts/column-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; +import { Pressable, Text, View } from 'react-native'; + +const sampleData = [ + { label: 'Q1 2024', value: 850 }, + { label: 'Q2 2024', value: 920 }, + { label: 'Q3 2024', value: 1100 }, + { label: 'Q4 2024', value: 1250 }, +]; + +export function ColumnChartSample() { + const [selectedIndex, setSelectedIndex] = useState(null); + const primaryColor = useThemeColor({}, 'primary'); + const mutedColor = useThemeColor({}, 'muted'); + + const enhancedData = sampleData.map((item, index) => ({ + ...item, + color: selectedIndex === index ? primaryColor : mutedColor, + })); + + return ( + + + + {sampleData.map((item, index) => ( + + setSelectedIndex(selectedIndex === index ? null : index) + } + style={{ + padding: 8, + backgroundColor: + selectedIndex === index ? primaryColor : mutedColor, + borderRadius: 6, + minWidth: 60, + alignItems: 'center', + }} + > + + {item.label} + + + ))} + + + ); +} + +``` + +##### column-chart-styled + +A customized column chart with custom colors and styling + +```tsx +import { ColumnChart } from '@/components/charts/column-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const sampleData = [ + { label: 'Mobile', value: 45, color: '#3b82f6' }, + { label: 'Desktop', value: 35, color: '#10b981' }, + { label: 'Tablet', value: 15, color: '#f59e0b' }, + { label: 'Smart TV', value: 8, color: '#ef4444' }, + { label: 'Wearable', value: 3, color: '#8b5cf6' }, +]; + +export function ColumnChartStyled() { + return ( + + + + ); +} + +``` + +##### column-chart-large + +A column chart with large dataset + +```tsx +import { ColumnChart } from '@/components/charts/column-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const largeSampleData = [ + { label: 'E-commerce', value: 2840, color: '#ff0066' }, + { label: 'AI', value: 2440, color: '#ff9900' }, + { label: 'Healthcare', value: 2150, color: '#00e6cc' }, + { label: 'Education', value: 1920, color: '#0099ff' }, + { label: 'Finance', value: 1780, color: '#ffcc00' }, + { label: 'Real Estate', value: 1650, color: '#9933ff' }, + { label: 'Travel', value: 1420, color: '#ff0080' }, + { label: 'Food & Dining', value: 1380, color: '#00cc66' }, + { label: 'Entertainment', value: 1250, color: '#ff6600' }, + { label: 'Sports', value: 1180, color: '#3399ff' }, + { label: 'Technology', value: 1050, color: '#cc66ff' }, + { label: 'Fashion', value: 980, color: '#ff3030' }, + { label: 'Automotive', value: 875, color: '#ff9900' }, + { label: 'Home & Garden', value: 720, color: '#0066ff' }, + { label: 'Beauty', value: 650, color: '#ff3366' }, + { label: 'Pets', value: 580, color: '#00ffcc' }, +]; + +export function ColumnChartLarge() { + return ( + + + + ); +} + +``` +--- + +### combobox + +A searchable dropdown component that combines an input with a list of options. + +**Installation:** +```bash +npx bna-ui add combobox +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![combobox preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-30-31_1.MP4) + +#### Basic Usage + +```tsx +import { Combobox } from '@/components/ui/combobox'; + +export default function Example() { + return ( + + ); +}``` + +--- + +### combobox-demo + +A basic combobox with search functionality + +**Installation:** +```bash +npx bna-ui add combobox-demo +``` + +**Registry Dependencies:** combobox + +**Preview:** + +![combobox-demo preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-30-31_1.MP4) + +#### Basic Usage + +```tsx +import { Combobox-demo } from '@/components/ui/combobox-demo'; + +export default function Example() { + return ( + + ); +}``` + +--- + +### combobox-disabled + +Disabled combobox component + +**Installation:** +```bash +npx bna-ui add combobox-disabled +``` + +**Registry Dependencies:** combobox + +**Preview:** + +![combobox-disabled preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-31-48_1.MP4) + +#### Basic Usage + +```tsx +import { Combobox-disabled } from '@/components/ui/combobox-disabled'; + +export default function Example() { + return ( + + ); +}``` + +--- + +### combobox-form + +Combobox integrated with form validation + +**Installation:** +```bash +npx bna-ui add combobox-form +``` + +**Registry Dependencies:** combobox, text, view + +**Preview:** + +![combobox-form preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-32-52_1.MP4) + +#### Basic Usage + +```tsx +import { Combobox-form } from '@/components/ui/combobox-form'; + +export default function Example() { + return ( + + ); +}``` + +--- + +### combobox-groups + +Combobox with grouped options + +**Installation:** +```bash +npx bna-ui add combobox-groups +``` + +**Registry Dependencies:** combobox + +**Preview:** + +![combobox-groups preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-31-00_1.MP4) + +#### Basic Usage + +```tsx +import { Combobox-groups } from '@/components/ui/combobox-groups'; + +export default function Example() { + return ( + + ); +}``` + +--- + +### combobox-large + +Combobox handling large datasets efficiently + +**Installation:** +```bash +npx bna-ui add combobox-large +``` + +**Registry Dependencies:** combobox + +**Preview:** + +![combobox-large preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-33-14_1.MP4) + +#### Basic Usage + +```tsx +import { Combobox-large } from '@/components/ui/combobox-large'; + +export default function Example() { + return ( + + ); +}``` + +--- + +### combobox-multiple + +Combobox that allows selecting multiple values + +**Installation:** +```bash +npx bna-ui add combobox-multiple +``` + +**Registry Dependencies:** combobox + +**Preview:** + +![combobox-multiple preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-31-21_1.MP4) + +#### Basic Usage + +```tsx +import { Combobox-multiple } from '@/components/ui/combobox-multiple'; + +export default function Example() { + return ( + + ); +}``` + +--- + +### combobox-search + +Combobox with custom search behavior + +**Installation:** +```bash +npx bna-ui add combobox-search +``` + +**Registry Dependencies:** combobox + +**Preview:** + +![combobox-search preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_06-30-2025 23-32-02_1.MP4) + +#### Basic Usage + +```tsx +import { Combobox-search } from '@/components/ui/combobox-search'; + +export default function Example() { + return ( + + ); +}``` + +--- + +### date-picker + +A customizable date and time picker component with bottom sheet UI. + +**Installation:** +```bash +npx bna-ui add date-picker +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** bottom-sheet, button, icon, scroll-view, text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![date-picker preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 01-08-59_1.MP4) + +#### Basic Usage + +```tsx +import { Date-picker } from '@/components/ui/date-picker'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### date-picker-demo + +A basic date picker with calendar view + +```tsx +import { DatePicker } from '@/components/ui/date-picker'; +import React, { useState } from 'react'; + +export function DatePickerDemo() { + const [selectedDate, setSelectedDate] = useState(); + + return ( + + ); +} + +``` + +##### date-picker-time + +A time picker with hour and minute selection + +```tsx +import { DatePicker } from '@/components/ui/date-picker'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function DatePickerTime() { + const [time24, setTime24] = useState(); + const [time12, setTime12] = useState(); + + return ( + + + + + + ); +} + +``` + +##### date-picker-datetime + +A combined date and time picker + +```tsx +import { DatePicker } from '@/components/ui/date-picker'; +import React, { useState } from 'react'; + +export function DatePickerDateTime() { + const [dateTime, setDateTime] = useState(); + + return ( + + ); +} + +``` + +##### date-picker-range + +A date range picker + +```tsx +import { DatePicker, DateRange } from '@/components/ui/date-picker'; +import React, { useState } from 'react'; + +export function DatePickerRange() { + const [selectedRange, setSelectedRange] = useState(); + + return ( + + ); +} + +``` + +##### date-picker-constraints + +Date picker with minimum and maximum date limits + +```tsx +import { DatePicker } from '@/components/ui/date-picker'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function DatePickerConstraints() { + const [pastDate, setPastDate] = useState(); + const [futureDate, setFutureDate] = useState(); + const [rangeDate, setRangeDate] = useState(); + + const today = new Date(); + const maxPastDate = new Date(); + maxPastDate.setDate(today.getDate() - 1); // Yesterday + + const minFutureDate = new Date(); + minFutureDate.setDate(today.getDate() + 1); // Tomorrow + + const minRangeDate = new Date(); + minRangeDate.setMonth(today.getMonth() - 1); // Last month + + const maxRangeDate = new Date(); + maxRangeDate.setMonth(today.getMonth() + 1); // Next month + + return ( + + + + + + + + ); +} + +``` + +##### date-picker-variants + +Date pickers with different styling variants + +```tsx +import { DatePicker } from '@/components/ui/date-picker'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function DatePickerVariants() { + const [filledDate, setFilledDate] = useState(); + const [outlineDate, setOutlineDate] = useState(); + const [groupDate, setGroupDate] = useState(); + + return ( + + + + + + + + ); +} + +``` + +##### date-picker-formats + +Time picker with 12-hour and 24-hour formats + +```tsx +import { DatePicker } from '@/components/ui/date-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function DatePickerFormats() { + const [datetime24, setDateTime24] = useState(); + const [datetime12, setDateTime12] = useState(); + + return ( + + + + 24-Hour Format + + + + + + + 12-Hour Format with AM/PM + + + + + ); +} + +``` + +##### date-picker-form + +Date picker integrated within a form with validation + +```tsx +import { Button } from '@/components/ui/button'; +import { DatePicker } from '@/components/ui/date-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; +import { Alert } from 'react-native'; + +export function DatePickerForm() { + const [birthDate, setBirthDate] = useState(); + const [appointmentDate, setAppointmentDate] = useState(); + const [errors, setErrors] = useState<{ + birthDate?: string; + appointmentDate?: string; + }>({}); + + const validateForm = () => { + const newErrors: typeof errors = {}; + + if (!birthDate) { + newErrors.birthDate = 'Birth date is required'; + } else { + const today = new Date(); + const age = today.getFullYear() - birthDate.getFullYear(); + if (age < 18) { + newErrors.birthDate = 'Must be 18 years or older'; + } + } + + if (!appointmentDate) { + newErrors.appointmentDate = 'Appointment date is required'; + } else { + const today = new Date(); + today.setHours(0, 0, 0, 0); + if (appointmentDate < today) { + newErrors.appointmentDate = 'Appointment must be in the future'; + } + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = () => { + if (validateForm()) { + Alert.alert('Success', 'Form submitted successfully!'); + } + }; + + const maxBirthDate = new Date(); + maxBirthDate.setFullYear(maxBirthDate.getFullYear() - 18); + + const minAppointmentDate = new Date(); + minAppointmentDate.setDate(minAppointmentDate.getDate() + 1); + + return ( + + + Registration Form + + + { + setBirthDate(date); + if (errors.birthDate) { + setErrors((prev) => ({ ...prev, birthDate: undefined })); + } + }} + placeholder='Select your birth date' + maximumDate={maxBirthDate} + error={errors.birthDate} + /> + + { + setAppointmentDate(date); + if (errors.appointmentDate) { + setErrors((prev) => ({ ...prev, appointmentDate: undefined })); + } + }} + placeholder='Select appointment date and time' + minimumDate={minAppointmentDate} + timeFormat='12' + error={errors.appointmentDate} + /> + + + + ); +} + +``` +--- + +### doughnut-chart + +A customizable doughnut chart component with smooth animations, interactive legends, and flexible styling. + +**Installation:** +```bash +npx bna-ui add doughnut-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Registry Dependencies:** text + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![doughnut-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 56.MOV) + +#### Basic Usage + +```tsx +import { Doughnut-chart } from '@/components/ui/doughnut-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### doughnut-chart-demo + +A doughnut chart with smooth animations and percentage labels + +```tsx +import { DoughnutChart } from '@/components/charts/doughnut-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Sales', value: 120 }, + { label: 'Marketing', value: 98 }, + { label: 'Support', value: 86 }, + { label: 'Development', value: 140 }, + { label: 'Design', value: 75 }, + { label: 'HR', value: 65 }, +]; + +export function DoughnutChartDemo() { + return ( + + + + ); +} + +``` + +##### doughnut-chart-sample + +A sample doughnut chart with custom theme colors + +```tsx +import { DoughnutChart } from '@/components/charts/doughnut-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const sampleData = [ + { label: 'Revenue', value: 45000 }, + { label: 'Expenses', value: 32000 }, + { label: 'Profit', value: 13000 }, +]; + +export function DoughnutChartSample() { + const primaryColor = useThemeColor({}, 'primary'); + const greenColor = useThemeColor({}, 'green'); + const orangeColor = useThemeColor({}, 'orange'); + + const dataWithColors = [ + { ...sampleData[0], color: primaryColor }, + { ...sampleData[1], color: orangeColor }, + { ...sampleData[2], color: greenColor }, + ]; + + return ( + + + + ); +} + +``` + +##### doughnut-chart-styled + +A customized doughnut chart with custom colors and styling + +```tsx +import { DoughnutChart } from '@/components/charts/doughnut-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const customData = [ + { label: 'Mobile', value: 65, color: '#FF6B6B' }, + { label: 'Desktop', value: 25, color: '#4ECDC4' }, + { label: 'Tablet', value: 8, color: '#45B7D1' }, + { label: 'Other', value: 2, color: '#96CEB4' }, +]; + +export function DoughnutChartStyled() { + return ( + + + + ); +} + +``` + +##### doughnut-chart-large + +A doughnut chart with large dataset and legend-only labels + +```tsx +import { DoughnutChart } from '@/components/charts/doughnut-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const largeDataset = [ + { label: 'E-commerce', value: 285, color: '#FF6B6B' }, + { label: 'Social Media', value: 245, color: '#4ECDC4' }, + { label: 'Search Engine', value: 198, color: '#45B7D1' }, + { label: 'Email Marketing', value: 156, color: '#96CEB4' }, + { label: 'Direct Traffic', value: 134, color: '#FFEAA7' }, + { label: 'Referral', value: 89, color: '#DDA0DD' }, + { label: 'Display Ads', value: 67, color: '#98D8C8' }, + { label: 'Video Ads', value: 45, color: '#F7DC6F' }, + { label: 'Affiliate', value: 23, color: '#BB8FCE' }, + { label: 'Other', value: 18, color: '#AED6F1' }, +]; + +export function DoughnutChartLarge() { + return ( + + + + ); +} + +``` +--- + +### file-picker + +A customizable file picker component with validation, preview, and multiple file support. + +**Installation:** +```bash +npx bna-ui add file-picker +``` + +**External Dependencies:** lucide-react-native, expo-document-picker + +**Registry Dependencies:** button, text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Props Interface + +```typescript +export interface FilePickerProps { + // Core functionality + onFilesSelected: (files: SelectedFile[]) => void; + onError?: (error: string) => void; + + // Configuration + fileType?: FileType; + multiple?: boolean; + maxFiles?: number; + maxSizeBytes?: number; + allowedExtensions?: string[]; + + // UI customization + placeholder?: string; + disabled?: boolean; + style?: ViewStyle; + showPreview?: boolean; + showFileInfo?: boolean; + + // Accessibility + accessibilityLabel?: string; + accessibilityHint?: string; + + variant?: ButtonVariant; +} +``` + +**Preview:** + +![file-picker preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 01-15-34_1.MP4) + +#### Basic Usage + +```tsx +import { File-picker } from '@/components/ui/file-picker'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### file-picker-demo + +A basic file picker with validation and preview + +```tsx +import { FilePicker } from '@/components/ui/file-picker'; +import React from 'react'; + +export function FilePickerDemo() { + return ( + console.log('Selected files:', files)} + onError={(error) => console.error('Error:', error)} + fileType='all' + multiple={true} + maxFiles={5} + placeholder='Select your files' + showFileInfo={true} + /> + ); +} + +``` + +##### file-picker-images + +File picker configured for images only + +```tsx +import { FilePicker, SelectedFile } from '@/components/ui/file-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function FilePickerImages() { + const [selectedFiles, setSelectedFiles] = useState([]); + + return ( + + console.error('Error:', error)} + fileType='image' + multiple={true} + maxFiles={3} + maxSizeBytes={5 * 1024 * 1024} // 5MB + allowedExtensions={['jpg', 'jpeg', 'png', 'gif', 'webp']} + placeholder='Select images (max 3)' + showFileInfo={true} + /> + {selectedFiles.length > 0 && ( + + {selectedFiles.length} image{selectedFiles.length > 1 ? 's' : ''}{' '} + selected + + )} + + ); +} + +``` + +##### file-picker-single + +File picker for selecting a single file + +```tsx +import { FilePicker, SelectedFile } from '@/components/ui/file-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function FilePickerSingle() { + const [selectedFile, setSelectedFile] = useState([]); + + return ( + + console.error('Error:', error)} + fileType='document' + multiple={false} + maxFiles={1} + maxSizeBytes={2 * 1024 * 1024} // 2MB + placeholder='Select a document' + showFileInfo={true} + /> + + {selectedFile.length > 0 && ( + + Selected File: + {selectedFile[0].name} + {selectedFile[0].size && ( + + {(selectedFile[0].size / 1024).toFixed(1)} KB + + )} + + )} + + ); +} + +``` + +##### file-picker-validation + +File picker with size limits and extension validation + +```tsx +import { FilePicker, SelectedFile } from '@/components/ui/file-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function FilePickerValidation() { + const [error, setError] = useState(''); + const [files, setFiles] = useState([]); + + return ( + + { + setFiles(files); + setError(''); + }} + onError={setError} + fileType='all' + multiple={true} + maxFiles={2} + maxSizeBytes={1 * 1024 * 1024} // 1MB limit + allowedExtensions={['pdf', 'doc', 'docx', 'txt']} + placeholder='Select (PDF, DOC, DOCX, TXT only)' + showFileInfo={true} + /> + + {error && ( + + {error} + + )} + + {files.length > 0 && !error && ( + + + ✓ Files validated successfully + + + {files.length} file{files.length > 1 ? 's' : ''} ready for upload + + + )} + + ); +} + +``` + +##### file-picker-styled + +File picker with custom styling and colors + +```tsx +import { FilePicker } from '@/components/ui/file-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function FilePickerStyled() { + return ( + + {/* Primary Style */} + + + Primary Style + + + console.log('Primary files:', files)} + onError={(error) => console.error('Error:', error)} + fileType='all' + multiple={true} + maxFiles={3} + placeholder='Upload files' + style={{ + borderWidth: 2, + borderColor: '#007AFF', + borderRadius: 12, + // backgroundColor: '#f0f8ff', + }} + /> + + + {/* Minimal Style */} + + + Minimal Style + + console.log('Minimal files:', files)} + onError={(error) => console.error('Error:', error)} + fileType='image' + multiple={false} + maxFiles={1} + placeholder='Choose image' + style={{ + borderWidth: 1, + borderStyle: 'dashed', + borderColor: '#ccc', + borderRadius: 8, + backgroundColor: 'transparent', + }} + /> + + + {/* Success Style */} + + + Success Style + + console.log('Success files:', files)} + onError={(error) => console.error('Error:', error)} + fileType='document' + multiple={true} + maxFiles={5} + placeholder='Select documents' + style={{ + borderWidth: 2, + borderColor: '#34C759', + borderRadius: 16, + // backgroundColor: '#f0fff4', + }} + /> + + + ); +} + +``` + +##### file-picker-controlled + +Controlled file picker using the useFilePicker hook + +```tsx +import { Button } from '@/components/ui/button'; +import { useFilePicker } from '@/components/ui/file-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function FilePickerControlled() { + const { + files, + addFiles, + removeFile, + clearFiles, + totalSize, + isValid, + errors, + } = useFilePicker({ + maxFiles: 3, + maxSizeBytes: 2 * 1024 * 1024, // 2MB + allowedExtensions: ['pdf', 'jpg', 'png', 'doc'], + onError: (error) => console.error('Validation error:', error), + }); + + const formatSize = (bytes: number) => { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + }; + + // Simulate adding files (in real app, this would come from file picker) + const simulateAddFiles = () => { + const mockFiles = [ + { uri: 'file://test1.pdf', name: 'test1.pdf', size: 150000 }, + { uri: 'file://test2.jpg', name: 'test2.jpg', size: 250000 }, + ]; + addFiles(mockFiles); + }; + + return ( + + + + + + + {/* Status Info */} + + Status + Files: {files.length}/3 + + Total Size: {formatSize(totalSize)} + + Valid: {isValid ? '✓' : '✗'} + + + {/* Errors */} + {errors.length > 0 && ( + + Errors: + {errors.map((error, index) => ( + + • {error} + + ))} + + )} + + {/* Files List */} + {files.length > 0 && ( + + + Selected Files: + + {files.map((file, index) => ( + + + + {file.name} + + {file.size && ( + + {formatSize(file.size)} + + )} + + + + ))} + + )} + + ); +} + +``` + +##### file-picker-info + +File picker displaying detailed file information + +```tsx +import { FilePicker, SelectedFile } from '@/components/ui/file-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; + +export function FilePickerInfo() { + const card = useThemeColor({}, 'card'); + const [files, setFiles] = useState([]); + const [uploadProgress, setUploadProgress] = useState({}); + + const handleFilesSelected = (selectedFiles: SelectedFile[]) => { + setFiles(selectedFiles); + // Simulate upload progress + selectedFiles.forEach((file, index) => { + let progress = 0; + const interval = setInterval(() => { + progress += Math.random() * 20; + if (progress >= 100) { + progress = 100; + clearInterval(interval); + } + setUploadProgress((prev: any) => ({ + ...prev, + [index]: Math.min(progress, 100), + })); + }, 200); + }); + }; + + const formatFileSize = (bytes: number) => { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; + }; + + const getFileTypeIcon = (fileName: string) => { + const ext = fileName.split('.').pop()?.toLowerCase() || ''; + + const typeMap: Record = { + pdf: '📄', + doc: '📝', + docx: '📝', + txt: '📄', + jpg: '🖼️', + jpeg: '🖼️', + png: '🖼️', + gif: '🖼️', + zip: '📦', + rar: '📦', + mp4: '🎥', + mp3: '🎵', + }; + + return typeMap[ext] || '📎'; + }; + + return ( + + console.error('Error:', error)} + fileType='all' + multiple={true} + maxFiles={4} + maxSizeBytes={5 * 1024 * 1024} // 5MB + placeholder='Select files for detailed preview' + showFileInfo={true} + /> + + {files.length > 0 && ( + + + File Details + + + {files.map((file, index) => ( + + + + {getFileTypeIcon(file.name)} + + + + + {file.name} + + + + {file.size && ( + + Size: {formatFileSize(file.size)} + + )} + + {file.mimeType && ( + + Type: {file.mimeType} + + )} + + + Status:{' '} + {uploadProgress[index] >= 100 + ? 'Uploaded' + : 'Uploading...'} + + + + {/* Progress Bar */} + {uploadProgress[index] !== undefined && ( + + + = 100 + ? '#4CAF50' + : '#2196F3', + borderRadius: 2, + }} + /> + + + {Math.round(uploadProgress[index] || 0)}% + + + )} + + + + ))} + + {/* Summary */} + + + Summary + + + {files.length} file{files.length > 1 ? 's' : ''} • Total size:{' '} + {formatFileSize( + files.reduce((sum, file) => sum + (file.size || 0), 0) + )} + + + + )} + + ); +} + +``` + +#### Props Examples + +```tsx +// Different variants +Default +Outline +```--- + +### gallery + +A responsive image gallery component with fullscreen viewing, zoom, and gesture support. + +**Installation:** +```bash +npx bna-ui add gallery +``` + +**External Dependencies:** expo-image, lucide-react-native, react-native-gesture-handler, react-native-reanimated + +**Registry Dependencies:** button, text + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![gallery preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 02-26-55_1.MP4) + +#### Basic Usage + +```tsx +import { Gallery } from '@/components/ui/gallery'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### gallery-demo + +A basic image gallery with grid layout and fullscreen viewing + +```tsx +import { Gallery, GalleryItem } from '@/components/ui/gallery'; +import React from 'react'; + +const sampleImages: GalleryItem[] = [ + { + id: '1', + uri: 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'City Skyline', + description: 'Modern architecture at sunset', + thumbnail: + 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '2', + uri: 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Winter Wonderland', + description: 'Snow-covered peaks and pristine wilderness', + thumbnail: + 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '3', + uri: 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Ocean Waves', + description: 'Peaceful ocean scene with rolling waves', + thumbnail: + 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '4', + uri: 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Forest Path', + description: 'A winding path through ancient trees', + thumbnail: + 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '5', + uri: 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Desert Dunes', + description: 'Golden sand dunes stretching to the horizon', + thumbnail: + 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '6', + uri: 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Beautiful Landscape', + description: 'A stunning view of mountains and valleys', + thumbnail: + 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, +]; + +export function GalleryDemo() { + return ( + + ); +} + +``` + +##### gallery-grid + +Gallery with custom columns, spacing, and aspect ratio + +```tsx +import { Gallery, GalleryItem } from '@/components/ui/gallery'; +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +const gridImages: GalleryItem[] = [ + { + id: '1', + uri: 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'City Skyline', + description: 'Modern architecture at sunset', + thumbnail: + 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '2', + uri: 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Winter Wonderland', + description: 'Snow-covered peaks and pristine wilderness', + thumbnail: + 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '3', + uri: 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Ocean Waves', + description: 'Peaceful ocean scene with rolling waves', + thumbnail: + 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '4', + uri: 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Forest Path', + description: 'A winding path through ancient trees', + thumbnail: + 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '5', + uri: 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Desert Dunes', + description: 'Golden sand dunes stretching to the horizon', + thumbnail: + 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '6', + uri: 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Beautiful Landscape', + description: 'A stunning view of mountains and valleys', + thumbnail: + 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, +]; + +export function GalleryGrid() { + return ( + + + + 4 Columns, No Spacing + + + + + + + 3 Columns with Spacing + + + + + + + + 2 Columns, Large Spacing + + + + + ); +} + +``` + +##### gallery-info + +Gallery displaying image titles and descriptions + +```tsx +import { Gallery, GalleryItem } from '@/components/ui/gallery'; +import React from 'react'; + +const infoImages: GalleryItem[] = [ + { + id: '1', + uri: 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'City Skyline', + description: 'Modern architecture at sunset', + thumbnail: + 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '2', + uri: 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Winter Wonderland', + description: 'Snow-covered peaks and pristine wilderness', + thumbnail: + 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '3', + uri: 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Ocean Waves', + description: 'Peaceful ocean scene with rolling waves', + thumbnail: + 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '4', + uri: 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Forest Path', + description: 'A winding path through ancient trees', + thumbnail: + 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '5', + uri: 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Desert Dunes', + description: 'Golden sand dunes stretching to the horizon', + thumbnail: + 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '6', + uri: 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Beautiful Landscape', + description: 'A stunning view of mountains and valleys', + thumbnail: + 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, +]; + +export function GalleryInfo() { + return ( + + ); +} + +``` + +##### gallery-layouts + +Various gallery layouts and configurations + +```tsx +import { Gallery, GalleryItem } from '@/components/ui/gallery'; +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +const layoutImages: GalleryItem[] = [ + { + id: '1', + uri: 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'City Skyline', + description: 'Modern architecture at sunset', + thumbnail: + 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '2', + uri: 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Winter Wonderland', + description: 'Snow-covered peaks and pristine wilderness', + thumbnail: + 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '3', + uri: 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Ocean Waves', + description: 'Peaceful ocean scene with rolling waves', + thumbnail: + 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '4', + uri: 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Forest Path', + description: 'A winding path through ancient trees', + thumbnail: + 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '5', + uri: 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Desert Dunes', + description: 'Golden sand dunes stretching to the horizon', + thumbnail: + 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '6', + uri: 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Beautiful Landscape', + description: 'A stunning view of mountains and valleys', + thumbnail: + 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, +]; + +export function GalleryLayouts() { + return ( + + + + Single Column (Feed Style) + + + + + + + Square Grid + + + + + + + Wide Thumbnails + + + + + ); +} + +``` + +##### gallery-controls + +Images with controls + +```tsx +import { Gallery, GalleryItem } from '@/components/ui/gallery'; +import React from 'react'; +import { Alert } from 'react-native'; + +const controlImages: GalleryItem[] = [ + { + id: '1', + uri: 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'City Skyline', + description: 'Modern architecture at sunset', + thumbnail: + 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '2', + uri: 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Winter Wonderland', + description: 'Snow-covered peaks and pristine wilderness', + thumbnail: + 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '3', + uri: 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Ocean Waves', + description: 'Peaceful ocean scene with rolling waves', + thumbnail: + 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '4', + uri: 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Forest Path', + description: 'A winding path through ancient trees', + thumbnail: + 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '5', + uri: 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Desert Dunes', + description: 'Golden sand dunes stretching to the horizon', + thumbnail: + 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '6', + uri: 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Beautiful Landscape', + description: 'A stunning view of mountains and valleys', + thumbnail: + 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, +]; + +export function GalleryControls() { + const handleDownload = (item: GalleryItem) => { + Alert.alert('Download', `Downloading: ${item.title || 'Image'}`, [ + { text: 'OK' }, + ]); + }; + + const handleShare = (item: GalleryItem) => { + Alert.alert('Share', `Sharing: ${item.title || 'Image'}`, [{ text: 'OK' }]); + }; + + return ( + + ); +} + +``` + +##### gallery-overlay + +Images with overlay + +```tsx +import { Gallery, GalleryItem } from '@/components/ui/gallery'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +const sampleImages: GalleryItem[] = [ + { + id: '1', + uri: 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'City Skyline', + description: 'Modern architecture at sunset', + thumbnail: + 'https://images.unsplash.com/photo-1637858868799-7f26a0640eb6?q=80&w=2960&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '2', + uri: 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Winter Wonderland', + description: 'Snow-covered peaks and pristine wilderness', + thumbnail: + 'https://images.unsplash.com/photo-1644190022446-04b99df7259a?q=80&w=2012&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '3', + uri: 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Ocean Waves', + description: 'Peaceful ocean scene with rolling waves', + thumbnail: + 'https://images.unsplash.com/photo-1717732596477-04f8c5d53387?q=80&w=987&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '4', + uri: 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Forest Path', + description: 'A winding path through ancient trees', + thumbnail: + 'https://images.unsplash.com/photo-1575737698350-52e966f924d4?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '5', + uri: 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Desert Dunes', + description: 'Golden sand dunes stretching to the horizon', + thumbnail: + 'https://images.unsplash.com/photo-1667830867718-da7f5a45d20d?q=80&w=1064&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, + { + id: '6', + uri: 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + title: 'Beautiful Landscape', + description: 'A stunning view of mountains and valleys', + thumbnail: + 'https://images.unsplash.com/photo-1593085512500-5d55148d6f0d?q=80&w=2334&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D', + }, +]; + +export function GalleryOverlay() { + return ( + ( + + + {item.title} + + + )} + /> + ); +} + +``` +--- + +### heatmap-chart + +A customizable heatmap chart component with smooth animations and flexible color scaling for visualizing matrix data. + +**Installation:** +```bash +npx bna-ui add heatmap-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![heatmap-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 52.MOV) + +#### Basic Usage + +```tsx +import { Heatmap-chart } from '@/components/ui/heatmap-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### heatmap-chart-demo + +A heatmap chart with smooth animations and color scaling + +```tsx +import { HeatmapChart } from '@/components/charts/heatmap-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { row: 'Mon', col: 'Morning', value: 45 }, + { row: 'Mon', col: 'Afternoon', value: 62 }, + { row: 'Mon', col: 'Evening', value: 38 }, + { row: 'Tue', col: 'Morning', value: 52 }, + { row: 'Tue', col: 'Afternoon', value: 71 }, + { row: 'Tue', col: 'Evening', value: 43 }, + { row: 'Wed', col: 'Morning', value: 39 }, + { row: 'Wed', col: 'Afternoon', value: 85 }, + { row: 'Wed', col: 'Evening', value: 57 }, + { row: 'Thu', col: 'Morning', value: 68 }, + { row: 'Thu', col: 'Afternoon', value: 92 }, + { row: 'Thu', col: 'Evening', value: 61 }, + { row: 'Fri', col: 'Morning', value: 73 }, + { row: 'Fri', col: 'Afternoon', value: 88 }, + { row: 'Fri', col: 'Evening', value: 79 }, +]; + +export function HeatmapChartDemo() { + return ( + + + + ); +} + +``` + +##### heatmap-chart-sample + +A sample heatmap chart with different data + +```tsx +import { HeatmapChart } from '@/components/charts/heatmap-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { row: 'Q1', col: 'Sales', value: 85 }, + { row: 'Q1', col: 'Marketing', value: 72 }, + { row: 'Q1', col: 'Support', value: 90 }, + { row: 'Q1', col: 'Development', value: 78 }, + { row: 'Q2', col: 'Sales', value: 92 }, + { row: 'Q2', col: 'Marketing', value: 65 }, + { row: 'Q2', col: 'Support', value: 88 }, + { row: 'Q2', col: 'Development', value: 95 }, + { row: 'Q3', col: 'Sales', value: 78 }, + { row: 'Q3', col: 'Marketing', value: 83 }, + { row: 'Q3', col: 'Support', value: 91 }, + { row: 'Q3', col: 'Development', value: 87 }, + { row: 'Q4', col: 'Sales', value: 96 }, + { row: 'Q4', col: 'Marketing', value: 89 }, + { row: 'Q4', col: 'Support', value: 94 }, + { row: 'Q4', col: 'Development', value: 92 }, +]; + +export function HeatmapChartSample() { + return ( + + + + ); +} + +``` + +##### heatmap-chart-styled + +A customized heatmap chart with custom colors and styling + +```tsx +import { HeatmapChart } from '@/components/charts/heatmap-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const sampleData = [ + { row: 'Low', col: 'Low', value: 10 }, + { row: 'Low', col: 'Medium', value: 25 }, + { row: 'Low', col: 'High', value: 40 }, + { row: 'Medium', col: 'Low', value: 30 }, + { row: 'Medium', col: 'Medium', value: 55 }, + { row: 'Medium', col: 'High', value: 70 }, + { row: 'High', col: 'Low', value: 50 }, + { row: 'High', col: 'Medium', value: 75 }, + { row: 'High', col: 'High', value: 95 }, +]; + +export function HeatmapChartStyled() { + const isDark = useThemeColor({}, 'background') === '#000000'; + + const colorScale = isDark + ? ['#0f172a', '#1e293b', '#334155', '#64748b', '#94a3b8'] + : ['#f8fafc', '#e2e8f0', '#cbd5e1', '#94a3b8', '#64748b']; + + return ( + + + + ); +} + +``` + +##### heatmap-chart-large + +A heatmap chart with large dataset + +```tsx +import { HeatmapChart } from '@/components/charts/heatmap-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +// Generate large dataset +const generateLargeDataset = () => { + const data = []; + const months = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ]; + const hours = Array.from({ length: 24 }, (_, i) => + i.toString().padStart(2, '0') + ); + + for (const month of months) { + for (const hour of hours) { + // Generate realistic activity data (higher during work hours) + const isWorkHour = parseInt(hour) >= 8 && parseInt(hour) <= 18; + const baseValue = isWorkHour ? 40 : 10; + const randomVariation = Math.random() * 30; + const value = Math.round(baseValue + randomVariation); + + data.push({ + row: month, + col: `${hour}:00`, + value, + }); + } + } + + return data; +}; + +const largeDataset = generateLargeDataset(); + +export function HeatmapChartLarge() { + return ( + + + + ); +} + +``` +--- + +### hello-wave + +An animated waving hand emoji component with smooth rotation animation. + +**Installation:** +```bash +npx bna-ui add hello-wave +``` + +**External Dependencies:** react-native-reanimated + +**Registry Dependencies:** text, view + +**Preview:** + +![hello-wave preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 07-12-54_1.mov) + +#### Basic Usage + +```tsx +import { Hello-wave } from '@/components/ui/hello-wave'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### hello-wave-demo + +An animated waving hand emoji + +```tsx +import { HelloWave } from '@/components/ui/hello-wave'; +import React from 'react'; + +export function HellowWaveDemo() { + return 👋; +} + +``` +--- + +### icon + +A themed icon component with support for Lucide React Native icons. + +**Installation:** +```bash +npx bna-ui add icon +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Preview:** + +![icon preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/IMG_5566.PNG) + +#### Basic Usage + +```tsx +import { Icon } from '@/components/ui/icon'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### icon-demo + +A basic icon with default styling + +```tsx +import { Icon } from '@/components/ui/icon'; +import { Heart } from 'lucide-react-native'; +import React from 'react'; + +export function IconDemo() { + return ; +} + +``` + +##### icon-sizes + +Icons in different sizes + +```tsx +import { Icon } from '@/components/ui/icon'; +import { View } from '@/components/ui/view'; +import { Star } from 'lucide-react-native'; +import React from 'react'; + +export function IconSizes() { + const sizes = [16, 20, 24, 32, 40, 48]; + + return ( + + {sizes.map((size) => ( + + ))} + + ); +} + +``` + +##### icon-colors + +Icons with custom colors and themed colors + +```tsx +import { Icon } from '@/components/ui/icon'; +import { View } from '@/components/ui/view'; +import { Circle } from 'lucide-react-native'; +import React from 'react'; + +export function IconColors() { + const colors = [ + '#FF6B6B', // Red + '#4ECDC4', // Teal + '#45B7D1', // Blue + '#96CEB4', // Green + '#FECA57', // Yellow + '#FF9FF3', // Pink + '#54A0FF', // Light Blue + '#5F27CD', // Purple + ]; + + return ( + + {colors.map((color, index) => ( + + ))} + + ); +} + +``` + +##### icon-stroke + +Icons with different stroke weights + +```tsx +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Zap } from 'lucide-react-native'; +import React from 'react'; + +export function IconStroke() { + const strokeWeights = [ + { weight: 1, label: 'Light' }, + { weight: 1.5, label: 'Regular' }, + { weight: 2, label: 'Medium' }, + { weight: 2.5, label: 'Bold' }, + ]; + + return ( + + {strokeWeights.map(({ weight, label }) => ( + + + + {label} + + + ))} + + ); +} + +``` + +##### icon-interactive + +Icons with press and hover interactions + +```tsx +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { View } from '@/components/ui/view'; +import { Bookmark, Heart, Share, ThumbsUp } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function IconInteractive() { + const [liked, setLiked] = useState(false); + const [thumbsUp, setThumbsUp] = useState(false); + const [bookmarked, setBookmarked] = useState(false); + + const iconButtons = [ + { + icon: Heart, + active: liked, + onPress: () => setLiked(!liked), + activeColor: '#FF6B6B', + inactiveColor: '#888', + }, + { + icon: ThumbsUp, + active: thumbsUp, + onPress: () => setThumbsUp(!thumbsUp), + activeColor: '#4ECDC4', + inactiveColor: '#888', + }, + { + icon: Bookmark, + active: bookmarked, + onPress: () => setBookmarked(!bookmarked), + activeColor: '#FECA57', + inactiveColor: '#888', + }, + { + icon: Share, + active: false, + onPress: () => {}, + activeColor: '#45B7D1', + inactiveColor: '#888', + }, + ]; + + return ( + + {iconButtons.map( + ({ icon, active, onPress, activeColor, inactiveColor }, index) => ( + + ) + )} + + ); +} + +``` + +##### icon-grid + +A grid of commonly used icons + +```tsx +import { Icon } from '@/components/ui/icon'; +import { View } from '@/components/ui/view'; +import { + Bell, + Calendar, + Camera, + Download, + Edit, + Heart, + Home, + Mail, + Minus, + Plus, + Search, + Settings, + Star, + Trash, + Upload, + User, +} from 'lucide-react-native'; +import React from 'react'; + +export function IconGrid() { + const icons = [ + Home, + Search, + Bell, + User, + Settings, + Heart, + Star, + Mail, + Calendar, + Camera, + Download, + Upload, + Edit, + Trash, + Plus, + Minus, + ]; + + return ( + + {icons.map((IconComponent, index) => ( + + + + ))} + + ); +} + +``` + +##### icon-themed + +Icons that adapt to light and dark themes + +```tsx +import { Icon } from '@/components/ui/icon'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Monitor, Moon, Palette, Sun } from 'lucide-react-native'; +import React from 'react'; + +export function IconThemed() { + const themedIcons = [ + { + icon: Sun, + label: 'Light Theme', + lightColor: '#FFA500', + darkColor: '#FFD700', + }, + { + icon: Moon, + label: 'Dark Theme', + lightColor: '#4A5568', + darkColor: '#E2E8F0', + }, + { + icon: Monitor, + label: 'System', + lightColor: '#2D3748', + darkColor: '#F7FAFC', + }, + { + icon: Palette, + label: 'Custom', + lightColor: '#E53E3E', + darkColor: '#FC8181', + }, + ]; + + return ( + + {themedIcons.map(({ icon, label, lightColor, darkColor }, index) => ( + + + {label} + + ))} + + ); +} + +``` +--- + +### image + +A responsive image component with loading states, error handling, and flexible styling options. + +**Installation:** +```bash +npx bna-ui add image +``` + +**External Dependencies:** expo-image + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Props Interface + +```typescript +export interface ImageProps extends Omit { + variant?: 'rounded' | 'circle' | 'default'; + source: ImageSource; + style?: ExpoImageProps['style']; + containerStyle?: any; + showLoadingIndicator?: boolean; + showErrorFallback?: boolean; + errorFallbackText?: string; + loadingIndicatorSize?: 'small' | 'large'; + loadingIndicatorColor?: string; + aspectRatio?: number; + width?: number | string; + height?: number | string; +} +``` + +**Preview:** + +![image preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/IMG_5575.PNG) + +#### Basic Usage + +```tsx +import { Image } from '@/components/ui/image'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### image-demo + +A basic image with loading indicator and error fallback + +```tsx +import { Image } from '@/components/ui/image'; + +export function ImageDemo() { + return ( + + ); +} + +``` + +##### image-variants + +Images with different border radius variants + +```tsx +import { Image } from '@/components/ui/image'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ImageVariants() { + return ( + + + + Rounded + + + + + Circle + + + + + Default + + + ); +} + +``` + +##### image-sizes + +Images in different sizes and aspect ratios + +```tsx +import { Image } from '@/components/ui/image'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ImageSizes() { + return ( + + + + + + + + + + Aspect Ratio Examples + + + + + + + + ); +} + +``` + +##### image-loading + +Images with different loading indicator configurations + +```tsx +import { Image } from '@/components/ui/image'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ImageLoading() { + return ( + + + Small Loading Indicator + + + + + Large Loading Indicator + + + + + Custom Loading Color + + + + + No Loading Indicator + + + + ); +} + +``` + +##### image-error + +Images with custom error fallback messages + +```tsx +import { Image } from '@/components/ui/image'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ImageError() { + return ( + + + Default Error Fallback + + + + + Custom Error Message + + + + + No Error Fallback + + + + + Circle Variant with Error + + + + ); +} + +``` + +##### image-gallery + +Multiple images arranged in a gallery layout + +```tsx +import { Image } from '@/components/ui/image'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; +import { ScrollView } from 'react-native'; + +export function ImageGallery() { + const images = [ + 'https://picsum.photos/300/200?random=10', + 'https://picsum.photos/300/200?random=11', + 'https://picsum.photos/300/200?random=12', + 'https://picsum.photos/300/200?random=13', + 'https://picsum.photos/300/200?random=14', + 'https://picsum.photos/300/200?random=15', + ]; + + return ( + + Grid Gallery + + {images.slice(0, 4).map((uri, index) => ( + + ))} + + + Horizontal Scroll Gallery + + {images.map((uri, index) => ( + + ))} + + + Featured Image + + + ); +} + +``` + +##### image-responsive + +Responsive images that adapt to container size + +```tsx +import { Image } from '@/components/ui/image'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ImageResponsive() { + return ( + + + Full Width (Container Responsive) + + + + + + Percentage Width + + + + + + + + Flex Layout + + + + + + + + + + + ); +} + +``` + +##### image-content-fit + +Images with different content fit modes + +```tsx +import { Image } from '@/components/ui/image'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ImageContentFit() { + const imageUri = 'https://picsum.photos/600/400?random=30'; + + return ( + + + Cover (Default) + + + + + + Contain + + + + + + Fill + + + + + + Scale Down + + + + + + None + + + + + ); +} + +``` + +#### Props Examples + +```tsx +// Different variants +Default +Outline +```--- + +### input + +A styled text input component with label, validation, icons, and grouped layouts. + +**Installation:** +```bash +npx bna-ui add input +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** text, icon, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Props Interface + +```typescript +export interface InputProps extends Omit { + label?: string; + error?: string; + icon?: React.ComponentType; + rightComponent?: React.ReactNode | (() => React.ReactNode); + containerStyle?: ViewStyle; + inputStyle?: TextStyle; + labelStyle?: TextStyle; + errorStyle?: TextStyle; + variant?: 'filled' | 'outline'; + disabled?: boolean; + type?: 'input' | 'textarea'; + placeholder?: string; + rows?: number; // Only used when type="textarea" +} +``` + +**Preview:** + +![input preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 06-25-09_1.MP4) + +#### Basic Usage + +```tsx +import React from 'react'; +import { Button } from '@/components/ui/button'; +import { Card } from '@/components/ui/card'; +import { Input } from '@/components/ui/input'; +import { View } from '@/components/ui/view'; + +export default function HomeScreen() { + return ( + + + + + + + ); +} +``` + + +#### Advanced Examples + +##### input-otp-demo + +A basic OTP input with 6 digits + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import React, { useState } from 'react'; + +export function InputOTPDemo() { + const [otp, setOtp] = useState(''); + + return ( + { + console.log('OTP Complete:', value); + }} + /> + ); +} + +``` + +##### input-otp-lengths + +OTP inputs with different digit lengths + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPLengths() { + const [otp4, setOtp4] = useState(''); + const [otp6, setOtp6] = useState(''); + + return ( + + + 4 Digits + + + + + + 6 Digits (Default) + + + + + ); +} + +``` + +##### input-otp-separator + +OTP input with dash separators between digits + +```tsx +import { InputOTP, InputOTPWithSeparator } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; + +export function InputOTPSeparator() { + const [otp1, setOtp1] = useState(''); + const [otp2, setOtp2] = useState(''); + const [otp3, setOtp3] = useState(''); + + const muted = useThemeColor({}, 'textMuted'); + + return ( + + + + With Dash Separator + + + + + + + With Dot Separator + + + • + + } + /> + + + + + With Custom Separator + + + } + /> + + + ); +} + +``` + +##### input-otp-masked + +OTP input that masks digits with dots for security + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPMasked() { + const [normalOtp, setNormalOtp] = useState(''); + const [maskedOtp, setMaskedOtp] = useState(''); + + return ( + + + + Normal (Visible Digits) + + + {normalOtp && ( + + Current value: {normalOtp} + + )} + + + + + Masked (Hidden Digits) + + + {maskedOtp && ( + + Current value: {maskedOtp} + + )} + + + ); +} + +``` + +##### input-otp-error + +OTP input showing error state with validation message + +```tsx +import { Button } from '@/components/ui/button'; +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPError() { + const [otp, setOtp] = useState(''); + const [error, setError] = useState(''); + + const validateOtp = (value: string) => { + if (value.length === 6) { + // Simulate validation - reject if all digits are the same + if (value === '111111' || value === '000000') { + setError('Invalid verification code. Please try again.'); + } else { + setError(''); + } + } else { + setError(''); + } + }; + + const handleOtpChange = (value: string) => { + setOtp(value); + validateOtp(value); + }; + + const simulateError = () => { + setError('Verification code has expired. Please request a new one.'); + }; + + const clearError = () => { + setError(''); + setOtp(''); + }; + + return ( + + + + Enter Verification Code + + + Try entering "111111" or "000000" to see error state + + + + { + if (!error) { + console.log('Valid OTP:', value); + } + }} + /> + + + + + + + ); +} + +``` + +##### input-otp-disabled + +OTP input in disabled state + +```tsx +import { Button } from '@/components/ui/button'; +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPDisabled() { + const [otp, setOtp] = useState('123'); + const [disabled, setDisabled] = useState(true); + + return ( + + + Disabled State + + Toggle the button below to enable/disable the input + + + + + + + + {!disabled && ( + Current value: {otp} + )} + + ); +} + +``` + +##### input-otp-styled + +OTP input with custom colors and styling + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; + +export function InputOTPStyled() { + const [otp1, setOtp1] = useState(''); + const [otp2, setOtp2] = useState(''); + const [otp3, setOtp3] = useState(''); + + const primary = useThemeColor({}, 'primary'); + const success = '#10B981'; + const purple = '#8B5CF6'; + + return ( + + + Rounded Style + + + + + Success Theme + + + + + Large & Purple + + + + ); +} + +``` + +##### input-otp-no-cursor + +OTP input without the blinking cursor indicator + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPNoCursor() { + const [otpWithCursor, setOtpWithCursor] = useState(''); + const [otpWithoutCursor, setOtpWithoutCursor] = useState(''); + + return ( + + + + With Cursor (Default) + + + + + + Without Cursor + + + + + Tap on the inputs above to see the difference in cursor behavior + + + ); +} + +``` + +##### input-demo + +A basic input with label and placeholder + +```tsx +import { Input } from '@/components/ui/input'; +import { User } from 'lucide-react-native'; +import React from 'react'; + +export function InputDemo() { + return ( + + ); +} + +``` + +##### input-icons + +Inputs with left-side icons + +```tsx +import { Input } from '@/components/ui/input'; +import { View } from '@/components/ui/view'; +import { Lock, Mail, Phone, Search } from 'lucide-react-native'; +import React from 'react'; + +export function InputIcons() { + return ( + + + + + + + ); +} + +``` + +##### input-variants + +Different input variants - filled and outline + +```tsx +import { Input } from '@/components/ui/input'; +import { View } from '@/components/ui/view'; +import { Mail, User } from 'lucide-react-native'; +import React from 'react'; + +export function InputVariants() { + return ( + + + + + ); +} + +``` + +##### input-validation + +Inputs with error states and validation messages + +```tsx +import { Input } from '@/components/ui/input'; +import { View } from '@/components/ui/view'; +import { Lock, Mail } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function InputValidation() { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + + const emailError = + email && !email.includes('@') ? 'Please enter a valid email address' : ''; + const passwordError = + password && password.length < 6 + ? 'Password must be at least 6 characters' + : ''; + + return ( + + + + + ); +} + +``` + +##### input-right-components + +Inputs with buttons, icons, or custom components on the right + +```tsx +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { Copy, Eye, EyeOff, Search } from 'lucide-react-native'; +import React, { useState } from 'react'; +import { Pressable } from 'react-native'; + +export function InputRightComponents() { + const muted = useThemeColor({}, 'mutedForeground'); + + const [copied, setCopied] = useState(false); + const [showPassword, setShowPassword] = useState(false); + + const handleCopy = () => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( + + + Go + + } + /> + + setShowPassword(!showPassword)}> + {showPassword ? ( + + ) : ( + + )} + + } + /> + + + + + {copied ? 'Copied!' : 'Copy'} + + + } + /> + + ); +} + +``` + +##### input-disabled + +Disabled inputs with reduced opacity + +```tsx +import { Input } from '@/components/ui/input'; +import { View } from '@/components/ui/view'; +import { Mail, User } from 'lucide-react-native'; +import React from 'react'; + +export function InputDisabled() { + return ( + + + + + ); +} + +``` + +##### input-grouped + +Multiple inputs grouped together in a card-like container + +```tsx +import { GroupedInput, GroupedInputItem } from '@/components/ui/input'; +import { Mail, MapPin, Phone, User } from 'lucide-react-native'; +import React from 'react'; + +export function InputGrouped() { + return ( + + + + + + + ); +} + +``` + +##### input-form + +Complete form example with various input types + +```tsx +import { Button } from '@/components/ui/button'; +import { GroupedInput, GroupedInputItem, Input } from '@/components/ui/input'; +import { View } from '@/components/ui/view'; +import { + Calendar, + CreditCard, + Lock, + Mail, + Phone, + User, +} from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function InputForm() { + const [formData, setFormData] = useState({ + firstName: '', + lastName: '', + email: '', + password: '', + confirmPassword: '', + phone: '', + cardNumber: '', + expiryDate: '', + cvv: '', + }); + + const [errors, setErrors] = useState>({}); + + const validateForm = () => { + const newErrors: Record = {}; + + if (!formData.firstName) newErrors.firstName = 'First name is required'; + if (!formData.email) newErrors.email = 'Email is required'; + else if (!formData.email.includes('@')) + newErrors.email = 'Invalid email format'; + if (!formData.password) newErrors.password = 'Password is required'; + else if (formData.password.length < 6) + newErrors.password = 'Password must be at least 6 characters'; + if (formData.password !== formData.confirmPassword) + newErrors.confirmPassword = 'Passwords do not match'; + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + const handleSubmit = () => { + if (validateForm()) { + alert('Form submitted successfully!'); + } + }; + + return ( + + + + setFormData((prev) => ({ ...prev, firstName: text })) + } + error={errors.firstName} + /> + + setFormData((prev) => ({ ...prev, lastName: text })) + } + /> + + setFormData((prev) => ({ ...prev, email: text })) + } + error={errors.email} + keyboardType='email-address' + /> + + setFormData((prev) => ({ ...prev, phone: text })) + } + keyboardType='phone-pad' + /> + + + + + setFormData((prev) => ({ ...prev, password: text })) + } + error={errors.password} + secureTextEntry + variant='outline' + /> + + setFormData((prev) => ({ ...prev, confirmPassword: text })) + } + error={errors.confirmPassword} + secureTextEntry + variant='outline' + /> + + + + + setFormData((prev) => ({ ...prev, cardNumber: text })) + } + keyboardType='numeric' + /> + + setFormData((prev) => ({ ...prev, expiryDate: text })) + } + keyboardType='numeric' + /> + + setFormData((prev) => ({ ...prev, cvv: text })) + } + keyboardType='numeric' + /> + + + + + ); +} + +``` + +#### Props Examples + +```tsx +// Different variants +Default +Outline +```#### Practical Examples + +```tsx +// Email input with validation + +``` + +```tsx +// Password input + +``` + +--- + +### input-otp + +A secure input component for one-time passwords and verification codes. + +**Installation:** +```bash +npx bna-ui add input-otp +``` + +**Registry Dependencies:** text + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Props Interface + +```typescript +export interface InputOTPProps + extends Omit { + /** Number of OTP digits */ + length?: number; + /** Current OTP value */ + value?: string; + /** Called when OTP value changes */ + onChangeText?: (value: string) => void; + /** Called when OTP is complete */ + onComplete?: (value: string) => void; + /** Error message to display */ + error?: string; + /** Disabled state */ + disabled?: boolean; + /** Container style */ + containerStyle?: ViewStyle; + /** Individual slot style */ + slotStyle?: ViewStyle; + /** Error style */ + errorStyle?: TextStyle; + /** Whether to mask the input (show dots instead of numbers) */ + masked?: boolean; + /** Separator component between slots */ + separator?: React.ReactNode; + /** Whether to show cursor in active slot */ + showCursor?: boolean; +} +``` + +**Preview:** + +![input-otp preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 06-34-14_1.MP4) + +#### Basic Usage + +```tsx +import { Input-otp } from '@/components/ui/input-otp'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### input-otp-demo + +A basic OTP input with 6 digits + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import React, { useState } from 'react'; + +export function InputOTPDemo() { + const [otp, setOtp] = useState(''); + + return ( + { + console.log('OTP Complete:', value); + }} + /> + ); +} + +``` + +##### input-otp-lengths + +OTP inputs with different digit lengths + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPLengths() { + const [otp4, setOtp4] = useState(''); + const [otp6, setOtp6] = useState(''); + + return ( + + + 4 Digits + + + + + + 6 Digits (Default) + + + + + ); +} + +``` + +##### input-otp-separator + +OTP input with dash separators between digits + +```tsx +import { InputOTP, InputOTPWithSeparator } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; + +export function InputOTPSeparator() { + const [otp1, setOtp1] = useState(''); + const [otp2, setOtp2] = useState(''); + const [otp3, setOtp3] = useState(''); + + const muted = useThemeColor({}, 'textMuted'); + + return ( + + + + With Dash Separator + + + + + + + With Dot Separator + + + • + + } + /> + + + + + With Custom Separator + + + } + /> + + + ); +} + +``` + +##### input-otp-masked + +OTP input that masks digits with dots for security + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPMasked() { + const [normalOtp, setNormalOtp] = useState(''); + const [maskedOtp, setMaskedOtp] = useState(''); + + return ( + + + + Normal (Visible Digits) + + + {normalOtp && ( + + Current value: {normalOtp} + + )} + + + + + Masked (Hidden Digits) + + + {maskedOtp && ( + + Current value: {maskedOtp} + + )} + + + ); +} + +``` + +##### input-otp-error + +OTP input showing error state with validation message + +```tsx +import { Button } from '@/components/ui/button'; +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPError() { + const [otp, setOtp] = useState(''); + const [error, setError] = useState(''); + + const validateOtp = (value: string) => { + if (value.length === 6) { + // Simulate validation - reject if all digits are the same + if (value === '111111' || value === '000000') { + setError('Invalid verification code. Please try again.'); + } else { + setError(''); + } + } else { + setError(''); + } + }; + + const handleOtpChange = (value: string) => { + setOtp(value); + validateOtp(value); + }; + + const simulateError = () => { + setError('Verification code has expired. Please request a new one.'); + }; + + const clearError = () => { + setError(''); + setOtp(''); + }; + + return ( + + + + Enter Verification Code + + + Try entering "111111" or "000000" to see error state + + + + { + if (!error) { + console.log('Valid OTP:', value); + } + }} + /> + + + + + + + ); +} + +``` + +##### input-otp-disabled + +OTP input in disabled state + +```tsx +import { Button } from '@/components/ui/button'; +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPDisabled() { + const [otp, setOtp] = useState('123'); + const [disabled, setDisabled] = useState(true); + + return ( + + + Disabled State + + Toggle the button below to enable/disable the input + + + + + + + + {!disabled && ( + Current value: {otp} + )} + + ); +} + +``` + +##### input-otp-styled + +OTP input with custom colors and styling + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; + +export function InputOTPStyled() { + const [otp1, setOtp1] = useState(''); + const [otp2, setOtp2] = useState(''); + const [otp3, setOtp3] = useState(''); + + const primary = useThemeColor({}, 'primary'); + const success = '#10B981'; + const purple = '#8B5CF6'; + + return ( + + + Rounded Style + + + + + Success Theme + + + + + Large & Purple + + + + ); +} + +``` + +##### input-otp-no-cursor + +OTP input without the blinking cursor indicator + +```tsx +import { InputOTP } from '@/components/ui/input-otp'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function InputOTPNoCursor() { + const [otpWithCursor, setOtpWithCursor] = useState(''); + const [otpWithoutCursor, setOtpWithoutCursor] = useState(''); + + return ( + + + + With Cursor (Default) + + + + + + Without Cursor + + + + + Tap on the inputs above to see the difference in cursor behavior + + + ); +} + +``` +--- + +### line-chart + +A customizable line chart component with animations, interactions, and gradient fills. + +**Installation:** +```bash +npx bna-ui add line-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![line-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 58.MOV) + +#### Basic Usage + +```tsx +import { Line-chart } from '@/components/ui/line-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### line-chart-demo + +A basic line chart with smooth animations and grid lines + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { LineChart } from '@/components/charts/line-chart'; +import React from 'react'; + +const sampleData = [ + { x: 1, y: 10, label: 'Jan' }, + { x: 2, y: 25, label: 'Feb' }, + { x: 3, y: 15, label: 'Mar' }, + { x: 4, y: 40, label: 'Apr' }, + { x: 5, y: 30, label: 'May' }, + { x: 6, y: 55, label: 'Jun' }, + { x: 7, y: 45, label: 'Jul' }, +]; + +export function LineChartDemo() { + return ( + + + + ); +} + +``` + +##### line-chart-interactive + +An interactive line chart with touch gestures + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { LineChart } from '@/components/charts/line-chart'; +import React from 'react'; + +const sampleData = [ + { x: 'Q1', y: 45, label: 'Q1 2024' }, + { x: 'Q2', y: 67, label: 'Q2 2024' }, + { x: 'Q3', y: 52, label: 'Q3 2024' }, + { x: 'Q4', y: 89, label: 'Q4 2024' }, + { x: 'Q1', y: 95, label: 'Q1 2025' }, + { x: 'Q2', y: 110, label: 'Q2 2025' }, +]; + +export function LineChartInteractive() { + return ( + + + + ); +} + +``` + +##### line-chart-styled + +A customized line chart with custom styling + +```tsx +import { ChartContainer } from '@/components/charts/chart-container'; +import { LineChart } from '@/components/charts/line-chart'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const sampleData = [ + { x: 'Mon', y: 23, label: 'Monday' }, + { x: 'Tue', y: 45, label: 'Tuesday' }, + { x: 'Wed', y: 67, label: 'Wednesday' }, + { x: 'Thu', y: 34, label: 'Thursday' }, + { x: 'Fri', y: 89, label: 'Friday' }, + { x: 'Sat', y: 56, label: 'Saturday' }, + { x: 'Sun', y: 78, label: 'Sunday' }, +]; + +export function LineChartStyled() { + const borderColor = useThemeColor({}, 'border'); + const backgroundColor = useThemeColor({}, 'card'); + + return ( + + + + ); +} + +``` + +##### line-chart-minimal + +A minimal line chart without labels or grid + +```tsx +import { LineChart } from '@/components/charts/line-chart'; +import React from 'react'; + +const sampleData = [ + { x: 1, y: 20 }, + { x: 2, y: 45 }, + { x: 3, y: 28 }, + { x: 4, y: 67 }, + { x: 5, y: 89 }, + { x: 6, y: 34 }, +]; + +export function LineChartMinimal() { + return ( + + ); +} + +``` +--- + +### link + +A navigation component that handles both internal and external links with customizable browser behavior. + +**Installation:** +```bash +npx bna-ui add link +``` + +**External Dependencies:** expo-router, expo-web-browser + +**Registry Dependencies:** text + +**Preview:** + +![link preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 06-40-41_1.MP4) + +#### Basic Usage + +```tsx +import { Link } from '@/components/ui/link'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### link-demo + +Basic internal navigation links + +```tsx +import { Link } from '@/components/ui/link'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function LinkDemo() { + return ( + + Go to Profile + Settings + User Details + + ); +} + +``` + +##### link-external + +Links that open external URLs + +```tsx +import { Link } from '@/components/ui/link'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function LinkExternal() { + return ( + + Visit GitHub + Expo Documentation + React Native Docs + + ); +} + +``` + +##### link-browser + +Links with different browser opening behaviors + +```tsx +import { Link } from '@/components/ui/link'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function LinkBrowser() { + return ( + + + + In-App Browser (Default) + + + + Open GitHub in-app + + Open Expo docs in-app + + + + + + External Browser + + + + Open GitHub externally + + + Open Expo docs externally + + + + + ); +} + +``` + +##### link-custom + +Links with custom child components instead of text + +```tsx +import { Button } from '@/components/ui/button'; +import { Link } from '@/components/ui/link'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { ExternalLink, HomeIcon, Mail } from 'lucide-react-native'; +import React from 'react'; + +export function LinkCustom() { + return ( + + + + + + + + + + External Link + + + + + + + + + ); +} + +``` + +##### link-types + +Various types of links including mailto and tel + +```tsx +import { Link } from '@/components/ui/link'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function LinkTypes() { + return ( + + + + Internal Navigation + + + Home Page + About Us + + Product Details + + + + + + External URLs + + Google + Example Site + + + + + + Communication Links + + + Send Email + Call Phone + + Email with Subject + + Send SMS + + + + ); +} + +``` + +##### link-styled + +Links with custom styling and variants + +```tsx +import { Link } from '@/components/ui/link'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function LinkStyled() { + return ( + + + Default Styled + + Default Link Style + External Link + + + + + + Custom Text Styling + + + + + Red Bold Link + + + + + + Green Italic Link + + + + + + Purple Uppercase + + + + + + + Inline Links + + This is a paragraph with an inline link that + flows naturally with the text. You can also have{' '} + external inline links in your + content. + + + + ); +} + +``` + +##### link-buttons + +Links styled as buttons for navigation + +```tsx +import { Button } from '@/components/ui/button'; +import { Link } from '@/components/ui/link'; +import { View } from '@/components/ui/view'; +import { ExternalLink, Settings, User } from 'lucide-react-native'; +import React from 'react'; + +export function LinkButtons() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +``` +--- + +### media-picker + +A versatile component for selecting images and videos from device gallery or camera with preview capabilities. + +**Installation:** +```bash +npx bna-ui add media-picker +``` + +**External Dependencies:** expo-image, expo-image-picker, expo-media-library, lucide-react-native + +**Registry Dependencies:** button, text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Props Interface + +```typescript +export interface MediaPickerProps { + children?: React.ReactNode; + style?: ViewStyle; + size?: ButtonSize; + variant?: ButtonVariant; + icon?: React.ComponentType; + disabled?: boolean; + mediaType?: MediaType; + multiple?: boolean; + maxSelection?: number; + quality?: MediaQuality; + buttonText?: string; + placeholder?: string; + gallery?: boolean; + showPreview?: boolean; + previewSize?: number; + selectedAssets?: MediaAsset[]; + onSelectionChange?: (assets: MediaAsset[]) => void; + onError?: (error: string) => void; +} +``` + +**Preview:** + +![media-picker preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 07-03-09_1.MP4) + +#### Basic Usage + +```tsx +import { Media-picker } from '@/components/ui/media-picker'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### media-picker-demo + +A basic media picker with image and video selection + +```tsx +import { MediaPicker } from '@/components/ui/media-picker'; +import React from 'react'; + +export function MediaPickerDemo() { + return ( + { + console.log('Selected assets:', assets); + }} + onError={(error) => { + console.error('Media picker error:', error); + }} + /> + ); +} + +``` + +##### media-picker-images + +Media picker configured for images only + +```tsx +import { MediaPicker } from '@/components/ui/media-picker'; +import { Image } from 'lucide-react-native'; +import React from 'react'; + +export function MediaPickerImages() { + return ( + { + console.log('Selected images:', assets); + }} + /> + ); +} + +``` + +##### media-picker-videos + +Media picker configured for videos only + +```tsx +import { MediaPicker } from '@/components/ui/media-picker'; +import { Video } from 'lucide-react-native'; +import React from 'react'; + +export function MediaPickerVideos() { + return ( + { + console.log('Selected videos:', assets); + }} + /> + ); +} + +``` + +##### media-picker-multiple + +Media picker with multiple selection enabled + +```tsx +import { MediaPicker } from '@/components/ui/media-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Plus } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function MediaPickerMultiple() { + const [selectedCount, setSelectedCount] = useState(0); + + return ( + + { + setSelectedCount(assets.length); + console.log('Selected assets:', assets); + }} + /> + + {selectedCount > 0 && ( + + {selectedCount} item{selectedCount !== 1 ? 's' : ''} selected + + )} + + ); +} + +``` + +##### media-picker-gallery + +Media picker with custom gallery modal + +```tsx +import { MediaAsset, MediaPicker } from '@/components/ui/media-picker'; +import { Folder } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function MediaPickerGallery() { + const [selected, setSelected] = useState([]); + + return ( + + ); +} + +``` + +##### media-picker-preview + +Media picker showing selected media previews + +```tsx +import { MediaAsset, MediaPicker } from '@/components/ui/media-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { ImageIcon } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function MediaPickerPreview() { + const [assets, setAssets] = useState([]); + + return ( + + { + setAssets(newAssets); + console.log('Assets with preview:', newAssets); + }} + /> + + {assets.length > 0 && ( + + + {assets.length} item{assets.length !== 1 ? 's' : ''} selected + + + Types: {assets.map((a) => a.type).join(', ')} + + + )} + + ); +} + +``` + +##### media-picker-quality + +Media picker with different quality settings + +```tsx +import { MediaPicker } from '@/components/ui/media-picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Settings } from 'lucide-react-native'; +import React from 'react'; + +export function MediaPickerQuality() { + return ( + + + + High Quality + + { + console.log('High quality assets:', assets); + }} + /> + + + + + Medium Quality + + { + console.log('Medium quality assets:', assets); + }} + /> + + + + + Low Quality + + { + console.log('Low quality assets:', assets); + }} + /> + + + ); +} + +``` + +#### Props Examples + +```tsx +// Different variants +Default +Outline +``` + +```tsx +// Different sizes +Small +Large +```--- + +### mode-toggle + +An animated button component for switching between light and dark themes. + +**Installation:** +```bash +npx bna-ui add mode-toggle +``` + +**External Dependencies:** lucide-react-native, react-native-reanimated + +**Registry Dependencies:** button, icon + +**Required Hooks:** useModeToggle + +**Preview:** + +![mode-toggle preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 07-11-16_1.MP4) + +#### Basic Usage + +```tsx +import { Mode-toggle } from '@/components/ui/mode-toggle'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### mode-toggle-demo + +Animated theme toggle button + +```tsx +import { ModeToggle } from '@/components/ui/mode-toggle'; +import React from 'react'; + +export function ModeToggleDemo() { + return ; +} + +``` +--- + +### onboarding + +A customizable multi-step onboarding flow with smooth animations and gesture support. + +**Installation:** +```bash +npx bna-ui add onboarding +``` + +**External Dependencies:** react-native-gesture-handler, react-native-reanimated + +**Registry Dependencies:** button, text + +**Required Hooks:** useThemeColor + +#### Props Interface + +```typescript +export interface OnboardingProps { + steps: OnboardingStep[]; + onComplete: () => void; + onSkip?: () => void; + showSkip?: boolean; + showProgress?: boolean; + swipeEnabled?: boolean; + primaryButtonText?: string; + skipButtonText?: string; + nextButtonText?: string; + backButtonText?: string; + style?: ViewStyle; + children?: React.ReactNode; +} +``` + +**Preview:** + +![onboarding preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 07-20-37_1.MP4) + +#### Basic Usage + +```tsx +import { Onboarding } from '@/components/ui/onboarding'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### onboarding-demo + +A basic onboarding flow with multiple steps + +```tsx +import { Onboarding, useOnboarding } from '@/components/ui/onboarding'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; + +export const OnboardingPresets = { + welcome: [ + { + id: 'welcome', + title: 'Welcome to Our App', + description: + 'Discover amazing features and get started with your journey.', + icon: 👋, + }, + { + id: 'features', + title: 'Powerful Features', + description: + 'Experience cutting-edge functionality designed to make your life easier.', + icon: , + }, + { + id: 'personalize', + title: 'Personalize Your Experience', + description: 'Customize the app to match your preferences and workflow.', + icon: 🎨, + }, + { + id: 'ready', + title: "You're All Set!", + description: + "Everything is ready. Let's start exploring what you can achieve.", + icon: 🚀, + }, + ], + + features: [ + { + id: 'organize', + title: 'Stay Organized', + description: 'Keep all your important information in one secure place.', + icon: 📋, + }, + { + id: 'collaborate', + title: 'Collaborate Seamlessly', + description: 'Work together with your team in real-time, anywhere.', + icon: 🤝, + }, + { + id: 'automate', + title: 'Automate Your Workflow', + description: 'Set up smart automations to save time and reduce errors.', + icon: 🤖, + }, + ], + + security: [ + { + id: 'secure', + title: 'Your Data is Secure', + description: + 'We use end-to-end encryption to keep your information safe.', + icon: 🔒, + }, + { + id: 'privacy', + title: 'Privacy First', + description: 'We never share your personal data with third parties.', + icon: 🛡️, + }, + { + id: 'control', + title: "You're in Control", + description: 'Manage your privacy settings and data preferences anytime.', + icon: ⚙️, + }, + ], +}; + +export function OnboardingDemo() { + const { hasCompletedOnboarding, completeOnboarding, skipOnboarding } = + useOnboarding(); + + if (hasCompletedOnboarding) { + return ( + + Welcome Back! + You've already completed the onboarding. + + ); + } + + return ( + + ); +} + +``` + +##### onboarding-images + +Onboarding flow with custom images for each step + +```tsx +import { Image } from '@/components/ui/image'; +import { Onboarding, OnboardingStep } from '@/components/ui/onboarding'; +import React from 'react'; + +const WelcomeImage = () => ( + +); + +const FeaturesImage = () => ( + +); + +const StartImage = () => ( + +); + +export function OnboardingImages() { + const steps: OnboardingStep[] = [ + { + id: '1', + title: 'Welcome to the Team', + description: + 'Join thousands of users who have already discovered the power of our platform.', + image: , + }, + { + id: '2', + title: 'Powerful Features', + description: + 'Access advanced tools and features that will help you achieve your goals faster.', + image: , + }, + { + id: '3', + title: 'Ready to Launch', + description: + "Everything is set up and ready. Let's start building something amazing together!", + image: , + }, + ]; + + return ( + console.log('Onboarding with images completed!')} + onSkip={() => console.log('Onboarding with images skipped!')} + primaryButtonText="Let's Go" + nextButtonText='Continue' + /> + ); +} + +``` + +##### onboarding-styled + +Onboarding with custom colors and styling + +```tsx +import { Onboarding, OnboardingStep } from '@/components/ui/onboarding'; +import { Feather } from '@expo/vector-icons'; +import { LinearGradient } from 'expo-linear-gradient'; +import React from 'react'; + +const GradientIcon = ({ + iconName, + colors, +}: { + iconName: string; + colors: [string, string]; +}) => ( + + + +); + +export function OnboardingStyled() { + const steps: OnboardingStep[] = [ + { + id: '1', + title: 'Secure & Private', + description: + 'Your data is protected with end-to-end encryption. We prioritize your privacy above all else.', + icon: , + backgroundColor: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', + }, + { + id: '2', + title: 'Lightning Fast', + description: + 'Experience blazing fast performance with our optimized infrastructure and smart caching.', + icon: , + backgroundColor: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)', + }, + { + id: '3', + title: 'Always Connected', + description: + 'Stay connected with real-time sync across all your devices. Never miss an important update.', + icon: , + backgroundColor: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)', + }, + ]; + + return ( + console.log('Styled onboarding completed!')} + onSkip={() => console.log('Styled onboarding skipped!')} + primaryButtonText='Start Now' + nextButtonText='Next Step' + skipButtonText='Skip Intro' + style={{ backgroundColor: '#1a1a2e' }} + /> + ); +} + +``` + +##### onboarding-no-skip + +Onboarding flow without skip functionality + +```tsx +import { Onboarding, OnboardingStep } from '@/components/ui/onboarding'; +import { Feather } from '@expo/vector-icons'; +import React from 'react'; +import { View } from 'react-native'; + +const InfoIcon = ({ name, color }: { name: string; color: string }) => ( + + + +); + +export function OnboardingNoSkip() { + const steps: OnboardingStep[] = [ + { + id: '1', + title: 'Setup Your Profile', + description: + "Let's start by setting up your profile. This helps us personalize your experience.", + icon: , + }, + { + id: '2', + title: 'Choose Preferences', + description: + 'Select your preferences to customize the app according to your needs and workflow.', + icon: , + }, + { + id: '3', + title: 'Enable Notifications', + description: + 'Stay updated with important notifications. You can always change these settings later.', + icon: , + }, + { + id: '4', + title: 'All Set!', + description: + "Congratulations! Your account is now ready. Let's dive in and explore the features.", + icon: , + }, + ]; + + return ( + console.log('Required onboarding completed!')} + showSkip={false} + primaryButtonText='Complete Setup' + nextButtonText='Continue' + backButtonText='Previous' + /> + ); +} + +``` + +##### onboarding-no-swipe + +Onboarding with swipe gestures disabled + +```tsx +import { Onboarding, OnboardingStep } from '@/components/ui/onboarding'; +import { Feather } from '@expo/vector-icons'; +import React from 'react'; +import { View } from 'react-native'; + +const StepIcon = ({ + name, + bgColor, + iconColor, +}: { + name: string; + bgColor: string; + iconColor: string; +}) => ( + + + +); + +export function OnboardingNoSwipe() { + const steps: OnboardingStep[] = [ + { + id: '1', + title: 'Tutorial Mode', + description: + 'Follow along with our step-by-step tutorial. Use the buttons below to navigate at your own pace.', + icon: , + }, + { + id: '2', + title: 'Learn the Basics', + description: + 'Master the fundamental features that will help you get the most out of our platform.', + icon: , + }, + { + id: '3', + title: 'Practice Makes Perfect', + description: + 'Try out the features yourself in a safe environment before working with real data.', + icon: , + }, + ]; + + return ( + console.log('Tutorial completed!')} + onSkip={() => console.log('Tutorial skipped!')} + swipeEnabled={false} + showProgress={true} + primaryButtonText='Start Using App' + nextButtonText='Next Lesson' + skipButtonText='Skip Tutorial' + /> + ); +} + +``` + +##### onboarding-custom-buttons + +Onboarding with custom button text + +```tsx +import { Onboarding, OnboardingStep } from '@/components/ui/onboarding'; +import { Heart, Rocket, Target } from 'lucide-react-native'; +import React from 'react'; + +export function OnboardingCustomButtons() { + const steps: OnboardingStep[] = [ + { + id: '1', + title: '🎉 Welcome Aboard!', + description: + "We're thrilled to have you join our community of innovators and creators.", + icon: , + }, + { + id: '2', + title: '🚀 Boost Your Productivity', + description: + 'Discover powerful tools that will transform the way you work and collaborate.', + icon: , + }, + { + id: '3', + title: '🎯 Achieve Your Goals', + description: + 'Set ambitious targets and track your progress with our advanced analytics.', + icon: , + }, + ]; + + return ( + console.log('Custom buttons onboarding completed!')} + onSkip={() => console.log('Custom buttons onboarding skipped!')} + primaryButtonText='🚀 Launch App' + nextButtonText='👉 Continue' + backButtonText='👈 Back' + skipButtonText='⏭️ Skip for Now' + /> + ); +} + +``` + +##### onboarding-hook + +Media picker showing selected media previews + +```tsx +import { Button } from '@/components/ui/button'; +import { + Onboarding, + OnboardingStep, + useOnboarding, +} from '@/components/ui/onboarding'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +function MainApp() { + const { resetOnboarding } = useOnboarding(); + + return ( + + + {/* {} */} + + Welcome to the App! + + + You've successfully completed the onboarding process. You can restart + it anytime using the button below. + + + + + + ); +} + +function OnboardingFlow() { + const { completeOnboarding, skipOnboarding } = useOnboarding(); + + const steps: OnboardingStep[] = [ + { + id: '1', + title: 'Hook-based State', + description: + 'This onboarding uses the useOnboarding hook to manage state across your entire app.', + // icon: '🪝', + }, + { + id: '2', + title: 'Persistent State', + description: + 'The hook remembers your progress and can be used to control onboarding flow throughout your app.', + // icon: '📱', + }, + { + id: '3', + title: 'Easy Integration', + description: + 'Integrate onboarding state with your existing app navigation and user management.', + // icon: '🚀', + }, + ]; + + return ( + + ); +} + +export function OnboardingHook() { + const { hasCompletedOnboarding } = useOnboarding(); + + return hasCompletedOnboarding ? : ; +} + +``` +--- + +### parallax-scrollview + +A scroll view with parallax header effect that transforms as the user scrolls. + +**Installation:** +```bash +npx bna-ui add parallax-scrollview +``` + +**External Dependencies:** react-native-reanimated + +**Registry Dependencies:** view + +**Required Hooks:** useBottomTabOverflow, useThemeColor + +**Preview:** + +![parallax-scrollview preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 08-33-25_1.MP4) + +#### Basic Usage + +```tsx +import { Parallax-scrollview } from '@/components/ui/parallax-scrollview'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### parallax-scrollview-demo + +A basic parallax scroll view with header image + +```tsx +import { ParallaxScrollView } from '@/components/ui/parallax-scrollview'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Image } from 'expo-image'; +import React from 'react'; + +export function ParallaxScrollViewDemo() { + return ( + + } + > + + Parallax Scroll View + + This is a basic example of a parallax scroll view. The header image + moves at a different speed than the content as you scroll, creating a + beautiful parallax effect. + + + Scroll up and down to see the parallax animation in action. The header + will transform and scale based on your scroll position. + + + You can also try pulling down (over-scrolling) to see the header scale + up beyond its normal size. + + + + ); +} + +``` + +##### parallax-scrollview-custom-height + +Parallax scroll view with custom header height + +```tsx +import { ParallaxScrollView } from '@/components/ui/parallax-scrollview'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Image } from 'expo-image'; +import React from 'react'; + +export function ParallaxScrollViewCustomHeight() { + return ( + + } + > + + Custom Header Height + + This example demonstrates a taller header (500px) that provides more + visual impact and space for the parallax effect. + + + Larger headers work great for hero sections, profile pages, or any + screen where you want to make a strong visual impression. + + + The parallax animation remains smooth regardless of the header size, + automatically adjusting the transformation values based on the + specified height. + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. + + + + ); +} + +``` + +##### parallax-scrollview-gradient + +Parallax scroll view with gradient overlay header + +```tsx +import { ParallaxScrollView } from '@/components/ui/parallax-scrollview'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Image } from 'expo-image'; +import { LinearGradient } from 'expo-linear-gradient'; +import React from 'react'; + +export function ParallaxScrollViewGradient() { + return ( + + + + + + Scenic Mountain View + + + + } + > + + Gradient Overlay Header + + This example shows how to add a gradient overlay to your header image, + which is perfect for ensuring text readability over images. + + + The gradient creates a smooth transition from the image to a darker + overlay at the bottom, where you can place text or other UI elements. + + + This technique is commonly used in hero sections, article headers, and + profile screens where you need to overlay content on images. + + + + The parallax effect works seamlessly with gradient overlays and + maintains smooth performance even with multiple layers. + + + + ); +} + +``` + +##### parallax-scrollview-profile + +Complete profile screen using parallax scroll view + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { Badge } from '@/components/ui/badge'; +import { ParallaxScrollView } from '@/components/ui/parallax-scrollview'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Image } from 'expo-image'; +import { LinearGradient } from 'expo-linear-gradient'; +import React from 'react'; + +export function ParallaxScrollViewProfile() { + return ( + + + + + + + JD + + + + John Doe + + + Software Engineer + + + + + } + > + + + About + + Passionate software engineer with 5+ years of experience in mobile + and web development. Specialized in React Native, TypeScript, and + modern UI frameworks. + + + + + Skills + + {[ + 'React Native', + 'TypeScript', + 'Node.js', + 'GraphQL', + 'MongoDB', + ].map((skill) => ( + + {skill} + + ))} + + + + + Experience + + + Senior Mobile Developer + Tech Corp • 2022 - Present + + Leading mobile development team and architecting scalable React + Native applications. + + + + Frontend Developer + StartupXYZ • 2020 - 2022 + + Built responsive web applications using React and modern + frontend technologies. + + + + + + + Contact + 📧 john.doe@example.com + 🌐 johndoe.dev + 📱 LinkedIn: @johndoe + + + + ); +} + +``` + +##### parallax-scrollview-article + +Article layout with parallax hero image + +```tsx +import { ParallaxScrollView } from '@/components/ui/parallax-scrollview'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Image } from 'expo-image'; +import { LinearGradient } from 'expo-linear-gradient'; +import React from 'react'; + +export function ParallaxScrollViewArticle() { + return ( + + + + + + + TECHNOLOGY + + + + The Future of Mobile Development + + + Published on March 15, 2024 • 8 min read + + + + } + > + + + Mobile development has evolved dramatically over the past decade, with + new frameworks, tools, and paradigms emerging to meet the ever-growing + demands of users and businesses alike. + + + + React Native has established itself as a leading cross-platform + solution, enabling developers to write once and deploy everywhere. The + framework's component-based architecture and hot reloading + capabilities have revolutionized the development experience. + + + + Key Trends in 2024 + + • AI-powered development tools and code generation + + + • Enhanced performance optimization techniques + + + • Better cross-platform native module integration + + + • Improved debugging and testing frameworks + + + + + + "The best mobile apps are those that feel native to each platform + while maintaining a consistent user experience across devices." + + + + + Performance optimization remains a critical consideration. Modern apps + need to handle complex animations, large datasets, and real-time + updates while maintaining smooth 60fps interactions. + + + + Looking Ahead + + The future of mobile development is bright, with emerging + technologies like AR/VR integration, improved offline capabilities, + and seamless cloud integration opening new possibilities for + developers and users alike. + + + + + + + JD + + + John Developer + + Senior Mobile Engineer + + + + + + + ); +} + +``` + +##### parallax-scrollview-product + +Product detail screen with parallax image gallery + +```tsx +import { Button } from '@/components/ui/button'; +import { ParallaxScrollView } from '@/components/ui/parallax-scrollview'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { Image } from 'expo-image'; +import React from 'react'; + +export function ParallaxScrollViewProduct() { + return ( + + } + > + + + + + Running Shoes + + Nike Air Zoom Series + + + + $159.99 + + + + + + {[1, 2, 3, 4, 5].map((star) => ( + + ★ + + ))} + + 4.8 (2.1k reviews) + + + + + Description + + Experience ultimate comfort and performance with these premium + running shoes. Featuring advanced cushioning technology and + breathable mesh construction for all-day comfort. + + + + + Available Sizes + + {['7', '7.5', '8', '8.5', '9', '9.5', '10', '10.5', '11'].map( + (size) => ( + + US {size} + + ) + )} + + + + + Color Options + + + + + + + + + + Features + + + + Lightweight design for all-day wear + + + + Enhanced arch support + + + + + + Shipping & Returns + + • Free shipping on orders over $100 + + • 30-day return policy + • 1-year manufacturer warranty + + + + + + + + + + ); +} + +``` +--- + +### picker + +A customizable dropdown picker component with search, sections, and multiple selection support. + +**Installation:** +```bash +npx bna-ui add picker +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** icon, scroll-view, text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![picker preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 09-07-27_1.MP4) + +#### Basic Usage + +```tsx +import { Picker } from '@/components/ui/picker'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### picker-demo + +A basic picker with simple options + +```tsx +import { Picker } from '@/components/ui/picker'; +import React, { useState } from 'react'; + +export function PickerDemo() { + const [value, setValue] = useState(''); + + const options = [ + { label: 'Apple', value: 'apple' }, + { label: 'Banana', value: 'banana' }, + { label: 'Orange', value: 'orange' }, + { label: 'Grape', value: 'grape' }, + ]; + + return ( + + ); +} + +``` + +##### picker-sections + +Picker with grouped options in sections + +```tsx +import { Picker } from '@/components/ui/picker'; +import React, { useState } from 'react'; + +export function PickerSections() { + const [value, setValue] = useState(''); + + const sections = [ + { + title: 'Fruits', + options: [ + { label: 'Apple', value: 'apple' }, + { label: 'Banana', value: 'banana' }, + { label: 'Orange', value: 'orange' }, + ], + }, + { + title: 'Vegetables', + options: [ + { label: 'Carrot', value: 'carrot' }, + { label: 'Broccoli', value: 'broccoli' }, + { label: 'Spinach', value: 'spinach' }, + ], + }, + ]; + + return ( + + ); +} + +``` + +##### picker-multiple + +Picker allowing multiple selections + +```tsx +import { Picker } from '@/components/ui/picker'; +import React, { useState } from 'react'; + +export function PickerMultiple() { + const [values, setValues] = useState([]); + + const options = [ + { label: 'JavaScript', value: 'js' }, + { label: 'TypeScript', value: 'ts' }, + { label: 'Python', value: 'py' }, + { label: 'Java', value: 'java' }, + { label: 'C++', value: 'cpp' }, + { label: 'Rust', value: 'rust' }, + ]; + + return ( + + ); +} + +``` + +##### picker-searchable + +Picker with search functionality + +```tsx +import { Picker } from '@/components/ui/picker'; +import React, { useState } from 'react'; + +export function PickerSearchable() { + const [value, setValue] = useState(''); + + const options = [ + { label: 'United States', value: 'us' }, + { label: 'Canada', value: 'ca' }, + { label: 'United Kingdom', value: 'uk' }, + { label: 'Germany', value: 'de' }, + { label: 'France', value: 'fr' }, + { label: 'Japan', value: 'jp' }, + { label: 'Australia', value: 'au' }, + { label: 'Brazil', value: 'br' }, + { label: 'India', value: 'in' }, + { label: 'China', value: 'cn' }, + ]; + + return ( + + ); +} + +``` + +##### picker-variants + +Different picker variants: outline, filled, and group + +```tsx +import { Picker } from '@/components/ui/picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function PickerVariants() { + const [outlineValue, setOutlineValue] = useState(''); + const [filledValue, setFilledValue] = useState(''); + const [groupValue, setGroupValue] = useState(''); + + const options = [ + { label: 'Small', value: 'sm' }, + { label: 'Medium', value: 'md' }, + { label: 'Large', value: 'lg' }, + { label: 'Extra Large', value: 'xl' }, + ]; + + return ( + + + + Outline Variant + + + + + + + Filled Variant + + + + + + + Group Variant + + + + + ); +} + +``` + +##### picker-styled + +Picker with custom styling, icons, and labels + +```tsx +import { Picker } from '@/components/ui/picker'; +import { MapPin, Settings, User } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function PickerStyled() { + const [location, setLocation] = useState(''); + const [user, setUser] = useState(''); + const [setting, setSetting] = useState(''); + + const locations = [ + { label: 'New York', value: 'ny' }, + { label: 'Los Angeles', value: 'la' }, + { label: 'Chicago', value: 'chi' }, + ]; + + const users = [ + { label: 'John Doe', value: 'john' }, + { label: 'Jane Smith', value: 'jane' }, + { label: 'Bob Johnson', value: 'bob' }, + ]; + + const settings = [ + { label: 'Notifications', value: 'notifications' }, + { label: 'Privacy', value: 'privacy' }, + { label: 'Account', value: 'account' }, + ]; + + return ( + <> + + + + + + + ); +} + +``` + +##### picker-form + +Picker integrated with form validation and error handling + +```tsx +import { Button } from '@/components/ui/button'; +import { Picker } from '@/components/ui/picker'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function PickerForm() { + const [category, setCategory] = useState(''); + const [priority, setPriority] = useState(''); + const [errors, setErrors] = useState<{ + category?: string; + priority?: string; + }>({}); + + const categories = [ + { label: 'Bug Report', value: 'bug' }, + { label: 'Feature Request', value: 'feature' }, + { label: 'General Inquiry', value: 'general' }, + ]; + + const priorities = [ + { label: 'Low', value: 'low' }, + { label: 'Medium', value: 'medium' }, + { label: 'High', value: 'high' }, + { label: 'Critical', value: 'critical' }, + ]; + + const handleSubmit = () => { + const newErrors: { category?: string; priority?: string } = {}; + + if (!category) { + newErrors.category = 'Please select a category'; + } + + if (!priority) { + newErrors.priority = 'Please select a priority'; + } + + setErrors(newErrors); + + if (Object.keys(newErrors).length === 0) { + // Form is valid + console.log('Form submitted:', { category, priority }); + } + }; + + return ( + + { + setCategory(value); + if (errors.category) { + setErrors((prev) => ({ ...prev, category: undefined })); + } + }} + placeholder='Select category...' + label='Category' + error={errors.category} + variant='outline' + /> + + { + setPriority(value); + if (errors.priority) { + setErrors((prev) => ({ ...prev, priority: undefined })); + } + }} + placeholder='Select priority...' + label='Priority' + error={errors.priority} + variant='outline' + /> + + + + ); +} + +``` + +##### picker-advanced + +Picker with descriptions, disabled options, and custom modal title + +```tsx +import { Picker } from '@/components/ui/picker'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function PickerAdvanced() { + const [plan, setPlan] = useState(''); + + const sections = [ + { + title: 'Individual Plans', + options: [ + { + label: 'Basic', + value: 'basic', + description: '$9/month - Perfect for individuals', + }, + { + label: 'Pro', + value: 'pro', + description: '$19/month - Advanced features included', + }, + ], + }, + { + title: 'Team Plans', + options: [ + { + label: 'Team', + value: 'team', + description: '$39/month - Collaboration tools', + }, + { + label: 'Enterprise', + value: 'enterprise', + description: '$99/month - Full enterprise features', + }, + { + label: 'Custom', + value: 'custom', + description: 'Contact us for pricing', + disabled: true, + }, + ], + }, + ]; + + return ( + + + + {plan && ( + + + Selected:{' '} + { + sections.flatMap((s) => s.options).find((o) => o.value === plan) + ?.label + } + + + )} + + ); +} + +``` +--- + +### pie-chart + +A customizable pie chart component with smooth animations and flexible styling. + +**Installation:** +```bash +npx bna-ui add pie-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![pie-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 44.MOV) + +#### Basic Usage + +```tsx +import { Pie-chart } from '@/components/ui/pie-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### pie-chart-demo + +A pie chart with smooth animations + +```tsx +import { PieChart } from '@/components/charts/pie-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Sales', value: 120 }, + { label: 'Marketing', value: 98 }, + { label: 'Support', value: 86 }, + { label: 'Development', value: 140 }, + { label: 'Design', value: 75 }, + { label: 'HR', value: 65 }, +]; + +export function PieChartDemo() { + return ( + + + + ); +} + +``` + +##### pie-chart-sample + +A sample pie chart + +```tsx +import { PieChart } from '@/components/charts/pie-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Mobile', value: 45 }, + { label: 'Desktop', value: 35 }, + { label: 'Tablet', value: 15 }, + { label: 'Other', value: 5 }, +]; + +export function PieChartSample() { + return ( + + + + ); +} + +``` + +##### pie-chart-styled + +A customized pie chart with custom colors and styling + +```tsx +import { PieChart } from '@/components/charts/pie-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +export function PieChartStyled() { + const primaryColor = useThemeColor({}, 'primary'); + const successColor = useThemeColor({}, 'green'); + const warningColor = useThemeColor({}, 'orange'); + const errorColor = useThemeColor({}, 'red'); + + const styledData = [ + { label: 'Completed', value: 65, color: successColor }, + { label: 'In Progress', value: 20, color: primaryColor }, + { label: 'Pending', value: 10, color: warningColor }, + { label: 'Failed', value: 5, color: errorColor }, + ]; + + return ( + + + + ); +} + +``` + +##### pie-chart-large + +A pie chart with large dataset + +```tsx +import { PieChart } from '@/components/charts/pie-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const largeData = [ + { label: 'North America', value: 35 }, + { label: 'Europe', value: 28 }, + { label: 'Asia Pacific', value: 22 }, + { label: 'Latin America', value: 8 }, + { label: 'Middle East', value: 4 }, + { label: 'Africa', value: 3 }, +]; + +export function PieChartLarge() { + return ( + + + + ); +} + +``` +--- + +### polar-area-chart + +A customizable polar area chart component with smooth animations and flexible styling for displaying radial data. + +**Installation:** +```bash +npx bna-ui add polar-area-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![polar-area-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 40.MOV) + +#### Basic Usage + +```tsx +import { Polar-area-chart } from '@/components/ui/polar-area-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### polar-area-chart-demo + +A polar area chart with smooth animations + +```tsx +import { PolarAreaChart } from '@/components/charts/polar-area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Sales', value: 120 }, + { label: 'Marketing', value: 98 }, + { label: 'Support', value: 86 }, + { label: 'Development', value: 140 }, + { label: 'Design', value: 75 }, + { label: 'HR', value: 65 }, +]; + +export function PolarAreaChartDemo() { + return ( + + + + ); +} + +``` + +##### polar-area-chart-sample + +A sample polar area chart + +```tsx +import { PolarAreaChart } from '@/components/charts/polar-area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const skillsData = [ + { label: 'JavaScript', value: 95 }, + { label: 'React', value: 88 }, + { label: 'TypeScript', value: 82 }, + { label: 'Node.js', value: 78 }, + { label: 'Python', value: 65 }, +]; + +export function PolarAreaChartSample() { + const primaryColor = useThemeColor({}, 'primary'); + const blueColor = useThemeColor({}, 'blue'); + const greenColor = useThemeColor({}, 'green'); + const orangeColor = useThemeColor({}, 'orange'); + const purpleColor = useThemeColor({}, 'purple'); + + const dataWithColors = skillsData.map((item, index) => ({ + ...item, + color: [primaryColor, blueColor, greenColor, orangeColor, purpleColor][ + index + ], + })); + + return ( + + + + ); +} + +``` + +##### polar-area-chart-styled + +A customized polar area chart with custom colors and styling + +```tsx +import { PolarAreaChart } from '@/components/charts/polar-area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const marketData = [ + { label: 'Mobile Apps', value: 45, color: '#FF6B6B' }, + { label: 'Web Apps', value: 38, color: '#4ECDC4' }, + { label: 'Desktop', value: 25, color: '#45B7D1' }, + { label: 'IoT', value: 18, color: '#96CEB4' }, + { label: 'AI/ML', value: 32, color: '#FFEAA7' }, + { label: 'Blockchain', value: 15, color: '#DDA0DD' }, +]; + +export function PolarAreaChartStyled() { + return ( + + + + ); +} + +``` + +##### polar-area-chart-large + +A polar area chart with large dataset + +```tsx +import { PolarAreaChart } from '@/components/charts/polar-area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const largeDataset = [ + { label: 'Q1 Sales', value: 185 }, + { label: 'Q2 Sales', value: 220 }, + { label: 'Q3 Sales', value: 195 }, + { label: 'Q4 Sales', value: 240 }, + { label: 'Marketing', value: 156 }, + { label: 'Support', value: 134 }, + { label: 'Development', value: 189 }, + { label: 'Design', value: 123 }, + { label: 'HR', value: 98 }, + { label: 'Finance', value: 145 }, + { label: 'Operations', value: 167 }, + { label: 'Research', value: 112 }, +]; + +export function PolarAreaChartLarge() { + return ( + + + + ); +} + +``` +--- + +### popover + +A contextual overlay that displays rich content triggered by user interaction. + +**Installation:** +```bash +npx bna-ui add popover +``` + +**External Dependencies:** react-native-reanimated + +**Registry Dependencies:** button + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![popover preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 09-14-01_1.MP4) + +#### Basic Usage + +```tsx +import { Popover } from '@/components/ui/popover'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### popover-demo + +A basic popover with trigger button and content + +```tsx +import { Button } from '@/components/ui/button'; +import { + Popover, + PopoverBody, + PopoverClose, + PopoverContent, + PopoverFooter, + PopoverHeader, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Text } from '@/components/ui/text'; +import React from 'react'; + +export function PopoverDemo() { + return ( + + + + + + + Popover Title + + + + This is the popover content. You can put any content here. + + + + + + + + + + ); +} + +``` + +##### popover-positioning + +Popovers positioned on different sides of the trigger + +```tsx +import { Button } from '@/components/ui/button'; +import { + Popover, + PopoverBody, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function PopoverPositioning() { + return ( + + + + + + + + Popover content positioned on top + + + + + + + + + + + + Left positioned + + + + + + + + + + + Right positioned + + + + + + + + + + + + Popover content positioned on bottom + + + + + ); +} + +``` + +##### popover-alignment + +Popovers with different alignment options + +```tsx +import { Button } from '@/components/ui/button'; +import { + Popover, + PopoverBody, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function PopoverAlignment() { + return ( + + + Bottom Side Alignment + + + + + + + + Aligned to start (left) + + + + + + + + + + + Aligned to center + + + + + + + + + + + Aligned to end (right) + + + + + + + + Right Side Alignment + + + + + + + + Aligned to start (top) + + + + + + + + + + + Aligned to center + + + + + + + + + + + Aligned to end (bottom) + + + + + + + ); +} + +``` + +##### popover-controlled + +A controlled popover with external state management + +```tsx +import { Button } from '@/components/ui/button'; +import { + Popover, + PopoverBody, + PopoverContent, + PopoverHeader, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function PopoverControlled() { + const [isOpen, setIsOpen] = useState(false); + + return ( + + + + + + + Status: {isOpen ? 'Open' : 'Closed'} + + + + + + + + Controlled Popover + + + + This popover's state is controlled externally. You can open and + close it using the buttons above or by clicking the trigger. + + + + + + ); +} + +``` + +##### popover-custom + +Popovers with custom content and styling + +```tsx +import { Button } from '@/components/ui/button'; +import { + Popover, + PopoverBody, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +export function PopoverCustom() { + const primaryColor = useThemeColor({}, 'primary'); + const mutedColor = useThemeColor({}, 'muted'); + + return ( + + + + + + + + + This popover has custom styling with primary color background + + + + + + + + + + + + + Large Popover Content + + + This popover has custom dimensions and can hold more content. It + demonstrates how you can customize the appearance and size of + popover components to fit your design needs. + + + The content area is scrollable if it exceeds the maximum height. + + + + + + + + + + + + + This is a help tooltip using a custom circular trigger button + + + + + + ); +} + +``` + +##### popover-form + +A popover containing form elements + +```tsx +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { + Popover, + PopoverBody, + PopoverClose, + PopoverContent, + PopoverFooter, + PopoverHeader, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function PopoverForm() { + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + + const handleSubmit = () => { + // Handle form submission + console.log('Form submitted:', { name, email }); + // Reset form + setName(''); + setEmail(''); + }; + + return ( + + + + + + + Add New Contact + + + + + Name + + + + Email + + + + + + + + + + + + + + + ); +} + +``` + +##### popover-menu + +A popover styled as a dropdown menu + +```tsx +import { Button } from '@/components/ui/button'; +import { + Popover, + PopoverClose, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; +import { TouchableOpacity } from 'react-native'; + +interface MenuItemProps { + icon: string; + label: string; + onPress: () => void; + destructive?: boolean; +} + +function MenuItem({ + icon, + label, + onPress, + destructive = false, +}: MenuItemProps) { + const textColor = useThemeColor( + {}, + destructive ? 'destructive' : 'foreground' + ); + const mutedColor = useThemeColor({}, 'muted'); + + return ( + + + + {icon} + + {label} + + + ); +} + +export function PopoverMenu() { + const borderColor = useThemeColor({}, 'border'); + + const handleMenuAction = (action: string) => { + console.log(`Menu action: ${action}`); + }; + + return ( + + + + + + + handleMenuAction('profile')} + /> + handleMenuAction('settings')} + /> + handleMenuAction('export')} + /> + + handleMenuAction('signout')} + /> + handleMenuAction('delete')} + destructive + /> + + + + + + + + + handleMenuAction('edit')} + /> + handleMenuAction('copy')} + /> + handleMenuAction('share')} + /> + handleMenuAction('favorite')} + /> + + + + ); +} + +``` +--- + +### progress + +A progress bar component to show completion status with optional interactivity. + +**Installation:** +```bash +npx bna-ui add progress +``` + +**External Dependencies:** react-native-gesture-handler, react-native-reanimated + +**Registry Dependencies:** view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![progress preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/IMG_5657.PNG) + +#### Basic Usage + +```tsx +import { Progress } from '@/components/ui/progress'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### progress-demo + +A basic progress bar showing completion status + +```tsx +import { Progress } from '@/components/ui/progress'; +import React from 'react'; + +export function ProgressDemo() { + return ; +} + +``` + +##### progress-interactive + +An interactive progress bar that can be dragged or tapped + +```tsx +import { Progress } from '@/components/ui/progress'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function ProgressInteractive() { + const [value, setValue] = useState(45); + const [isSeking, setIsSeeking] = useState(false); + + return ( + + + {isSeking ? 'Seeking...' : `Progress: ${Math.round(value)}%`} + + setIsSeeking(true)} + onSeekEnd={() => setIsSeeking(false)} + /> + + Tap or drag to adjust the progress + + + ); +} + +``` + +##### progress-heights + +Progress bars with different heights + +```tsx +import { Progress } from '@/components/ui/progress'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ProgressHeights() { + return ( + + + Small (2px) + + + + + Default (4px) + + + + + Medium (8px) + + + + + Large (12px) + + + + + Extra Large (20px) + + + + ); +} + +``` + +##### progress-labels + +Progress bars with percentage labels and descriptions + +```tsx +import { Progress } from '@/components/ui/progress'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ProgressLabels() { + const tasks = [ + { label: 'Installing dependencies', progress: 100 }, + { label: 'Building application', progress: 75 }, + { label: 'Running tests', progress: 45 }, + { label: 'Deploying to production', progress: 0 }, + ]; + + return ( + + {tasks.map((task, index) => ( + + + + {task.label} + + + {task.progress}% + + + + + ))} + + + + + Overall Progress + + + 55% + + + + + 2 of 4 tasks completed + + + + ); +} + +``` + +##### progress-animated + +Progress bars with smooth animations and transitions + +```tsx +import { Progress } from '@/components/ui/progress'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useEffect, useState } from 'react'; + +export function ProgressAnimated() { + const [progress1, setProgress1] = useState(0); + const [progress2, setProgress2] = useState(0); + const [progress3, setProgress3] = useState(0); + + useEffect(() => { + // Animate first progress bar + const timer1 = setTimeout(() => setProgress1(75), 500); + + // Animate second progress bar + const timer2 = setTimeout(() => setProgress2(60), 1000); + + // Animate third progress bar + const timer3 = setTimeout(() => setProgress3(85), 1500); + + return () => { + clearTimeout(timer1); + clearTimeout(timer2); + clearTimeout(timer3); + }; + }, []); + + const [cycleProgress, setCycleProgress] = useState(0); + + useEffect(() => { + const interval = setInterval(() => { + setCycleProgress((prev) => { + const newValue = prev + 10; + return newValue > 100 ? 0 : newValue; + }); + }, 300); + + return () => clearInterval(interval); + }, []); + + return ( + + + Staggered Animation + + + File Upload: {progress1}% + + + + + Processing: {progress2}% + + + + + Optimization: {progress3}% + + + + + + Continuous Animation + + Loading: {cycleProgress}% + + + + + ); +} + +``` + +##### progress-media + +Progress bars styled for media player controls + +```tsx +import { Progress } from '@/components/ui/progress'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; +import { TouchableOpacity } from 'react-native'; + +export function ProgressMedia() { + const [progress, setProgress] = useState(35); + const [isPlaying, setIsPlaying] = useState(false); + const [volume, setVolume] = useState(75); + + const formatTime = (percent: number) => { + const totalSeconds = Math.floor((percent / 100) * 180); // 3 minute song + const minutes = Math.floor(totalSeconds / 60); + const seconds = totalSeconds % 60; + return `${minutes}:${seconds.toString().padStart(2, '0')}`; + }; + + return ( + + {/* Media Player */} + + + + Song Title + + + Artist Name + + + + + + + + {formatTime(progress)} + + + 3:00 + + + + + + setIsPlaying(!isPlaying)} + > + + {isPlaying ? '⏸️' : '▶️'} + + + + + + {/* Volume Control */} + + + 🔊 + + + + + {Math.round(volume)}% + + + + + ); +} + +``` + +##### progress-steps + +Multi-step progress indicators + +```tsx +import { Progress } from '@/components/ui/progress'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; +import { TouchableOpacity } from 'react-native'; + +export function ProgressSteps() { + const [currentStep, setCurrentStep] = useState(2); + + const steps = ['Account Setup', 'Personal Info', 'Verification', 'Complete']; + + const progress = (currentStep / (steps.length - 1)) * 100; + + return ( + + {/* Step Progress */} + + + Setup Progress + + Step {currentStep + 1} of {steps.length} + + + + + + + {steps.map((step, index) => ( + + + + {index < currentStep ? '✓' : index + 1} + + + + {step} + + + ))} + + + + {/* Controls */} + + 0 ? '#007AFF' : '#e5e7eb', + borderRadius: 6, + }} + onPress={() => setCurrentStep(Math.max(0, currentStep - 1))} + disabled={currentStep === 0} + > + 0 ? '#fff' : '#999', + fontWeight: '500', + }} + > + Previous + + + + + setCurrentStep(Math.min(steps.length - 1, currentStep + 1)) + } + disabled={currentStep === steps.length - 1} + > + + Next + + + + + ); +} + +``` + +##### progress-ring-chart-demo + +A circular progress ring with smooth animations + +```tsx +import { ProgressRingChart } from '@/components/charts/progress-ring-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +export function ProgressRingChartDemo() { + return ( + + + + ); +} + +``` + +##### progress-ring-chart-sample + +A sample progress ring chart + +```tsx +import { ProgressRingChart } from '@/components/charts/progress-ring-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +export function ProgressRingChartSample() { + return ( + + + + ); +} + +``` + +##### progress-ring-chart-styled + +A customized progress ring with gradient and custom styling + +```tsx +import { ProgressRingChart } from '@/components/charts/progress-ring-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +export function ProgressRingChartStyled() { + return ( + + + + ); +} + +``` + +##### progress-ring-chart-large + +A large progress ring with center text + +```tsx +import { ProgressRingChart } from '@/components/charts/progress-ring-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +export function ProgressRingChartLarge() { + return ( + + + + ); +} + +``` +--- + +### progress-ring-chart + +A customizable circular progress ring component with smooth animations and flexible styling. + +**Installation:** +```bash +npx bna-ui add progress-ring-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![progress-ring-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 36.MOV) + +#### Basic Usage + +```tsx +import { Progress-ring-chart } from '@/components/ui/progress-ring-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### progress-ring-chart-demo + +A circular progress ring with smooth animations + +```tsx +import { ProgressRingChart } from '@/components/charts/progress-ring-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +export function ProgressRingChartDemo() { + return ( + + + + ); +} + +``` + +##### progress-ring-chart-sample + +A sample progress ring chart + +```tsx +import { ProgressRingChart } from '@/components/charts/progress-ring-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +export function ProgressRingChartSample() { + return ( + + + + ); +} + +``` + +##### progress-ring-chart-styled + +A customized progress ring with gradient and custom styling + +```tsx +import { ProgressRingChart } from '@/components/charts/progress-ring-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +export function ProgressRingChartStyled() { + return ( + + + + ); +} + +``` + +##### progress-ring-chart-large + +A large progress ring with center text + +```tsx +import { ProgressRingChart } from '@/components/charts/progress-ring-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +export function ProgressRingChartLarge() { + return ( + + + + ); +} + +``` +--- + +### radar-chart + +A customizable radar chart component with smooth animations and flexible styling for displaying multi-dimensional data. + +**Installation:** +```bash +npx bna-ui add radar-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![radar-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 32.MOV) + +#### Basic Usage + +```tsx +import { Radar-chart } from '@/components/ui/radar-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### radar-chart-demo + +A radar chart with smooth animations + +```tsx +import { RadarChart } from '@/components/charts/radar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Speed', value: 80 }, + { label: 'Reliability', value: 92 }, + { label: 'Comfort', value: 75 }, + { label: 'Safety', value: 88 }, + { label: 'Efficiency', value: 85 }, + { label: 'Style', value: 70 }, +]; + +export function RadarChartDemo() { + return ( + + + + ); +} + +``` + +##### radar-chart-sample + +A sample radar chart + +```tsx +import { RadarChart } from '@/components/charts/radar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const skillsData = [ + { label: 'Frontend', value: 95 }, + { label: 'Backend', value: 82 }, + { label: 'Mobile', value: 78 }, + { label: 'DevOps', value: 65 }, + { label: 'Design', value: 70 }, +]; + +export function RadarChartSample() { + return ( + + + + ); +} + +``` + +##### radar-chart-styled + +A customized radar chart with custom colors and styling + +```tsx +import { RadarChart } from '@/components/charts/radar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const performanceData = [ + { label: 'Innovation', value: 88 }, + { label: 'Quality', value: 92 }, + { label: 'Delivery', value: 85 }, + { label: 'Customer Satisfaction', value: 90 }, + { label: 'Cost Efficiency', value: 78 }, + { label: 'Team Collaboration', value: 95 }, + { label: 'Process Improvement', value: 82 }, +]; + +export function RadarChartStyled() { + const accentColor = useThemeColor({}, 'accent'); + + return ( + + + + ); +} + +``` + +##### radar-chart-large + +A radar chart with large dataset + +```tsx +import { RadarChart } from '@/components/charts/radar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const comprehensiveData = [ + { label: 'Leadership', value: 85 }, + { label: 'Communication', value: 90 }, + { label: 'Technical Skills', value: 88 }, + { label: 'Problem Solving', value: 92 }, + { label: 'Creativity', value: 78 }, + { label: 'Adaptability', value: 86 }, + { label: 'Time Management', value: 82 }, + { label: 'Teamwork', value: 94 }, + { label: 'Strategic Thinking', value: 80 }, + { label: 'Customer Focus', value: 87 }, +]; + +export function RadarChartLarge() { + return ( + + + + ); +} + +``` +--- + +### radial-bar-chart + +A customizable radial bar chart component with smooth animations, gradient support, and center value display. + +**Installation:** +```bash +npx bna-ui add radial-bar-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![radial-bar-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 61.MOV) + +#### Basic Usage + +```tsx +import { Radial-bar-chart } from '@/components/ui/radial-bar-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### radial-bar-chart-demo + +A radial bar chart with smooth animations and center totals + +```tsx +import { RadialBarChart } from '@/components/charts/radial-bar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Sales', value: 120 }, + { label: 'Marketing', value: 98 }, + { label: 'Support', value: 86 }, + { label: 'Development', value: 140 }, + { label: 'Design', value: 75 }, +]; + +export function RadialBarChartDemo() { + return ( + + + + ); +} + +``` + +##### radial-bar-chart-sample + +A sample radial bar chart with custom data + +```tsx +import { RadialBarChart } from '@/components/charts/radial-bar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const sampleData = [ + { label: 'Mobile', value: 45 }, + { label: 'Desktop', value: 38 }, + { label: 'Tablet', value: 17 }, +]; + +export function RadialBarChartSample() { + const blue = useThemeColor({}, 'blue'); + const green = useThemeColor({}, 'green'); + const orange = useThemeColor({}, 'orange'); + + const dataWithColors = sampleData.map((item, index) => ({ + ...item, + color: [blue, green, orange][index], + })); + + return ( + + + + ); +} + +``` + +##### radial-bar-chart-gradient + +A radial bar chart with gradient effects + +```tsx +import { RadialBarChart } from '@/components/charts/radial-bar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const sampleData = [ + { label: 'Q1 Revenue', value: 85 }, + { label: 'Q2 Revenue', value: 92 }, + { label: 'Q3 Revenue', value: 78 }, + { label: 'Q4 Revenue', value: 96 }, +]; + +export function RadialBarChartGradient() { + const purple = useThemeColor({}, 'purple'); + const pink = useThemeColor({}, 'pink'); + const blue = useThemeColor({}, 'blue'); + const green = useThemeColor({}, 'green'); + + const dataWithColors = sampleData.map((item, index) => ({ + ...item, + color: [purple, pink, blue, green][index], + })); + + return ( + + + + ); +} + +``` + +##### radial-bar-chart-large + +A radial bar chart with large dataset + +```tsx +import { RadialBarChart } from '@/components/charts/radial-bar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const largeDataset = [ + { label: 'Product A', value: 156 }, + { label: 'Product B', value: 142 }, + { label: 'Product C', value: 98 }, + { label: 'Product D', value: 124 }, + { label: 'Product E', value: 89 }, + { label: 'Product F', value: 167 }, + { label: 'Product G', value: 78 }, + { label: 'Product H', value: 134 }, +]; + +export function RadialBarChartLarge() { + return ( + + + + ); +} + +``` +--- + +### radio + +A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time. + +**Installation:** +```bash +npx bna-ui add radio +``` + +**External Dependencies:** react-native + +**Registry Dependencies:** text + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![radio preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 09-42-48_1.MP4) + +#### Basic Usage + +```tsx +import { Radio } from '@/components/ui/radio'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### radio-demo + +A basic radio group with multiple options + +```tsx +import { RadioGroup } from '@/components/ui/radio'; +import React, { useState } from 'react'; + +export function RadioDemo() { + const [value, setValue] = useState('option1'); + + return ( + + ); +} + +``` + +##### radio-horizontal + +Radio buttons arranged horizontally + +```tsx +import { RadioGroup } from '@/components/ui/radio'; +import React, { useState } from 'react'; + +export function RadioHorizontal() { + const [value, setValue] = useState('small'); + + return ( + + ); +} + +``` + +##### radio-disabled + +Radio group with some disabled options + +```tsx +import { RadioGroup } from '@/components/ui/radio'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function RadioDisabled() { + const [value1, setValue1] = useState('option1'); + const [value2, setValue2] = useState('option2'); + + return ( + + {/* Some disabled options */} + + + With disabled options + + + + + {/* Entire group disabled */} + + + Entire group disabled + + + + + ); +} + +``` + +##### radio-styled + +Radio buttons with custom colors and styling + +```tsx +import { RadioGroup } from '@/components/ui/radio'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; + +export function RadioStyled() { + const green = useThemeColor({}, 'green'); + const card = useThemeColor({}, 'card'); + + const [value1, setValue1] = useState('red'); + const [value2, setValue2] = useState('plan1'); + + return ( + + {/* Custom colors */} + + + Card-like options + + + + + {/* Card-like styling */} + + + Custom styling + + + + + ); +} + +``` + +##### radio-form + +Radio group integrated with form validation + +```tsx +import { Button } from '@/components/ui/button'; +import { RadioGroup } from '@/components/ui/radio'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; +import { Alert } from 'react-native'; + +export function RadioForm() { + const [experience, setExperience] = useState(''); + const [notification, setNotification] = useState('email'); + const [theme, setTheme] = useState('system'); + + const handleSubmit = () => { + if (!experience) { + Alert.alert('Error', 'Please select your experience level'); + return; + } + + Alert.alert( + 'Form Submitted', + `Experience: ${experience}\nNotifications: ${notification}\nTheme: ${theme}` + ); + }; + + return ( + + User Preferences + + + + Experience Level * + + + + + + + Notification Preference + + + + + + + Theme Preference + + + + + + + ); +} + +``` + +##### radio-large + +Radio buttons with larger size and spacing + +```tsx +import { RadioGroup } from '@/components/ui/radio'; +import React, { useState } from 'react'; + +export function RadioLarge() { + const [value, setValue] = useState('option1'); + + return ( + + ); +} + +``` + +##### radio-single + +Individual radio button component usage + +```tsx +import { RadioButton } from '@/components/ui/radio'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function RadioSingle() { + const [selectedValue, setSelectedValue] = useState('option2'); + + const options = [ + { label: 'First Option', value: 'option1' }, + { label: 'Second Option', value: 'option2' }, + { label: 'Third Option', value: 'option3' }, + { label: 'Disabled Option', value: 'option4', disabled: true }, + ]; + + return ( + + + Individual Radio Buttons + + + + {options.map((option) => ( + setSelectedValue(option.value)} + /> + ))} + + + Selected: {selectedValue} + + ); +} + +``` +--- + +### scatter-chart + +A customizable scatter plot component with smooth animations and flexible styling for visualizing data relationships. + +**Installation:** +```bash +npx bna-ui add scatter-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![scatter-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 48.MOV) + +#### Basic Usage + +```tsx +import { Scatter-chart } from '@/components/ui/scatter-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### scatter-chart-demo + +A scatter plot with smooth animations + +```tsx +import { ScatterPlot } from '@/components/charts/scatter-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { x: 10, y: 20, label: 'Point A' }, + { x: 25, y: 35, label: 'Point B' }, + { x: 40, y: 15, label: 'Point C' }, + { x: 55, y: 45, label: 'Point D' }, + { x: 70, y: 30, label: 'Point E' }, + { x: 85, y: 55, label: 'Point F' }, + { x: 30, y: 50, label: 'Point G' }, + { x: 65, y: 25, label: 'Point H' }, +]; + +export function ScatterChartDemo() { + return ( + + + + ); +} + +``` + +##### scatter-chart-sample + +A sample scatter chart with various data points + +```tsx +import { ScatterPlot } from '@/components/charts/scatter-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { x: 5, y: 12, label: 'Alpha' }, + { x: 15, y: 28, label: 'Beta' }, + { x: 35, y: 42, label: 'Gamma' }, + { x: 45, y: 18, label: 'Delta' }, + { x: 25, y: 65, label: 'Epsilon' }, + { x: 55, y: 38, label: 'Zeta' }, + { x: 75, y: 52, label: 'Eta' }, + { x: 65, y: 78, label: 'Theta' }, + { x: 85, y: 25, label: 'Iota' }, + { x: 95, y: 88, label: 'Kappa' }, +]; + +export function ScatterChartSample() { + return ( + + + + ); +} + +``` + +##### scatter-chart-styled + +A customized scatter chart with custom colors and styling + +```tsx +import { ScatterPlot } from '@/components/charts/scatter-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React from 'react'; + +const sampleData = [ + { x: 20, y: 80, label: 'High Performance' }, + { x: 35, y: 65, label: 'Good Performance' }, + { x: 50, y: 70, label: 'Average Performance' }, + { x: 65, y: 45, label: 'Below Average' }, + { x: 80, y: 55, label: 'Improving' }, + { x: 25, y: 90, label: 'Excellent' }, + { x: 75, y: 35, label: 'Needs Work' }, + { x: 60, y: 85, label: 'Outstanding' }, +]; + +export function ScatterChartStyled() { + return ( + + + + ); +} + +``` + +##### scatter-chart-large + +A scatter chart with large dataset + +```tsx +import { ScatterPlot } from '@/components/charts/scatter-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +// Generate a larger dataset for demonstration +const generateLargeDataset = () => { + const data = []; + for (let i = 0; i < 30; i++) { + data.push({ + x: Math.random() * 100, + y: Math.random() * 100, + label: `Point ${i + 1}`, + }); + } + return data; +}; + +const largeDataset = generateLargeDataset(); + +export function ScatterChartLarge() { + return ( + + + + ); +} + +``` +--- + +### scroll-view + +A scrollable view component that allows content to be scrolled when it exceeds the container size. + +**Installation:** +```bash +npx bna-ui add scroll-view +``` + +**Registry Dependencies:** view + +**Preview:** + +![scroll-view preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 09-49-52_1.MP4) + +#### Basic Usage + +```tsx +import { Scroll-view } from '@/components/ui/scroll-view'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### scroll-view-demo + +A basic scrollable view with content + +```tsx +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function ScrollViewDemo() { + const card = useThemeColor({}, 'card'); + + return ( + + + {Array.from({ length: 20 }, (_, i) => ( + + Scrollable item {i + 1} + + ))} + + + ); +} + +``` + +##### scroll-view-vertical + +Vertical scrolling with multiple items + +```tsx +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function ScrollViewVertical() { + const colors = [ + '#ef4444', + '#f97316', + '#eab308', + '#22c55e', + '#3b82f6', + '#8b5cf6', + '#ec4899', + ]; + + return ( + + + {Array.from({ length: 15 }, (_, i) => ( + + + Card {i + 1} + + + ))} + + + ); +} + +``` + +##### scroll-view-horizontal + +Horizontal scrolling with cards + +```tsx +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function ScrollViewHorizontal() { + const gradients = [ + ['#ff9a9e', '#fecfef'], + ['#a18cd1', '#fbc2eb'], + ['#fad0c4', '#ffd1ff'], + ['#ffecd2', '#fcb69f'], + ['#a8edea', '#fed6e3'], + ['#d299c2', '#fef9d7'], + ['#89f7fe', '#66a6ff'], + ]; + + return ( + + + {Array.from({ length: 10 }, (_, i) => ( + + + Item {i + 1} + + + ))} + + + ); +} + +``` + +##### scroll-view-nested + +ScrollViews nested within each other + +```tsx +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function ScrollViewNested() { + return ( + + + + Vertical Scroll + + + {Array.from({ length: 3 }, (_, sectionIndex) => ( + + + Section {sectionIndex + 1} + + + + + {Array.from({ length: 8 }, (_, itemIndex) => ( + + + {sectionIndex + 1}.{itemIndex + 1} + + + ))} + + + + ))} + + + End of scrollable content + + + + ); +} + +``` + +##### scroll-view-refresh + +ScrollView with pull-to-refresh functionality + +```tsx +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React, { useCallback, useState } from 'react'; +import { RefreshControl } from 'react-native'; + +export function ScrollViewRefresh() { + const card = useThemeColor({}, 'card'); + const green = useThemeColor({}, 'green'); + + const [refreshing, setRefreshing] = useState(false); + const [lastRefresh, setLastRefresh] = useState( + new Date().toLocaleTimeString() + ); + + const onRefresh = useCallback(() => { + setRefreshing(true); + setTimeout(() => { + setRefreshing(false); + setLastRefresh(new Date().toLocaleTimeString()); + }, 2000); + }, []); + + return ( + + + } + > + + + Pull to Refresh + + + Last refreshed: {lastRefresh} + + + + {Array.from({ length: 15 }, (_, i) => ( + + + News Item {i + 1} + + + This is a sample news item that demonstrates the pull-to-refresh + functionality. + + + ))} + + + ); +} + +``` + +##### scroll-view-styled + +ScrollView with custom styling and padding + +```tsx +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ScrollViewStyled() { + return ( + + + + + 🌙 Dark Theme ScrollView + + + This ScrollView uses custom dark styling with rounded corners and + shadows. + + + + {Array.from({ length: 12 }, (_, i) => ( + + + Card {i + 1} + + + Beautiful custom styled card with gradient-like colors and + shadows. + + + ))} + + + + ✨ End of styled content + + + + + ); +} + +``` + +##### scroll-view-indicators + +ScrollView with custom scroll indicators + +```tsx +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function ScrollViewIndicators() { + const card = useThemeColor({}, 'card'); + + return ( + + {/* Vertical with indicators */} + + + With Scroll Indicators + + + + {Array.from({ length: 12 }, (_, i) => ( + + Item {i + 1} - Scroll indicators visible + + ))} + + + + + {/* Horizontal without indicators */} + + + Without Scroll Indicators + + + + {Array.from({ length: 8 }, (_, i) => ( + + + {i + 1} + + + ))} + + + + + ); +} + +``` + +##### scroll-view-inset + +ScrollView with content inset adjustments + +```tsx +import { ScrollView } from '@/components/ui/scroll-view'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function ScrollViewInset() { + const card = useThemeColor({}, 'card'); + + return ( + + {/* Standard ScrollView */} + + + Standard Content + + + + {Array.from({ length: 8 }, (_, i) => ( + + Standard item {i + 1} + + ))} + + + + + {/* ScrollView with content inset */} + + + With Content Inset Adjustments + + + + + + Header with Inset + + + + {Array.from({ length: 6 }, (_, i) => ( + + Inset adjusted item {i + 1} + + ))} + + + + Footer with Inset + + + + + + + ); +} + +``` +--- + +### searchbar + +A customizable search input with debouncing, loading states, and suggestions. + +**Installation:** +```bash +npx bna-ui add searchbar +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** icon, text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![searchbar preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 10-22-37_1.MP4) + +#### Basic Usage + +```tsx +import { Searchbar } from '@/components/ui/searchbar'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### searchbar-demo + +A basic search bar with search functionality + +```tsx +import { SearchBar } from '@/components/ui/searchbar'; +import React, { useState } from 'react'; + +export function SearchBarDemo() { + const [searchQuery, setSearchQuery] = useState(''); + + const handleSearch = (query: string) => { + console.log('Searching for:', query); + }; + + return ( + + ); +} + +``` + +##### searchbar-loading + +Search bar with loading indicator + +```tsx +import { SearchBar } from '@/components/ui/searchbar'; +import React, { useState } from 'react'; + +export function SearchBarLoading() { + const [searchQuery, setSearchQuery] = useState(''); + const [loading, setLoading] = useState(false); + + const handleSearch = (query: string) => { + if (query.trim()) { + setLoading(true); + // Simulate API call + setTimeout(() => { + setLoading(false); + console.log('Search completed for:', query); + }, 2000); + } + }; + + return ( + + ); +} + +``` + +##### searchbar-icons + +Search bar with custom left and right icons + +```tsx +import { Icon } from '@/components/ui/icon'; +import { SearchBar } from '@/components/ui/searchbar'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { Filter, MapPin, User } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function SearchBarIcons() { + const [locationQuery, setLocationQuery] = useState(''); + const [userQuery, setUserQuery] = useState(''); + const icon = useThemeColor({}, 'icon'); + + return ( + + {/* Location search with map pin icon */} + } + onSearch={(query) => console.log('Location search:', query)} + /> + + {/* User search with custom icons */} + } + rightIcon={} + showClearButton={false} + onSearch={(query) => console.log('User search:', query)} + /> + + ); +} + +``` + +##### searchbar-suggestions + +Search bar with dropdown suggestions + +```tsx +import { SearchBarWithSuggestions } from '@/components/ui/searchbar'; +import React, { useState } from 'react'; + +export function SearchBarSuggestions() { + const [searchQuery, setSearchQuery] = useState(''); + + const suggestions = [ + 'React Native', + 'React Navigation', + 'React Hook Form', + 'Redux Toolkit', + 'Expo Router', + 'TypeScript', + 'JavaScript', + 'Node.js', + 'Next.js', + 'Tailwind CSS', + ]; + + const handleSearch = (query: string) => { + console.log('Searching for:', query); + }; + + const handleSuggestionPress = (suggestion: string) => { + setSearchQuery(suggestion); + handleSearch(suggestion); + }; + + return ( + + ); +} + +``` + +##### searchbar-styled + +Search bar with custom styling and colors + +```tsx +import { SearchBar } from '@/components/ui/searchbar'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function SearchBarStyled() { + const [query1, setQuery1] = useState(''); + const [query2, setQuery2] = useState(''); + const [query3, setQuery3] = useState(''); + + return ( + + {/* Rounded with gradient-like background */} + + + {/* Minimal flat design */} + + + {/* Dark theme with custom height */} + + + ); +} + +``` + +##### searchbar-no-clear + +Search bar without the clear button + +```tsx +import { SearchBar } from '@/components/ui/searchbar'; +import React, { useState } from 'react'; + +export function SearchBarNoClear() { + const [searchQuery, setSearchQuery] = useState(''); + + const handleSearch = (query: string) => { + console.log('Searching without clear button:', query); + }; + + return ( + + ); +} + +``` + +##### searchbar-instant + +Search bar with no debounce for instant search + +```tsx +import { SearchBar } from '@/components/ui/searchbar'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function SearchBarInstant() { + const [searchQuery, setSearchQuery] = useState(''); + const [searchResults, setSearchResults] = useState([]); + + const mockData = [ + 'Apple', + 'Banana', + 'Cherry', + 'Date', + 'Elderberry', + 'Fig', + 'Grape', + 'Honeydew', + 'Kiwi', + 'Lemon', + ]; + + const handleInstantSearch = (query: string) => { + if (query.trim()) { + const results = mockData.filter((item) => + item.toLowerCase().includes(query.toLowerCase()) + ); + setSearchResults(results); + } else { + setSearchResults([]); + } + }; + + return ( + + + + {searchResults.length > 0 && ( + + + Found {searchResults.length} results: + + {searchResults.map((result, index) => ( + + • {result} + + ))} + + )} + + ); +} + +``` +--- + +### separator + +Visually or semantically separates content. + +**Installation:** +```bash +npx bna-ui add separator +``` + +**Registry Dependencies:** view + +**Required Hooks:** useThemeColor + +**Preview:** + +![separator preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/IMG_5781.PNG) + +#### Basic Usage + +```tsx +import { Separator } from '@/components/ui/separator'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### separator-demo + +A basic horizontal separator + +```tsx +import { Separator } from '@/components/ui/separator'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SeparatorDemo() { + return ( + + Above separator + + Below separator + + ); +} + +``` + +##### separator-vertical + +A vertical separator for inline content + +```tsx +import { Separator } from '@/components/ui/separator'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SeparatorVertical() { + return ( + + Left content + + Right content + + ); +} + +``` + +##### separator-thickness + +Separators with different thickness values + +```tsx +import { Separator } from '@/components/ui/separator'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SeparatorThickness() { + return ( + + + Thin (1px) + + + + + Medium (2px) + + + + + Thick (4px) + + + + + Extra thick (8px) + + + + ); +} + +``` + +##### separator-colors + +Separators with custom colors and opacity + +```tsx +import { Separator } from '@/components/ui/separator'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SeparatorColors() { + return ( + + + Default + + + + + Red + + + + + Blue + + + + + Green + + + + + Semi-transparent + + + + ); +} + +``` + +##### separator-spacing + +Separators with different margin and padding + +```tsx +import { Separator } from '@/components/ui/separator'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SeparatorSpacing() { + return ( + + Tight spacing + + Content with minimal spacing + + + + Normal spacing + + Standard content spacing + + + + Loose spacing + + Generous content spacing + + ); +} + +``` +--- + +### share + +A button component for sharing content across platforms with native share functionality. + +**Installation:** +```bash +npx bna-ui add share +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** button, text + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![share preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 10-26-52_1.MP4) + +#### Basic Usage + +```tsx +import { Share } from '@/components/ui/share'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### share-demo + +A basic share button with text and URL sharing + +```tsx +import { ShareButton } from '@/components/ui/share'; +import React from 'react'; + +export function ShareDemo() { + return ( + { + console.log('Shared successfully:', activityType); + }} + onShareError={(error) => { + console.error('Share failed:', error); + }} + > + Share + + ); +} + +``` + +##### share-variants + +Share buttons with different visual variants + +```tsx +import { ShareButton } from '@/components/ui/share'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ShareVariants() { + const shareContent = { + message: 'Check out this amazing content!', + url: 'https://example.com', + }; + + return ( + + + Default + + + + Secondary + + + + Outline + + + + Ghost + + + + Link + + + + Destructive + + + ); +} + +``` + +##### share-sizes + +Share buttons in different sizes + +```tsx +import { ShareButton } from '@/components/ui/share'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ShareSizes() { + const shareContent = { + message: 'Check out this amazing content!', + url: 'https://example.com', + }; + + return ( + + + Small + + + + Default + + + + Large + + + + + ); +} + +``` + +##### share-url-only + +Share button for sharing URLs without additional text + +```tsx +import { ShareButton } from '@/components/ui/share'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ShareUrlOnly() { + return ( + + + Share GitHub + + + + Share React Native Docs + + + + Share Expo + + + ); +} + +``` + +##### share-custom-content + +Share button with custom title, subject, and content + +```tsx +import { ShareButton } from '@/components/ui/share'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ShareCustomContent() { + return ( + + {/* Rich content with title and subject */} + + Share Article + + + {/* App promotion */} + + Share App + + + {/* Event invitation */} + + Share Event + + + ); +} + +``` + +##### share-icon-only + +Compact share button with icon only + +```tsx +import { ShareButton } from '@/components/ui/share'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ShareIconOnly() { + const shareContent = { + message: 'Check out this amazing content!', + url: 'https://example.com', + }; + + return ( + + + + + + + + + + ); +} + +``` + +##### share-callbacks + +Share button with success, error, and dismiss callbacks + +```tsx +import { ShareButton } from '@/components/ui/share'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function ShareCallbacks() { + const [status, setStatus] = useState('Ready to share'); + const [isLoading, setIsLoading] = useState(false); + + const handleShareStart = () => { + setStatus('Starting share...'); + setIsLoading(true); + }; + + const handleShareSuccess = (activityType?: string | null) => { + setStatus( + `Shared successfully${activityType ? ` via ${activityType}` : ''}!` + ); + setIsLoading(false); + + // Reset status after 3 seconds + setTimeout(() => setStatus('Ready to share'), 3000); + }; + + const handleShareError = (error: Error) => { + setStatus(`Share failed: ${error.message}`); + setIsLoading(false); + + // Reset status after 3 seconds + setTimeout(() => setStatus('Ready to share'), 3000); + }; + + const handleShareDismiss = () => { + setStatus('Share cancelled'); + setIsLoading(false); + + // Reset status after 2 seconds + setTimeout(() => setStatus('Ready to share'), 2000); + }; + + return ( + + Status: {status} + + + Share with Callbacks + + + ); +} + +``` + +##### share-hook + +Using the useShare hook for programmatic sharing + +```tsx +import { Button } from '@/components/ui/button'; +import { useShare } from '@/components/ui/share'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function ShareHook() { + const { shareText, shareUrl, shareContent } = useShare(); + const [status, setStatus] = useState('Choose a sharing method'); + + const handleShareText = async () => { + try { + setStatus('Sharing text...'); + await shareText( + 'Hello from the useShare hook! This is just a plain text message.' + ); + setStatus('Text shared successfully!'); + } catch (error) { + setStatus(`Failed to share text: ${(error as Error).message}`); + } + }; + + const handleShareUrl = async () => { + try { + setStatus('Sharing URL...'); + await shareUrl( + 'https://reactnative.dev', + 'Check out the official React Native documentation!' + ); + setStatus('URL shared successfully!'); + } catch (error) { + setStatus(`Failed to share URL: ${(error as Error).message}`); + } + }; + + const handleShareContent = async () => { + try { + setStatus('Sharing content...'); + await shareContent({ + message: + '🚀 Just built an amazing React Native app with this component library!', + url: 'https://github.com/example/ui-library', + title: 'Amazing UI Library', + subject: 'Check out this UI library', + }); + setStatus('Content shared successfully!'); + } catch (error) { + setStatus(`Failed to share content: ${(error as Error).message}`); + } + }; + + return ( + + {status} + + + + + + + + + + ); +} + +``` +--- + +### sheet + +A modal component that slides in from the side of the screen, commonly used for navigation menus, filters, and detail views. + +**Installation:** +```bash +npx bna-ui add sheet +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** button, text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![sheet preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 10-31-38_1.MP4) + +#### Basic Usage + +```tsx +import { Sheet } from '@/components/ui/sheet'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### sheet-demo + +A basic sheet that slides in from the right side + +```tsx +import { Button } from '@/components/ui/button'; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from '@/components/ui/sheet'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function SheetDemo() { + const [open, setOpen] = useState(false); + + return ( + + + + + + + Welcome to the Sheet + + This is a basic sheet component that slides in from the right side + of the screen. + + + + + This sheet can contain any content you need. It's perfect for + navigation menus, forms, settings, or detailed information. + + + + + + ); +} + +``` + +##### sheet-left + +A sheet that slides in from the left side + +```tsx +import { Button } from '@/components/ui/button'; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from '@/components/ui/sheet'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function SheetLeft() { + const [open, setOpen] = useState(false); + + return ( + + + + + + + Left Side Sheet + + This sheet slides in from the left side of the screen. + + + + + Left-side sheets are commonly used for navigation menus and primary + actions that need to be easily accessible. + + + + + + ); +} + +``` + +##### sheet-navigation + +A sheet that slides in from the navigation side + +```tsx +import { Button } from '@/components/ui/button'; +import { Icon } from '@/components/ui/icon'; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from '@/components/ui/sheet'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { Bell, Home, Mail, Search, Settings, User } from 'lucide-react-native'; +import React, { useState } from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; + +export function SheetNavigation() { + const [open, setOpen] = useState(false); + const [activeItem, setActiveItem] = useState('home'); + + const textColor = useThemeColor({}, 'text'); + const mutedColor = useThemeColor({}, 'textMuted'); + const borderColor = useThemeColor({}, 'border'); + + const navigationItems = [ + { id: 'home', label: 'Home', icon: Home }, + { id: 'profile', label: 'Profile', icon: User }, + { id: 'messages', label: 'Messages', icon: Mail }, + { id: 'search', label: 'Search', icon: Search }, + { id: 'notifications', label: 'Notifications', icon: Bell }, + { id: 'settings', label: 'Settings', icon: Settings }, + ]; + + const handleItemPress = (itemId: string) => { + setActiveItem(itemId); + setOpen(false); + }; + + return ( + + + + + + + Navigation Menu + + Navigate to different sections of the app. + + + + {navigationItems.map((item) => { + const name = item.icon; + const isActive = activeItem === item.id; + + return ( + handleItemPress(item.id)} + > + + + {item.label} + + + ); + })} + + + + ); +} + +const styles = StyleSheet.create({ + navigationContainer: { + padding: 16, + gap: 8, + }, + navigationItem: { + flexDirection: 'row', + alignItems: 'center', + gap: 12, + padding: 12, + borderRadius: 8, + borderWidth: 1, + borderColor: 'transparent', + }, + navigationText: { + fontSize: 16, + fontWeight: '500', + }, +}); + +``` + +##### sheet-form + +A sheet that slides in from the form side + +```tsx +import { Button } from '@/components/ui/button'; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from '@/components/ui/sheet'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import React, { useState } from 'react'; +import { Alert, StyleSheet, TextInput } from 'react-native'; + +export function SheetForm() { + const [open, setOpen] = useState(false); + const [formData, setFormData] = useState({ + name: '', + email: '', + message: '', + }); + + const textColor = useThemeColor({}, 'text'); + const backgroundColor = useThemeColor({}, 'background'); + const borderColor = useThemeColor({}, 'border'); + const mutedColor = useThemeColor({}, 'textMuted'); + + const handleSubmit = () => { + if (!formData.name || !formData.email || !formData.message) { + Alert.alert('Error', 'Please fill in all fields'); + return; + } + + Alert.alert('Success', 'Form submitted successfully!'); + setFormData({ name: '', email: '', message: '' }); + setOpen(false); + }; + + const handleReset = () => { + setFormData({ name: '', email: '', message: '' }); + }; + + return ( + + + + + + + Contact Us + + Fill out the form below and we'll get back to you soon. + + + + + Name + + setFormData((prev) => ({ ...prev, name: text })) + } + placeholder='Enter your name' + placeholderTextColor={mutedColor} + /> + + + + Email + + setFormData((prev) => ({ ...prev, email: text })) + } + placeholder='Enter your email' + placeholderTextColor={mutedColor} + keyboardType='email-address' + autoCapitalize='none' + /> + + + + Message + + setFormData((prev) => ({ ...prev, message: text })) + } + placeholder='Enter your message' + placeholderTextColor={mutedColor} + multiline + numberOfLines={4} + textAlignVertical='top' + /> + + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + formContainer: { + padding: 24, + gap: 20, + }, + fieldContainer: { + gap: 8, + }, + label: { + fontSize: 16, + fontWeight: '500', + }, + input: { + borderWidth: 1, + borderRadius: 8, + padding: 12, + fontSize: 16, + }, + textArea: { + height: 100, + }, + buttonContainer: { + flexDirection: 'row', + gap: 12, + marginTop: 12, + }, + button: { + flex: 1, + }, +}); + +``` + +##### sheet-filter + +A sheet that slides in from the filter side + +```tsx +import { Button } from '@/components/ui/button'; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, + SheetTrigger, +} from '@/components/ui/sheet'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { Filter } from 'lucide-react-native'; +import React, { useState } from 'react'; +import { StyleSheet, TouchableOpacity } from 'react-native'; + +export function SheetFilter() { + const [open, setOpen] = useState(false); + const [filters, setFilters] = useState({ + category: 'all', + price: 'all', + rating: 'all', + brand: 'all', + }); + + const textColor = useThemeColor({}, 'text'); + const mutedColor = useThemeColor({}, 'textMuted'); + const borderColor = useThemeColor({}, 'border'); + + const filterOptions = { + category: [ + { value: 'all', label: 'All Categories' }, + { value: 'electronics', label: 'Electronics' }, + { value: 'clothing', label: 'Clothing' }, + { value: 'books', label: 'Books' }, + { value: 'home', label: 'Home & Garden' }, + ], + price: [ + { value: 'all', label: 'Any Price' }, + { value: 'under-25', label: 'Under $25' }, + { value: '25-50', label: '$25 - $50' }, + { value: '50-100', label: '$50 - $100' }, + { value: 'over-100', label: 'Over $100' }, + ], + rating: [ + { value: 'all', label: 'Any Rating' }, + { value: '4-plus', label: '4+ Stars' }, + { value: '3-plus', label: '3+ Stars' }, + { value: '2-plus', label: '2+ Stars' }, + ], + brand: [ + { value: 'all', label: 'All Brands' }, + { value: 'apple', label: 'Apple' }, + { value: 'samsung', label: 'Samsung' }, + { value: 'nike', label: 'Nike' }, + { value: 'adidas', label: 'Adidas' }, + ], + }; + + const handleFilterChange = ( + filterType: keyof typeof filters, + value: string + ) => { + setFilters((prev) => ({ ...prev, [filterType]: value })); + }; + + const handleApplyFilters = () => { + // Apply filters logic here + console.log('Applied filters:', filters); + setOpen(false); + }; + + const handleClearFilters = () => { + setFilters({ + category: 'all', + price: 'all', + rating: 'all', + brand: 'all', + }); + }; + + const renderFilterSection = ( + title: string, + filterType: keyof typeof filters, + options: { value: string; label: string }[] + ) => ( + + {title} + + {options.map((option) => ( + handleFilterChange(filterType, option.value)} + > + + {option.label} + + + ))} + + + ); + + return ( + + + + + + + Filter Products + + Refine your search results using the filters below. + + + + {renderFilterSection('Category', 'category', filterOptions.category)} + {renderFilterSection('Price Range', 'price', filterOptions.price)} + {renderFilterSection('Rating', 'rating', filterOptions.rating)} + {renderFilterSection('Brand', 'brand', filterOptions.brand)} + + + + + + + + + ); +} + +const styles = StyleSheet.create({ + filterContainer: { + padding: 16, + gap: 24, + }, + filterSection: { + gap: 12, + }, + sectionTitle: { + fontSize: 18, + fontWeight: '600', + }, + optionsContainer: { + gap: 8, + }, + option: { + padding: 12, + borderRadius: 8, + borderWidth: 1, + }, + optionText: { + fontSize: 16, + }, + buttonContainer: { + flexDirection: 'row', + gap: 12, + marginTop: 12, + }, + button: { + flex: 1, + }, +}); + +``` +--- + +### skeleton + +A placeholder component to show a loading state while content is being fetched. + +**Installation:** +```bash +npx bna-ui add skeleton +``` + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![skeleton preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 10-39-47_1.MP4) + +#### Basic Usage + +```tsx +import { Skeleton } from '@/components/ui/skeleton'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### skeleton-demo + +A basic skeleton loader with pulsing animation + +```tsx +import { Skeleton } from '@/components/ui/skeleton'; +import React from 'react'; + +export function SkeletonDemo() { + return ; +} + +``` + +##### skeleton-sizes + +Skeletons in various sizes and dimensions + +```tsx +import { Skeleton } from '@/components/ui/skeleton'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SkeletonSizes() { + return ( + + + + + + + ); +} + +``` + +##### skeleton-card + +Skeleton placeholders arranged in a card layout + +```tsx +import { Skeleton } from '@/components/ui/skeleton'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function SkeletonCard() { + const card = useThemeColor({}, 'card'); + + return ( + + {/* Header */} + + + + + + + + + {/* Content */} + + + {/* Footer */} + + + + + + + ); +} + +``` + +##### skeleton-profile + +Skeleton layout mimicking a user profile + +```tsx +import { Skeleton } from '@/components/ui/skeleton'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function SkeletonProfile() { + const card = useThemeColor({}, 'card'); + + return ( + + {/* Profile Picture */} + + + {/* Name and Title */} + + + + + + {/* Stats */} + + + + + + + + + + + + + + + + {/* Bio */} + + + + + + + ); +} + +``` + +##### skeleton-list + +Multiple skeleton items arranged in a list + +```tsx +import { Skeleton } from '@/components/ui/skeleton'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function SkeletonList() { + const card = useThemeColor({}, 'card'); + + return ( + + {Array.from({ length: 5 }, (_, i) => ( + + + + + + + + + ))} + + ); +} + +``` + +##### skeleton-shapes + +Skeletons with custom shapes and styling + +```tsx +import { Skeleton } from '@/components/ui/skeleton'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React from 'react'; + +export function SkeletonShapes() { + const card = useThemeColor({}, 'card'); + + return ( + + {/* Circle */} + + + {/* Square */} + + + {/* Rounded Rectangle */} + + + {/* Pill */} + + + {/* Custom styled */} + + + ); +} + +``` +--- + +### spinner + +A loading indicator component with multiple variants and customization options. + +**Installation:** +```bash +npx bna-ui add spinner +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** text + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Types + +```typescript +export type SpinnerVariant = 'default' | 'cirlce' | 'dots' | 'pulse' | 'bars' +``` + +**Preview:** + +![spinner preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 10-46-27_1.MP4) + +#### Basic Usage + +```tsx +import { Spinner } from '@/components/ui/spinner'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### spinner-demo + +A basic spinner with default styling + +```tsx +import { Spinner } from '@/components/ui/spinner'; +import React from 'react'; + +export function SpinnerDemo() { + return ; +} + +``` + +##### spinner-variants + +Different spinner variants: default, circle, dots, pulse, and bars + +```tsx +import { Spinner } from '@/components/ui/spinner'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SpinnerVariants() { + const variants = [ + { variant: 'default' as const, label: 'Default' }, + { variant: 'cirlce' as const, label: 'Circle' }, + { variant: 'dots' as const, label: 'Dots' }, + { variant: 'pulse' as const, label: 'Pulse' }, + { variant: 'bars' as const, label: 'Bars' }, + ]; + + return ( + + {variants.map(({ variant, label }) => ( + + + + {label} + + + ))} + + ); +} + +``` + +##### spinner-sizes + +Spinners in different sizes: sm, default, lg, and icon + +```tsx +import { Spinner } from '@/components/ui/spinner'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SpinnerSizes() { + const sizes = [ + { size: 'sm' as const, label: 'Small' }, + { size: 'default' as const, label: 'Default' }, + { size: 'lg' as const, label: 'Large' }, + { size: 'icon' as const, label: 'Icon' }, + ]; + + return ( + + {sizes.map(({ size, label }) => ( + + + + {label} + + + ))} + + ); +} + +``` + +##### spinner-labels + +Spinners with custom loading labels + +```tsx +import { Spinner } from '@/components/ui/spinner'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SpinnerLabels() { + return ( + + + + + + + ); +} + +``` + +##### spinner-speeds + +Spinners with different animation speeds: slow, normal, and fast + +```tsx +import { Spinner } from '@/components/ui/spinner'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SpinnerSpeeds() { + const speeds = [ + { speed: 'slow' as const, label: 'Slow' }, + { speed: 'normal' as const, label: 'Normal' }, + { speed: 'fast' as const, label: 'Fast' }, + ]; + + return ( + + {speeds.map(({ speed, label }) => ( + + + + {label} + + + ))} + + ); +} + +``` + +##### spinner-colors + +Spinners with custom colors and styling + +```tsx +import { Spinner } from '@/components/ui/spinner'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SpinnerColors() { + const colors = [ + { color: '#3b82f6', label: 'Blue', variant: 'default' as const }, + { color: '#10b981', label: 'Green', variant: 'dots' as const }, + { color: '#f59e0b', label: 'Orange', variant: 'pulse' as const }, + { color: '#ef4444', label: 'Red', variant: 'bars' as const }, + { color: '#8b5cf6', label: 'Purple', variant: 'cirlce' as const }, + ]; + + return ( + + {colors.map(({ color, label, variant }) => ( + + + + {label} + + + ))} + + ); +} + +``` + +##### spinner-overlay + +Full-screen loading overlay with backdrop + +```tsx +import { Button } from '@/components/ui/button'; +import { LoadingOverlay } from '@/components/ui/spinner'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function SpinnerOverlay() { + const [showOverlay, setShowOverlay] = useState(false); + + const handleShowOverlay = () => { + setShowOverlay(true); + // Auto hide after 3 seconds for demo + setTimeout(() => setShowOverlay(false), 3000); + }; + + return ( + + + + + + ); +} + +``` + +##### spinner-inline + +Small spinners for inline usage in buttons or text + +```tsx +import { InlineLoader } from '@/components/ui/spinner'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function SpinnerInline() { + return ( + + + Loading data + + + + + Processing + + + + + + Syncing... + + + ); +} + +``` +--- + +### stacked-area-chart + +A customizable stacked area chart component with smooth animations and gradient fills for visualizing multiple data series over time. + +**Installation:** +```bash +npx bna-ui add stacked-area-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![stacked-area-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 20.MOV) + +#### Basic Usage + +```tsx +import { Stacked-area-chart } from '@/components/ui/stacked-area-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### stacked-area-chart-demo + +A stacked area chart with smooth animations and gradient fills + +```tsx +import { StackedAreaChart } from '@/components/charts/stacked-area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { x: 1, y: [20, 30, 25], label: 'Jan' }, + { x: 2, y: [25, 35, 30], label: 'Feb' }, + { x: 3, y: [30, 40, 35], label: 'Mar' }, + { x: 4, y: [35, 45, 40], label: 'Apr' }, + { x: 5, y: [40, 50, 45], label: 'May' }, + { x: 6, y: [45, 55, 50], label: 'Jun' }, +]; + +const categories = ['Product A', 'Product B', 'Product C']; + +export function StackedAreaChartDemo() { + return ( + + + + ); +} + +``` + +##### stacked-area-chart-sample + +A sample stacked area chart with revenue data + +```tsx +import { StackedAreaChart } from '@/components/charts/stacked-area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { x: 1, y: [45, 55, 35, 25], label: 'Q1' }, + { x: 2, y: [50, 60, 40, 30], label: 'Q2' }, + { x: 3, y: [55, 65, 45, 35], label: 'Q3' }, + { x: 4, y: [60, 70, 50, 40], label: 'Q4' }, + { x: 5, y: [65, 75, 55, 45], label: 'Q1' }, + { x: 6, y: [70, 80, 60, 50], label: 'Q2' }, +]; + +const categories = ['Direct Sales', 'Online', 'Retail', 'Partner']; + +export function StackedAreaChartSample() { + return ( + + + + ); +} + +``` + +##### stacked-area-chart-styled + +A customized stacked area chart with custom colors and styling + +```tsx +import { StackedAreaChart } from '@/components/charts/stacked-area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { x: 1, y: [120, 80, 60], label: 'Week 1' }, + { x: 2, y: [140, 90, 70], label: 'Week 2' }, + { x: 3, y: [160, 100, 80], label: 'Week 3' }, + { x: 4, y: [180, 110, 90], label: 'Week 4' }, + { x: 5, y: [200, 120, 100], label: 'Week 5' }, + { x: 6, y: [220, 130, 110], label: 'Week 6' }, + { x: 7, y: [240, 140, 120], label: 'Week 7' }, + { x: 8, y: [260, 150, 130], label: 'Week 8' }, +]; + +const categories = ['Premium', 'Standard', 'Basic']; + +export function StackedAreaChartStyled() { + return ( + + + + ); +} + +``` + +##### stacked-area-chart-large + +A stacked area chart with large dataset + +```tsx +import { StackedAreaChart } from '@/components/charts/stacked-area-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const generateLargeDataset = () => { + const data = []; + const months = [ + 'Jan', + 'Feb', + 'Mar', + 'Apr', + 'May', + 'Jun', + 'Jul', + 'Aug', + 'Sep', + 'Oct', + 'Nov', + 'Dec', + ]; + + for (let i = 0; i < 12; i++) { + data.push({ + x: i + 1, + y: [ + Math.floor(Math.random() * 50) + 100, // Desktop + Math.floor(Math.random() * 80) + 120, // Mobile + Math.floor(Math.random() * 40) + 60, // Tablet + Math.floor(Math.random() * 30) + 40, // TV + Math.floor(Math.random() * 20) + 20, // Watch + ], + label: months[i], + }); + } + + return data; +}; + +const sampleData = generateLargeDataset(); +const categories = ['Desktop', 'Mobile', 'Tablet', 'TV', 'Watch']; + +export function StackedAreaChartLarge() { + return ( + + + + ); +} + +``` +--- + +### stacked-bar-chart + +A customizable stacked bar chart component with smooth animations, support for both horizontal and vertical layouts, and flexible styling. + +**Installation:** +```bash +npx bna-ui add stacked-bar-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![stacked-bar-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-12-2025 28.MOV) + +#### Basic Usage + +```tsx +import { Stacked-bar-chart } from '@/components/ui/stacked-bar-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### stacked-bar-chart-demo + +A stacked bar chart with smooth animations + +```tsx +import { StackedBarChart } from '@/components/charts/stacked-bar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Q1', values: [120, 98, 86] }, + { label: 'Q2', values: [140, 110, 95] }, + { label: 'Q3', values: [160, 130, 105] }, + { label: 'Q4', values: [180, 150, 115] }, +]; + +const categories = ['Sales', 'Marketing', 'Support']; + +export function StackedBarChartDemo() { + return ( + + + + ); +} + +``` + +##### stacked-bar-chart-horizontal + +A horizontal stacked bar chart + +```tsx +import { StackedBarChart } from '@/components/charts/stacked-bar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Product A', values: [45, 30, 25] }, + { label: 'Product B', values: [60, 40, 35] }, + { label: 'Product C', values: [55, 35, 30] }, + { label: 'Product D', values: [70, 45, 40] }, + { label: 'Product E', values: [50, 32, 28] }, +]; + +const categories = ['Direct Sales', 'Online', 'Retail']; + +export function StackedBarChartHorizontal() { + return ( + + + + ); +} + +``` + +##### stacked-bar-chart-styled + +A customized stacked bar chart with custom colors and styling + +```tsx +import { StackedBarChart } from '@/components/charts/stacked-bar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Mobile', values: [85, 45, 30, 20] }, + { label: 'Desktop', values: [120, 80, 50, 35] }, + { label: 'Tablet', values: [65, 35, 25, 15] }, + { label: 'Smart TV', values: [40, 20, 15, 10] }, +]; + +const categories = ['Chrome', 'Safari', 'Firefox', 'Edge']; + +// Custom colors for different browsers +const customColors = [ + '#4285F4', // Chrome blue + '#FF9500', // Safari orange + '#FF6611', // Firefox orange + '#0078D4', // Edge blue +]; + +export function StackedBarChartStyled() { + return ( + + + + ); +} + +``` + +##### stacked-bar-chart-large + +A stacked bar chart with large dataset + +```tsx +import { StackedBarChart } from '@/components/charts/stacked-bar-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Jan', values: [220, 180, 140, 100, 80] }, + { label: 'Feb', values: [240, 190, 150, 110, 85] }, + { label: 'Mar', values: [260, 200, 160, 120, 90] }, + { label: 'Apr', values: [280, 210, 170, 130, 95] }, + { label: 'May', values: [300, 220, 180, 140, 100] }, + { label: 'Jun', values: [320, 230, 190, 150, 105] }, + { label: 'Jul', values: [340, 240, 200, 160, 110] }, + { label: 'Aug', values: [360, 250, 210, 170, 115] }, + { label: 'Sep', values: [380, 260, 220, 180, 120] }, + { label: 'Oct', values: [400, 270, 230, 190, 125] }, + { label: 'Nov', values: [420, 280, 240, 200, 130] }, + { label: 'Dec', values: [440, 290, 250, 210, 135] }, +]; + +const categories = ['Enterprise', 'Professional', 'Standard', 'Basic', 'Free']; + +export function StackedBarChartLarge() { + return ( + + + + ); +} + +``` +--- + +### switch + +A control that allows the user to toggle between checked and not checked states. + +**Installation:** +```bash +npx bna-ui add switch +``` + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Preview:** + +![switch preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 11-02-14_1.MP4) + +#### Basic Usage + +```tsx +import { Switch } from '@/components/ui/switch'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### switch-demo + +A basic switch with label + +```tsx +import { Switch } from '@/components/ui/switch'; +import React, { useState } from 'react'; + +export function SwitchDemo() { + const [isEnabled, setIsEnabled] = useState(false); + + return ( + + ); +} + +``` + +##### switch-simple + +A switch without label text + +```tsx +import { Switch } from '@/components/ui/switch'; +import React, { useState } from 'react'; + +export function SwitchSimple() { + const [isEnabled, setIsEnabled] = useState(false); + + return ; +} + +``` + +##### switch-error + +Switch with error message and styling + +```tsx +import { Switch } from '@/components/ui/switch'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function SwitchError() { + const [isEnabled, setIsEnabled] = useState(false); + + return ( + + + + {}} + error='You must accept the privacy policy' + /> + + ); +} + +``` + +##### switch-disabled + +Switches in disabled state + +```tsx +import { Switch } from '@/components/ui/switch'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function SwitchDisabled() { + const [value, setValue] = useState(false); + + return ( + + + + {}} + disabled={true} + /> + + ); +} + +``` + +##### switch-settings + +Multiple switches arranged in a settings list + +```tsx +import { Switch } from '@/components/ui/switch'; +import { View } from '@/components/ui/view'; +import { useThemeColor } from '@/hooks/useThemeColor'; +import { BORDER_RADIUS } from '@/theme/globals'; +import React, { useState } from 'react'; + +export function SwitchSettings() { + const card = useThemeColor({}, 'card'); + + const [notifications, setNotifications] = useState(true); + const [darkMode, setDarkMode] = useState(false); + const [location, setLocation] = useState(true); + const [analytics, setAnalytics] = useState(false); + + return ( + + + + + + + ); +} + +``` + +##### switch-colors + +Switches with custom colors and styling + +```tsx +import { Switch } from '@/components/ui/switch'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function SwitchColors() { + const [switch1, setSwitch1] = useState(true); + const [switch2, setSwitch2] = useState(true); + const [switch3, setSwitch3] = useState(true); + + return ( + + + + + + ); +} + +``` +--- + +### table + +A flexible data table component with sorting, filtering, pagination, and search functionality. + +**Installation:** +```bash +npx bna-ui add table +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** button, text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +#### Props Interface + +```typescript +export interface TableProps { + data: T[]; + columns: TableColumn[]; + pagination?: boolean; + pageSize?: number; + searchable?: boolean; + searchPlaceholder?: string; + loading?: boolean; + emptyMessage?: string; + style?: ViewStyle; + headerStyle?: ViewStyle; + rowStyle?: ViewStyle; + cellStyle?: ViewStyle; + onRowPress?: (row: T, index: number) => void; + sortable?: boolean; + filterable?: boolean; +} +``` + +**Preview:** + +![table preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-01-2025 11-10-10_1.MP4) + +#### Basic Usage + +```tsx +import { Table } from '@/components/ui/table'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### table-demo + +A basic data table with sample data + +```tsx +import { Table, TableColumn } from '@/components/ui/table'; +import React from 'react'; + +interface User { + id: number; + name: string; + email: string; + role: string; + status: 'Active' | 'Inactive'; +} + +const sampleData: User[] = [ + { + id: 1, + name: 'John Doe', + email: 'john@example.com', + role: 'Admin', + status: 'Active', + }, + { + id: 2, + name: 'Jane Smith', + email: 'jane@example.com', + role: 'User', + status: 'Active', + }, + { + id: 3, + name: 'Bob Johnson', + email: 'bob@example.com', + role: 'Manager', + status: 'Inactive', + }, + { + id: 4, + name: 'Alice Brown', + email: 'alice@example.com', + role: 'User', + status: 'Active', + }, + { + id: 5, + name: 'Charlie Wilson', + email: 'charlie@example.com', + role: 'Admin', + status: 'Active', + }, +]; + +const columns: TableColumn[] = [ + { + id: 'name', + header: 'Name', + accessorKey: 'name', + sortable: true, + filterable: true, + }, + { + id: 'email', + header: 'Email', + accessorKey: 'email', + sortable: true, + filterable: true, + }, + { + id: 'role', + header: 'Role', + accessorKey: 'role', + sortable: true, + filterable: true, + }, + { + id: 'status', + header: 'Status', + accessorKey: 'status', + sortable: true, + filterable: true, + }, +]; + +export function TableDemo() { + return ( +
+ ); +} + +``` + +##### table-sortable + +Table with sortable columns + +```tsx +import { Table, TableColumn } from '@/components/ui/table'; +import React from 'react'; + +interface Product { + id: number; + name: string; + price: number; + category: string; + inStock: boolean; + rating: number; +} + +const products: Product[] = [ + { + id: 1, + name: 'Laptop Pro', + price: 1299.99, + category: 'Electronics', + inStock: true, + rating: 4.5, + }, + { + id: 2, + name: 'Wireless Mouse', + price: 29.99, + category: 'Electronics', + inStock: true, + rating: 4.2, + }, + { + id: 3, + name: 'Coffee Mug', + price: 12.99, + category: 'Kitchen', + inStock: false, + rating: 4.0, + }, + { + id: 4, + name: 'Desk Chair', + price: 199.99, + category: 'Furniture', + inStock: true, + rating: 4.7, + }, + { + id: 5, + name: 'Notebook', + price: 5.99, + category: 'Office', + inStock: true, + rating: 3.8, + }, + { + id: 6, + name: 'Smartphone', + price: 699.99, + category: 'Electronics', + inStock: true, + rating: 4.3, + }, +]; + +const columns: TableColumn[] = [ + { + id: 'name', + header: 'Product Name', + accessorKey: 'name', + sortable: true, + filterable: true, + minWidth: 150, + }, + { + id: 'price', + header: 'Price', + accessorKey: 'price', + sortable: true, + align: 'right', + cell: (value) => `$${value.toFixed(2)}`, + minWidth: 100, + }, + { + id: 'category', + header: 'Category', + accessorKey: 'category', + sortable: true, + filterable: true, + minWidth: 120, + }, + { + id: 'inStock', + header: 'In Stock', + accessorKey: 'inStock', + sortable: true, + align: 'center', + cell: (value) => (value ? '✅' : '❌'), + minWidth: 100, + }, + { + id: 'rating', + header: 'Rating', + accessorKey: 'rating', + sortable: true, + align: 'center', + cell: (value) => `⭐ ${value}`, + minWidth: 100, + }, +]; + +export function TableSortable() { + return ( +
+ ); +} + +``` + +##### table-custom-cells + +Table with custom cell renderers and formatting + +```tsx +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { Badge } from '@/components/ui/badge'; +import { Table, TableColumn } from '@/components/ui/table'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +interface Employee { + id: number; + name: string; + email: string; + department: string; + salary: number; + avatar?: string; + joinDate: string; + status: 'Active' | 'On Leave' | 'Terminated'; +} + +const employees: Employee[] = [ + { + id: 1, + name: 'Sarah Johnson', + email: 'sarah.j@company.com', + department: 'Engineering', + salary: 95000, + avatar: 'https://avatars.githubusercontent.com/u/1?v=4', + joinDate: '2022-01-15', + status: 'Active', + }, + { + id: 2, + name: 'Mike Chen', + email: 'mike.c@company.com', + department: 'Design', + salary: 78000, + joinDate: '2023-03-20', + status: 'Active', + }, + { + id: 3, + name: 'Emma Davis', + email: 'emma.d@company.com', + department: 'Marketing', + salary: 65000, + avatar: 'https://avatars.githubusercontent.com/u/2?v=4', + joinDate: '2021-11-08', + status: 'On Leave', + }, + { + id: 4, + name: 'James Wilson', + email: 'james.w@company.com', + department: 'Sales', + salary: 72000, + joinDate: '2020-09-12', + status: 'Terminated', + }, +]; + +const columns: TableColumn[] = [ + { + id: 'employee', + header: 'Employee', + accessorKey: 'name', + sortable: true, + filterable: true, + minWidth: 200, + cell: (value, row) => ( + + + {row.avatar && } + + {row.name + .split(' ') + .map((n) => n[0]) + .join('')} + + + + + {row.name} + + + {row.email} + + + + ), + }, + { + id: 'department', + header: 'Department', + accessorKey: 'department', + sortable: true, + filterable: true, + minWidth: 120, + }, + { + id: 'salary', + header: 'Salary', + accessorKey: 'salary', + sortable: true, + align: 'right', + minWidth: 120, + cell: (value) => ( + + ${value.toLocaleString()} + + ), + }, + { + id: 'joinDate', + header: 'Join Date', + accessorKey: 'joinDate', + sortable: true, + align: 'center', + minWidth: 120, + cell: (value) => new Date(value).toLocaleDateString(), + }, + { + id: 'status', + header: 'Status', + accessorKey: 'status', + sortable: true, + filterable: true, + align: 'center', + minWidth: 120, + cell: (value) => ( + + {value} + + ), + }, +]; + +export function TableCustomCells() { + return ( +
+ ); +} + +``` + +##### table-pagination + +Table with pagination controls + +```tsx +import { Table, TableColumn } from '@/components/ui/table'; +import React from 'react'; + +interface Order { + id: string; + customer: string; + product: string; + amount: number; + date: string; + status: 'Pending' | 'Completed' | 'Cancelled'; +} + +// Generate sample data +const generateOrders = (count: number): Order[] => { + const customers = [ + 'John Doe', + 'Jane Smith', + 'Bob Johnson', + 'Alice Brown', + 'Charlie Wilson', + 'Diana Ross', + 'Frank Miller', + 'Grace Lee', + ]; + const products = [ + 'Laptop', + 'Mouse', + 'Keyboard', + 'Monitor', + 'Headphones', + 'Webcam', + 'Tablet', + 'Phone', + ]; + const statuses: Order['status'][] = ['Pending', 'Completed', 'Cancelled']; + + return Array.from({ length: count }, (_, i) => ({ + id: `ORD-${String(i + 1).padStart(4, '0')}`, + customer: customers[i % customers.length], + product: products[i % products.length], + amount: Math.floor(Math.random() * 1000) + 50, + date: new Date(Date.now() - Math.random() * 90 * 24 * 60 * 60 * 1000) + .toISOString() + .split('T')[0], + status: statuses[Math.floor(Math.random() * statuses.length)], + })); +}; + +const orders = generateOrders(50); + +const columns: TableColumn[] = [ + { + id: 'id', + header: 'Order ID', + accessorKey: 'id', + sortable: true, + filterable: true, + minWidth: 120, + }, + { + id: 'customer', + header: 'Customer', + accessorKey: 'customer', + sortable: true, + filterable: true, + minWidth: 150, + }, + { + id: 'product', + header: 'Product', + accessorKey: 'product', + sortable: true, + filterable: true, + minWidth: 120, + }, + { + id: 'amount', + header: 'Amount', + accessorKey: 'amount', + sortable: true, + align: 'right', + minWidth: 100, + cell: (value) => `$${value.toFixed(2)}`, + }, + { + id: 'date', + header: 'Date', + accessorKey: 'date', + sortable: true, + align: 'center', + minWidth: 120, + }, + { + id: 'status', + header: 'Status', + accessorKey: 'status', + sortable: true, + filterable: true, + align: 'center', + minWidth: 120, + }, +]; + +export function TablePagination() { + return ( +
+ ); +} + +``` + +##### table-search + +Table with search functionality + +```tsx +import { Table, TableColumn } from '@/components/ui/table'; +import React from 'react'; + +interface Book { + id: number; + title: string; + author: string; + genre: string; + year: number; + isbn: string; + pages: number; +} + +const books: Book[] = [ + { + id: 1, + title: 'The Great Gatsby', + author: 'F. Scott Fitzgerald', + genre: 'Fiction', + year: 1925, + isbn: '978-0-7432-7356-5', + pages: 180, + }, + { + id: 2, + title: 'To Kill a Mockingbird', + author: 'Harper Lee', + genre: 'Fiction', + year: 1960, + isbn: '978-0-06-112008-4', + pages: 281, + }, + { + id: 3, + title: '1984', + author: 'George Orwell', + genre: 'Dystopian', + year: 1949, + isbn: '978-0-452-28423-4', + pages: 328, + }, + { + id: 4, + title: 'Pride and Prejudice', + author: 'Jane Austen', + genre: 'Romance', + year: 1813, + isbn: '978-0-14-143951-8', + pages: 432, + }, + { + id: 5, + title: 'The Catcher in the Rye', + author: 'J.D. Salinger', + genre: 'Fiction', + year: 1951, + isbn: '978-0-316-76948-0', + pages: 277, + }, + { + id: 6, + title: 'Lord of the Flies', + author: 'William Golding', + genre: 'Fiction', + year: 1954, + isbn: '978-0-571-05686-2', + pages: 224, + }, + { + id: 7, + title: 'The Hobbit', + author: 'J.R.R. Tolkien', + genre: 'Fantasy', + year: 1937, + isbn: '978-0-547-92822-7', + pages: 366, + }, + { + id: 8, + title: "Harry Potter and the Sorcerer's Stone", + author: 'J.K. Rowling', + genre: 'Fantasy', + year: 1997, + isbn: '978-0-439-70818-8', + pages: 309, + }, + { + id: 9, + title: 'The Da Vinci Code', + author: 'Dan Brown', + genre: 'Mystery', + year: 2003, + isbn: '978-0-307-47427-5', + pages: 689, + }, + { + id: 10, + title: 'Brave New World', + author: 'Aldous Huxley', + genre: 'Science Fiction', + year: 1932, + isbn: '978-0-06-085052-4', + pages: 268, + }, +]; + +const columns: TableColumn[] = [ + { + id: 'title', + header: 'Title', + accessorKey: 'title', + sortable: true, + filterable: true, + minWidth: 200, + }, + { + id: 'author', + header: 'Author', + accessorKey: 'author', + sortable: true, + filterable: true, + minWidth: 150, + }, + { + id: 'genre', + header: 'Genre', + accessorKey: 'genre', + sortable: true, + filterable: true, + minWidth: 120, + }, + { + id: 'year', + header: 'Year', + accessorKey: 'year', + sortable: true, + align: 'center', + minWidth: 80, + }, + { + id: 'isbn', + header: 'ISBN', + accessorKey: 'isbn', + filterable: true, + minWidth: 150, + }, + { + id: 'pages', + header: 'Pages', + accessorKey: 'pages', + sortable: true, + align: 'right', + minWidth: 80, + }, +]; + +export function TableSearch() { + return ( +
+ ); +} + +``` + +##### table-loading + +Table showing loading state + +```tsx +import { Button } from '@/components/ui/button'; +import { Table, TableColumn } from '@/components/ui/table'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +interface ApiData { + id: number; + name: string; + value: number; + category: string; +} + +const mockData: ApiData[] = [ + { id: 1, name: 'Item A', value: 100, category: 'Type 1' }, + { id: 2, name: 'Item B', value: 250, category: 'Type 2' }, + { id: 3, name: 'Item C', value: 175, category: 'Type 1' }, + { id: 4, name: 'Item D', value: 320, category: 'Type 3' }, +]; + +const columns: TableColumn[] = [ + { + id: 'name', + header: 'Name', + accessorKey: 'name', + sortable: true, + filterable: true, + }, + { + id: 'value', + header: 'Value', + accessorKey: 'value', + sortable: true, + align: 'right', + }, + { + id: 'category', + header: 'Category', + accessorKey: 'category', + sortable: true, + filterable: true, + }, +]; + +export function TableLoading() { + const [loading, setLoading] = useState(false); + const [data, setData] = useState([]); + + const simulateLoading = () => { + setLoading(true); + setData([]); + + // Simulate API call + setTimeout(() => { + setData(mockData); + setLoading(false); + }, 2000); + }; + + const clearData = () => { + setData([]); + setLoading(false); + }; + + return ( + + + + + + +
+ + ); +} + +``` +--- + +### tabs + +A foundational View component with transparent background and ref forwarding support. + +**Installation:** +```bash +npx bna-ui add tabs +``` + +**External Dependencies:** react-native-gesture-handler, react-native-reanimated + +**Registry Dependencies:** text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![tabs preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-02-2025 06-18-46_1.MP4) + +#### Basic Usage + +```tsx +import { Tabs } from '@/components/ui/tabs'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### tabs-demo + +Basic tabs container with content + +```tsx +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function TabsDemo() { + return ( + + + Account + Followers + Following + Password + Settings + More + + + + + + Account Settings + + + Manage your account information and preferences here. + + + + + + + + Followers + + + Manage your followers information and preferences here. + + + + + + + + Following + + + Manage your following information and preferences here. + + + + + + + + Password Settings + + + Change your password and security settings preferences here. + + + + + + + + General Settings + + + Configure your application preferences and options. + + + + + + + + More + + + Configure your application preferences and options. + + + + + ); +} + +``` + +##### tabs-vertical + +Tabs arranged in vertical orientation + +```tsx +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function TabsVertical() { + return ( + + + 🧑‍💼 + 🫆 + 🔔 + 💰 + + + + + + Profile Information + + + Update your personal information and profile picture. + + + + + + + + Security Settings + + + Manage two-factor authentication and login security. + + + + + + + + Notification Preferences + + + Configure how and when you receive notifications. + + + + + + + + Billing & Subscription + + + Manage your subscription and payment methods. + + + + + ); +} + +``` + +##### tabs-disabled + +Tabs with disabled states + +```tsx +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function TabsDisabled() { + return ( + + + Available + Pending + + Premium + + + Enterprise + + + + + + + Available Features + + + These features are currently available to you. + + + + + + + + Pending Features + + + These features are being processed and will be available soon. + + + + + + + + Premium Features + + Upgrade to access premium features. + + + + + + + Enterprise Features + + Contact sales for enterprise features. + + + + ); +} + +``` + +##### tabs-styled + +Tabs with custom colors and styling + +```tsx +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import { useState } from 'react'; + +export function TabsStyled() { + const [value, setValue] = useState('design'); + + return ( + + + + Design + + + Development + + + Testing + + + + + + + Design Phase + + + Create wireframes, mockups, and design systems for your project. + + + + + + + + Development Phase + + + Build and implement the features based on the design specifications. + + + + + + + + Testing Phase + + + Perform quality assurance and user acceptance testing. + + + + + ); +} + +``` +--- + +### text + +A foundational View component with transparent background and ref forwarding support. + +**Installation:** +```bash +npx bna-ui add text +``` + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![text preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/IMG_5741.PNG) + +#### Basic Usage + +```tsx +import { Text } from '@/components/ui/text'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### text-demo + +Basic text component showing different variants + +```tsx +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function TextDemo() { + return ( + + Heading Text + Title Text + Subtitle Text + + This is body text that demonstrates the default styling for regular + content. + + Caption text for additional information + Link text with underline + + ); +} + +``` + +##### text-variants + +All text variants showing the typography hierarchy + +```tsx +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function TextVariants() { + return ( + + + + HEADING (28px, weight 700) + + + The quick brown fox jumps over the lazy dog + + + + + + TITLE (24px, weight 700) + + The quick brown fox jumps over the lazy dog + + + + + SUBTITLE (19px, weight 600) + + + The quick brown fox jumps over the lazy dog + + + + + + BODY (16px, weight 400) + + + The quick brown fox jumps over the lazy dog. This is the default text + variant used for body content and regular paragraphs. + + + + + + CAPTION (16px, weight 400, muted) + + + The quick brown fox jumps over the lazy dog + + + + + + LINK (16px, weight 500, underlined) + + The quick brown fox jumps over the lazy dog + + + ); +} + +``` + +##### text-colors + +Text with custom light and dark mode colors + +```tsx +import { Text } from '@/components/ui/text'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function TextColors() { + return ( + + + Custom Color Examples + + + + This text uses custom blue colors for light and dark themes + + + + This text uses custom green colors for light and dark themes + + + + This text uses custom amber colors for light and dark themes + + + + This text uses custom red colors for light and dark themes + + + + This text uses custom purple colors for light and dark themes + + + + + Note: These colors automatically adapt based on the current theme + (light/dark mode) + + + + ); +} + +``` +--- + +### toast + +A succinct message that is displayed temporarily with Dynamic Island animation inspired by iOS. + +**Installation:** +```bash +npx bna-ui add toast +``` + +**External Dependencies:** react-native-gesture-handler, react-native-reanimated, lucide-react-native + +**Registry Dependencies:** text + +#### Types + +```typescript +export type ToastVariant = 'default' | 'success' | 'error' | 'warning' | 'info' +``` + +**Preview:** + +![toast preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-02-2025 06-24-28_1.MP4) + +#### Basic Usage + +```tsx +import { Toast } from '@/components/ui/toast'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### toast-demo + +A basic toast notification with title and description + +```tsx +import { Button } from '@/components/ui/button'; +import { useToast } from '@/components/ui/toast'; +import React from 'react'; + +export function ToastDemo() { + const { toast } = useToast(); + + const showToast = () => { + toast({ + title: 'Toast Notification', + description: + 'This is a basic toast notification with title and description.', + variant: 'default', + }); + }; + + return ; +} + +``` + +##### toast-variants + +Toast notifications with different variants (success, error, warning, info) + +```tsx +import { Button } from '@/components/ui/button'; +import { useToast } from '@/components/ui/toast'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ToastVariants() { + const { success, error, warning, info } = useToast(); + + return ( + + + + + + + + + + ); +} + +``` + +##### toast-actions + +Toast notifications with action buttons + +```tsx +import { Button } from '@/components/ui/button'; +import { useToast } from '@/components/ui/toast'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ToastActions() { + const { toast } = useToast(); + + const showToastWithAction = () => { + toast({ + title: 'New message received', + description: 'You have a new message from John Doe.', + variant: 'info', + action: { + label: 'View', + onPress: () => { + console.log('View action pressed'); + // Navigate to message or perform action + }, + }, + }); + }; + + const showUndoToast = () => { + toast({ + title: 'Item deleted', + description: 'The item has been removed from your list.', + variant: 'warning', + duration: 8000, // Longer duration for undo action + action: { + label: 'Undo', + onPress: () => { + console.log('Undo action pressed'); + // Restore the deleted item + }, + }, + }); + }; + + return ( + + + + + + ); +} + +``` + +##### toast-duration + +Toast notifications with custom durations + +```tsx +import { Button } from '@/components/ui/button'; +import { useToast } from '@/components/ui/toast'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ToastDuration() { + const { toast } = useToast(); + + return ( + + + + + + + + + + ); +} + +``` + +##### toast-multiple + +Multiple toast notifications stacked vertically + +```tsx +import { Button } from '@/components/ui/button'; +import { useToast } from '@/components/ui/toast'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ToastMultiple() { + const { toast, dismissAll } = useToast(); + + const showMultipleToasts = () => { + const variants = ['success', 'warning', 'error', 'info'] as const; + const messages = [ + { title: 'Success!', description: 'Operation completed successfully' }, + { title: 'Warning', description: 'Please check your input' }, + { title: 'Error', description: 'Something went wrong' }, + { title: 'Info', description: "Here's some information" }, + ]; + + variants.forEach((variant, index) => { + setTimeout(() => { + toast({ + ...messages[index], + variant, + duration: 6000, + }); + }, index * 500); // Stagger the toasts + }); + }; + + const showBatchToasts = () => { + // Show multiple toasts at once + toast({ + title: 'First toast', + description: 'This is the first toast', + variant: 'success', + }); + + toast({ + title: 'Second toast', + description: 'This is the second toast', + variant: 'info', + }); + + toast({ + title: 'Third toast', + description: 'This is the third toast', + variant: 'warning', + }); + }; + + return ( + + + + + + + + ); +} + +``` + +##### toast-compact + +Compact toast notifications without title or description + +```tsx +import { Button } from '@/components/ui/button'; +import { useToast } from '@/components/ui/toast'; +import { View } from '@/components/ui/view'; +import React from 'react'; + +export function ToastCompact() { + const { toast } = useToast(); + + return ( + + + + + + + + + + + + ); +} + +``` +--- + +### toggle + +A two-state button that can be either on or off. + +**Installation:** +```bash +npx bna-ui add toggle +``` + +**External Dependencies:** lucide-react-native + +**Registry Dependencies:** text, view, icon + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![toggle preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-02-2025 06-32-48_1.MP4) + +#### Basic Usage + +```tsx +import { Toggle } from '@/components/ui/toggle'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### toggle-demo + +A basic toggle button with icon + +```tsx +import { Toggle } from '@/components/ui/toggle'; +import { Bold } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ToggleDemo() { + const [pressed, setPressed] = useState(false); + + return ( + + + + ); +} + +``` + +##### toggle-variants + +Toggle buttons in different variants + +```tsx +import { Toggle } from '@/components/ui/toggle'; +import { View } from '@/components/ui/view'; +import { Bold, Italic } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ToggleVariants() { + const [pressed1, setPressed1] = useState(false); + const [pressed2, setPressed2] = useState(true); + const [pressed3, setPressed3] = useState(false); + const [pressed4, setPressed4] = useState(true); + + return ( + + + + + + + + + + + + + + + ); +} + +``` + +##### toggle-sizes + +Toggle buttons in different sizes + +```tsx +import { Toggle } from '@/components/ui/toggle'; +import { View } from '@/components/ui/view'; +import { Bold } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ToggleSizes() { + const [pressed1, setPressed1] = useState(false); + const [pressed2, setPressed2] = useState(true); + + return ( + + + + + + Bold + + + ); +} + +``` + +##### toggle-text + +Toggle buttons with text labels + +```tsx +import { Toggle } from '@/components/ui/toggle'; +import { View } from '@/components/ui/view'; +import React, { useState } from 'react'; + +export function ToggleText() { + const [pressed1, setPressed1] = useState(false); + const [pressed2, setPressed2] = useState(true); + const [pressed3, setPressed3] = useState(false); + + return ( + + + Bold + + + Italic + + + Underline + + + ); +} + +``` + +##### toggle-disabled + +Disabled toggle buttons + +```tsx +import { Toggle } from '@/components/ui/toggle'; +import { View } from '@/components/ui/view'; +import { Bold, Italic } from 'lucide-react-native'; +import React from 'react'; + +export function ToggleDisabled() { + return ( + + + + + + + + + + + + + + + ); +} + +``` + +##### toggle-group-single + +Single selection toggle group + +```tsx +import { ToggleGroupSingle } from '@/components/ui/toggle'; +import { AlignCenter, AlignLeft, AlignRight } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ToggleGroupSingleDemo() { + const [value, setValue] = useState('left'); + + const items = [ + { value: 'left', label: 'Left', icon: AlignLeft }, + { value: 'center', label: 'Center', icon: AlignCenter }, + { value: 'right', label: 'Right', icon: AlignRight }, + ]; + + return ( + + ); +} + +``` + +##### toggle-group-multiple + +Multiple selection toggle group + +```tsx +import { ToggleGroupMultiple } from '@/components/ui/toggle'; +import { Bold, Italic, Underline } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ToggleGroupMultipleDemo() { + const [value, setValue] = useState(['bold']); + + const items = [ + { value: 'bold', label: 'Bold', icon: Bold }, + { value: 'italic', label: 'Italic', icon: Italic }, + { value: 'underline', label: 'Underline', icon: Underline }, + ]; + + return ( + + ); +} + +``` + +##### toggle-group-vertical + +Vertical toggle group layout + +```tsx +import { ToggleGroupSingle } from '@/components/ui/toggle'; +import { AlignCenter, AlignLeft, AlignRight } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ToggleGroupVertical() { + const [value, setValue] = useState('left'); + + const items = [ + { value: 'left', label: 'Left Align', icon: AlignLeft }, + { value: 'center', label: 'Center Align', icon: AlignCenter }, + { value: 'right', label: 'Right Align', icon: AlignRight }, + ]; + + return ( + + ); +} + +``` + +##### toggle-group-outline + +Toggle group with outline variant + +```tsx +import { ToggleGroupSingle } from '@/components/ui/toggle'; +import { Bold, Italic, Underline } from 'lucide-react-native'; +import React, { useState } from 'react'; + +export function ToggleGroupOutline() { + const [value, setValue] = useState('bold'); + + const items = [ + { value: 'bold', label: 'Bold', icon: Bold }, + { value: 'italic', label: 'Italic', icon: Italic }, + { value: 'underline', label: 'Underline', icon: Underline }, + ]; + + return ( + + ); +} + +``` +--- + +### treemap-chart + +A customizable treemap chart component with hierarchical data visualization, smooth animations, and flexible styling using the squarified treemap algorithm. + +**Installation:** +```bash +npx bna-ui add treemap-chart +``` + +**External Dependencies:** react-native-svg, react-native-reanimated, react-native-gesture-handler + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![treemap-chart preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/treemap-demo.MP4) + +#### Basic Usage + +```tsx +import { Treemap-chart } from '@/components/ui/treemap-chart'; + +export default function Example() { + return ( + + ); +}``` + + +#### Advanced Examples + +##### treemap-chart-demo + +A treemap chart with smooth animations + +```tsx +import { TreeMapChart } from '@/components/charts/treemap-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Sales', value: 120 }, + { label: 'Marketing', value: 98 }, + { label: 'Support', value: 86 }, + { label: 'Development', value: 140 }, + { label: 'Design', value: 75 }, + { label: 'HR', value: 65 }, +]; + +export function TreeMapChartDemo() { + return ( + + + + ); +} + +``` + +##### treemap-chart-sample + +A sample treemap chart with various data sizes + +```tsx +import { TreeMapChart } from '@/components/charts/treemap-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const sampleData = [ + { label: 'Product A', value: 250 }, + { label: 'Product B', value: 180 }, + { label: 'Product C', value: 320 }, + { label: 'Product D', value: 90 }, + { label: 'Product E', value: 150 }, + { label: 'Product F', value: 45 }, + { label: 'Product G', value: 210 }, + { label: 'Product H', value: 75 }, +]; + +export function TreeMapChartSample() { + return ( + + + + ); +} + +``` + +##### treemap-chart-styled + +A customized treemap chart with custom colors and styling + +```tsx +import { TreeMapChart } from '@/components/charts/treemap-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const styledData = [ + { label: 'Mobile', value: 450, color: '#FF6B6B' }, + { label: 'Desktop', value: 320, color: '#4ECDC4' }, + { label: 'Tablet', value: 180, color: '#45B7D1' }, + { label: 'Watch', value: 90, color: '#FFA07A' }, + { label: 'TV', value: 150, color: '#98D8C8' }, + { label: 'Other', value: 60, color: '#F7DC6F' }, +]; + +export function TreeMapChartStyled() { + return ( + + + + ); +} + +``` + +##### treemap-chart-large + +A treemap chart with large dataset + +```tsx +import { TreeMapChart } from '@/components/charts/treemap-chart'; +import { ChartContainer } from '@/components/charts/chart-container'; +import React from 'react'; + +const largeData = [ + { label: 'North America', value: 1250 }, + { label: 'Europe', value: 980 }, + { label: 'Asia Pacific', value: 1450 }, + { label: 'South America', value: 320 }, + { label: 'Africa', value: 180 }, + { label: 'Middle East', value: 240 }, + { label: 'Oceania', value: 95 }, + { label: 'Central Asia', value: 150 }, + { label: 'Caribbean', value: 85 }, + { label: 'Eastern Europe', value: 420 }, + { label: 'Nordic', value: 280 }, + { label: 'Southeast Asia', value: 650 }, + { label: 'East Africa', value: 120 }, + { label: 'West Africa', value: 200 }, + { label: 'Central America', value: 110 }, +]; + +export function TreeMapChartLarge() { + return ( + + + + ); +} + +``` +--- + +### video + +A video player component with custom controls, gestures, and subtitle support. + +**Installation:** +```bash +npx bna-ui add video +``` + +**External Dependencies:** expo-video, lucide-react-native + +**Registry Dependencies:** progress, text, view + +**Required Hooks:** useThemeColor + +**Theme Dependencies:** globals + +**Preview:** + +![video preview](https://cdn.jsdelivr.net/gh/ahmedbna/bna-ui-demo/ScreenRecording_07-02-2025 06-41-52_1.MP4) + +#### Basic Usage + +```tsx +import { Video } from '@/components/ui/video'; + +export default function Example() { + return ( +