import React from 'react' export type AccentKey = | 'primary' | 'warning' | 'info' | 'success' | 'danger' | 'secondary' | 'green' | 'pink' | 'yellow' | 'light' | 'dark' export interface PanelProps extends React.HTMLAttributes { size?: number variant?: 'border' | 'solid' accent?: AccentKey children?: React.ReactNode } const Panel: React.FC = ({ children }) => <>{children} Panel.displayName = 'SplitPanel' interface SplitProps { direction?: 'horizontal' | 'vertical' gutter?: string | number className?: string // allow any children but we will look for Panel elements children?: React.ReactNode variant?: 'border' | 'solid' accent?: AccentKey } const Split: React.FC & { Panel: React.FC } = ({ direction = 'horizontal', gutter = 20, className = '', children, variant: splitVariant, accent: splitAccent, }) => { const childrenArray = React.Children.toArray(children) if (childrenArray.length < 1) { throw new Error('Split requires at least one child Panel') } const gutterVal = typeof gutter === 'number' ? `${gutter}px` : gutter const containerStyle: React.CSSProperties = { ['--split-gutter' as any]: gutterVal } const dirClass = direction === 'vertical' ? 'split--vertical' : 'split--horizontal' // detect Panel children and extract sizes and passthrough props const sizes: (number | null)[] = childrenArray.map((c) => { if (React.isValidElement(c) && (c.type as any).displayName === 'SplitPanel') { const p = c as React.ReactElement return typeof p.props.size === 'number' ? p.props.size : 1 } return null }) const allPanels = sizes.every((s) => s != null) // compute flex styles if panels provided sizes return (
{childrenArray.map((child, idx) => { const style: React.CSSProperties = {} let extraClass = '' let extraStyle: React.CSSProperties | undefined if (React.isValidElement(child) && (child.type as any).displayName === 'SplitPanel') { const p = child as React.ReactElement extraClass = p.props.className || '' extraStyle = p.props.style // inherit variant/accent from parent if panel doesn't specify one if (!p.props.variant && splitVariant) { // handled below } if (!p.props.accent && splitAccent) { // handled below } } if (allPanels) { const unit = (sizes[idx] || 0) as number style.flex = `${unit} 1 0` } const mergedStyle = extraStyle ? { ...style, ...extraStyle } : style const childElem = React.isValidElement(child) ? (child as React.ReactElement) : null const variant = childElem ? childElem.props.variant ?? splitVariant : splitVariant // Determine accent: a panel with its own `accent` always uses it. // Otherwise, if the parent `Split` has an `accent`, only the first // panel (top-left decoration) and the last panel (bottom-right) // receive the inherited accent. No other panels inherit the split // accent. const panelHasOwnAccent = !!(childElem && childElem.props.accent) let accent: AccentKey | undefined = undefined let accentTL = false let accentBR = false const lastIndex = childrenArray.length - 1 const isFirst = idx === 0 const isLast = idx === lastIndex if (panelHasOwnAccent) { // panel-specified accent: show both decorations accent = childElem!.props.accent as AccentKey accentTL = true accentBR = true } else if (splitAccent) { // inherited accent from Split: only first gets top-left, last gets bottom-right if (isFirst) { accent = splitAccent accentTL = true } if (isLast) { accent = splitAccent accentBR = true } } let variantClass = '' if (variant === 'border') variantClass = 'pane--border' else if (variant === 'solid') variantClass = 'pane--solid' const paneClass = `pane ${variantClass} ${extraClass} ${accentTL ? 'pane--accent--tl' : ''} ${accentBR ? 'pane--accent--br' : ''}`.trim() // attach accent color as CSS var for the mixin to pick up const finalStyle = { ...mergedStyle } if (accent) { ;(finalStyle as any)['--pane-accent-color'] = `var(--color-variant-${accent})` } return (
{child}
) })}
) } Split.Panel = Panel export { Split } export default Split