Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 61 additions & 50 deletions app/(site)/docs/components/accordion/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,20 @@ export default function AccordionPage() {
"language": "tsx"
}
]}
componentCode={`import * as React from 'react';
import { Pressable, View, Text, LayoutAnimation, Platform, UIManager } from 'react-native';
import { Feather } from '@expo/vector-icons';
import { cn } from '@/lib/utils';
componentCode={`import * as React from "react";
import {
Pressable,
View,
Text,
LayoutAnimation,
Platform,
UIManager,
} from "react-native";
import { Feather } from "@expo/vector-icons";
import { cn } from "@/lib/utils";

// Enable layout animation for Android
if (Platform.OS === 'android') {
if (Platform.OS === "android") {
if (UIManager.setLayoutAnimationEnabledExperimental) {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
Expand All @@ -28,13 +35,15 @@ if (Platform.OS === 'android') {
interface AccordionContextValue {
value: string[];
onValueChange: (itemValue: string) => void;
type: 'single' | 'multiple';
type: "single" | "multiple";
}

const AccordionContext = React.createContext<AccordionContextValue | null>(null);
const AccordionContext = React.createContext<AccordionContextValue | null>(
null
);

export interface AccordionProps {
type?: 'single' | 'multiple';
type?: "single" | "multiple";
collapsible?: boolean;
value?: string[];
onValueChange?: (value: string[]) => void;
Expand All @@ -44,50 +53,55 @@ export interface AccordionProps {
}

const Accordion = ({
type = 'single',
type = "single",
collapsible = false,
value,
onValueChange,
defaultValue,
className,
children,
}: AccordionProps) => {
const [state, setState] = React.useState<string[]>(value || defaultValue || []);
const [state, setState] = React.useState<string[]>(
value || defaultValue || []
);

const isControlled = value !== undefined;
const accordionValue = isControlled ? value : state;

const handleValueChange = React.useCallback((itemValue: string) => {
const isSelected = accordionValue.includes(itemValue);
const handleValueChange = React.useCallback(
(itemValue: string) => {
const isSelected = accordionValue.includes(itemValue);

let newValue: string[] = [];
let newValue: string[] = [];

if (type === 'single') {
if (isSelected) {
newValue = collapsible ? [] : [itemValue];
} else {
newValue = [itemValue];
}
} else {
if (isSelected) {
newValue = accordionValue.filter((v) => v !== itemValue);
if (type === "single") {
if (isSelected) {
newValue = collapsible ? [] : [itemValue];
} else {
newValue = [itemValue];
}
} else {
newValue = [...accordionValue, itemValue];
if (isSelected) {
newValue = accordionValue.filter((v) => v !== itemValue);
} else {
newValue = [...accordionValue, itemValue];
}
}
}

if (!isControlled) {
setState(newValue);
}
if (!isControlled) {
setState(newValue);
}

onValueChange?.(newValue);
}, [accordionValue, collapsible, isControlled, onValueChange, type]);
onValueChange?.(newValue);
},
[accordionValue, collapsible, isControlled, onValueChange, type]
);

return (
<AccordionContext.Provider value={{ value: accordionValue, onValueChange: handleValueChange, type }}>
<View className={cn("w-full", className)}>
{children}
</View>
<AccordionContext.Provider
value={{ value: accordionValue, onValueChange: handleValueChange, type }}
>
<View className={cn("w-full", className)}>{children}</View>
</AccordionContext.Provider>
);
};
Expand All @@ -102,7 +116,7 @@ const AccordionItem = ({ value, className, children }: AccordionItemProps) => {
const context = React.useContext(AccordionContext);

if (!context) {
throw new Error('AccordionItem must be used within an Accordion');
throw new Error("AccordionItem must be used within an Accordion");
}

const isExpanded = context.value.includes(value);
Expand Down Expand Up @@ -141,7 +155,6 @@ const AccordionTrigger = ({
return null;
}

// Utiliser LayoutAnimation à la place de Reanimated pour l'icône
const iconRotation = isExpanded ? 180 : 0;

const handlePress = () => {
Expand All @@ -152,17 +165,16 @@ const AccordionTrigger = ({
return (
<Pressable
onPress={handlePress}
className={cn(
"flex-row items-center justify-between py-4",
className
)}
className={cn("flex-row items-center justify-between py-4", className)}
accessibilityRole="button"
accessibilityState={{ expanded: isExpanded }}
accessibilityHint="Toggle accordion section"
>
<View className="flex-1">
{typeof children === 'string' ? (
<Text className="text-base font-medium text-foreground">{children}</Text>
{typeof children === "string" ? (
<Text className="text-base font-medium text-foreground">
{children}
</Text>
) : (
children
)}
Expand Down Expand Up @@ -192,10 +204,8 @@ const AccordionContent = ({
}

return (
<View
className={cn("pb-4 pt-0", className)}
>
{typeof children === 'string' ? (
<View className={cn("pb-4 pt-0", className)}>
{typeof children === "string" ? (
<Text className="text-base text-muted-foreground">{children}</Text>
) : (
children
Expand All @@ -204,12 +214,13 @@ const AccordionContent = ({
);
};

Accordion.displayName = 'Accordion';
AccordionItem.displayName = 'AccordionItem';
AccordionTrigger.displayName = 'AccordionTrigger';
AccordionContent.displayName = 'AccordionContent';
Accordion.displayName = "Accordion";
AccordionItem.displayName = "AccordionItem";
AccordionTrigger.displayName = "AccordionTrigger";
AccordionContent.displayName = "AccordionContent";

export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; `}
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
`}
previewCode={`import { Accordion } from "@nativeui/ui";

export default function AccordionDemo() {
Expand Down
Loading