Checkpoint

This commit is contained in:
2026-03-09 16:43:24 +01:00
parent aed73e824e
commit 19bb022290
35 changed files with 1093 additions and 58 deletions

View File

@@ -0,0 +1,148 @@
import React from 'react'
export type AccentKey =
| 'primary'
| 'warning'
| 'info'
| 'success'
| 'danger'
| 'secondary'
| 'green'
| 'pink'
| 'yellow'
| 'light'
| 'dark'
export interface PanelProps extends React.HTMLAttributes<HTMLDivElement> {
size?: number
variant?: 'border' | 'solid'
accent?: AccentKey
children?: React.ReactNode
}
const Panel: React.FC<PanelProps> = ({ 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<SplitProps> & { Panel: React.FC<PanelProps> } = ({
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<PanelProps>
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 (
<div className={`split ${dirClass} ${className}`.trim()} style={containerStyle}>
{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<PanelProps>
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<PanelProps>) : 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 (
<div className={paneClass} key={idx} style={finalStyle}>
{child}
</div>
)
})}
</div>
)
}
Split.Panel = Panel
export { Split }
export default Split