@hanamura/react-containers
Version:
Flexible container components for React
69 lines • 3.17 kB
JavaScript
'use client';
import { jsxs as _jsxs } from "react/jsx-runtime";
import { Children, Fragment, isValidElement, cloneElement } from 'react';
import { useAdaptiveContainer, normalizeSpacingValue, } from '../core';
/**
* Stack component - arranges children in a vertical stack
*
* Features:
* - Responsive spacing based on breakpoints
* - Optional dividers at start, between items, and end
* - Customizable divider rendering with position information
* - Customizable HTML element via 'as' prop
*/
export function Stack({ children, options, queries, adaptiveOptions, className, style, as = 'div', ...rest }) {
// Get the current options based on active breakpoint
const { options: mergedOptions } = useAdaptiveContainer(options, queries, adaptiveOptions);
// Create the base style for the stack container
const containerStyle = {
display: 'flex',
flexDirection: 'column',
gap: normalizeSpacingValue(mergedOptions.gap) ?? 0,
paddingInline: normalizeSpacingValue(mergedOptions.paddingInline),
paddingBlock: normalizeSpacingValue(mergedOptions.paddingBlock),
...style,
};
// Default divider positions with between=true as default
const dividerPositions = {
start: false,
between: true,
end: false,
...mergedOptions.dividerPositions,
};
// Process children for rendering
const childrenArray = Children.toArray(children);
const childCount = childrenArray.length;
const Component = as;
// Helper function to render divider based on type
const renderDivider = (index, position) => {
if (!mergedOptions.divider)
return null;
if (typeof mergedOptions.divider === 'function') {
// If divider is a function, call it with props
return mergedOptions.divider({ index, position });
}
else {
// If divider is a ReactNode, clone it to avoid React warnings about using
// the same element instance in multiple locations
if (isValidElement(mergedOptions.divider)) {
// Add key to ensure React can properly differentiate between cloned elements
return cloneElement(mergedOptions.divider, {
key: `divider-${position}-${index}`
});
}
// For non-element ReactNodes (like strings), return as is
return mergedOptions.divider;
}
};
return (_jsxs(Component, { className: className, style: containerStyle, ...rest, children: [childCount > 0 &&
mergedOptions.divider &&
dividerPositions.start &&
renderDivider(0, 'start'), childrenArray.map((child, index) => (_jsxs(Fragment, { children: [child, mergedOptions.divider &&
index < childCount - 1 &&
dividerPositions.between &&
renderDivider(index + 1, 'between')] }, index))), childCount > 0 &&
mergedOptions.divider &&
dividerPositions.end &&
renderDivider(childCount, 'end')] }));
}
//# sourceMappingURL=Stack.js.map