import { selectDefined } from '@o/utils' import { gloss } from 'gloss' import React, { useMemo } from 'react' import { Toggler, useToggle } from './hooks/useToggle' import { Icon } from './Icon' export type CollapsableProps = { /** Uncontrolled: set initial state as collapsed */ defaultCollapsed?: boolean /** Enable collapsing this element */ collapsable?: boolean /** Controlled: set state collapsed */ collapsed?: boolean /** Callback on collapse change */ onCollapse?: (next: boolean) => any /** Pass in a `useToggle` that controls collapse state */ useCollapse?: Toggler } export const splitCollapseProps = ( all: A, ): [ CollapsableProps, Omit ] => { return useMemo(() => { const { defaultCollapsed, collapsed, collapsable, onCollapse, useCollapse: useToggleOg, ...rest } = all if ( selectDefined(defaultCollapsed, collapsed, collapsable, onCollapse, useToggle) === undefined ) { return [null, rest] as any } return [ { defaultCollapsed, collapsed, collapsable, onCollapse, useCollapse: useToggleOg }, rest, ] }, [all]) } export const useCollapse = (props: CollapsableProps) => { if (props.useCollapse) { return props.useCollapse } return useToggle( selectDefined(props.defaultCollapsed, props.collapsed, false), props.onCollapse, props, ) } export type CollapsableViewProps = CollapsableProps & { children: React.ReactNode } export const Collapsable = (props: CollapsableViewProps) => { return createCollapsableChildren(props) } export const createCollapsableChildren = (props: CollapsableViewProps) => { const innerToggle = useCollapse(props) const toggle = props.useCollapse || innerToggle // this inherits from useToggle nicely, not the clearest pattern... const isCollapsable = props.useCollapse ? toggle.collapseProps.collapsable : props.collapsable if (isCollapsable === false) { return <>{props.children} } return <>{toggle.val ? null : props.children || null} } export const CollapseArrow = (props: CollapsableProps) => { const toggle = useCollapse(props) if (!toggle.isCollapsable) { return null } return ( ) } const Chevron = gloss(Icon, { marginRight: 4, marginLeft: -2, transition: 'all ease 200ms', })