Skip to content

Instantly share code, notes, and snippets.

@osamaqarem
Last active August 20, 2023 17:47
Show Gist options
  • Save osamaqarem/fac018f6ed05088bae71ef098c453cea to your computer and use it in GitHub Desktop.
Save osamaqarem/fac018f6ed05088bae71ef098c453cea to your computer and use it in GitHub Desktop.
Accordion.tsx
import * as React from 'react';
import { LayoutAnimation, Pressable, PressableProps } from 'react-native';
type ChildrenOrRenderProp = React.ReactNode | ((mode: Context['mode']) => React.ReactNode);
type RewriteChildren<PropsType = Record<string, unknown>> = React.FC<
Omit<PropsType, 'children'> & { children?: ChildrenOrRenderProp }
>;
interface Context {
mode: 'closed' | 'open';
toggle: () => void;
}
const AccordionContext = React.createContext<Nullable<Context>>(null);
const useAccordionContext = () => {
const ctx = React.useContext(AccordionContext);
if (!ctx) {
throw new Error('Accordion components must be used within Accordon.Root');
}
return ctx;
};
export interface AccordionRootProps {
initialMode?: 'closed' | 'open';
}
const Root: RewriteChildren<AccordionRootProps> = (props) => {
const [mode, _setMode] = React.useState<'closed' | 'open'>(props.initialMode ?? 'closed');
const setMode = (newMode: typeof mode) => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
_setMode(newMode);
};
return (
<AccordionContext.Provider
value={{
mode,
toggle: () => setMode(mode === 'open' ? 'closed' : 'open'),
}}
>
{typeof props.children === 'function' ? props.children(mode) : props.children}
</AccordionContext.Provider>
);
};
const Toggle: RewriteChildren<PressableProps> = (props) => {
const ctx = useAccordionContext();
return (
<Pressable {...props} onPress={ctx.toggle}>
{typeof props.children === 'function' ? props.children(ctx.mode) : props.children}
</Pressable>
);
};
const Content: RewriteChildren = (props) => {
const ctx = useAccordionContext();
if (ctx.mode === 'closed') return null;
return <>{typeof props.children === 'function' ? props.children(ctx.mode) : props.children}</>;
};
export const Accordion = {
Root,
Toggle,
Content,
};
const Example = () => {
return (
<Accordion.Root>
<Accordion.Content>
<Text>Content</Text>
<Text>Content</Text>
<Text>Content</Text>
</Accordion.Content>
<Accordion.Toggle>
{(mode) => (mode === 'closed' ? <Text>Show More</Text> : <Text>Show Less</Text>)}
</Accordion.Toggle>
</Accordion.Root>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment