import React, { forwardRef } from 'react'; import { getWebProps } from 'react-native-unistyles/web'; import { ViewProps } from './types'; import { viewStyles } from './View.styles'; import useMergeRefs from '../hooks/useMergeRefs'; import { useWebLayout } from '../hooks/useWebLayout'; import type { IdealystElement } from '../utils/refTypes'; import { flattenStyle } from '../utils/flattenStyle'; /** * Fundamental layout container with background, border, and spacing options. * The base building block for composing UI layouts. */ const View = forwardRef(({ children, background = 'transparent', radius = 'none', border = 'none', // Spacing variants from ContainerStyleProps gap, padding, paddingVertical, paddingHorizontal, margin, marginVertical, marginHorizontal, // Override props (accepted but handled via style prop on web) backgroundColor: _backgroundColor, borderRadius: _borderRadius, borderWidth: _borderWidth, borderColor: _borderColor, scrollable, style, testID, id, onLayout, }, ref) => { const layoutRef = useWebLayout(onLayout); // Handle 'transparent' background separately since it's not a surface color key // The $surface iterator only expands to actual surface color keys const backgroundVariant = background === 'transparent' ? undefined : background; viewStyles.useVariants({ background: backgroundVariant, radius, border, gap, padding, paddingVertical, paddingHorizontal, margin, marginVertical, marginHorizontal, }); // Call style as function to get theme-reactive styles /** @ts-ignore */ const webProps = getWebProps((viewStyles.view as any)({})); /** @ts-ignore */ const wrapperWebProps = getWebProps(viewStyles.scrollableWrapper); const mergedRef = useMergeRefs(ref, webProps.ref, layoutRef); // Flatten style array into a single object (style can be an array on RN) const flatStyle = flattenStyle(style); // When scrollable, render a wrapper + content structure // Wrapper: sizing and margin (positioning in parent layout) // Content: absolutely positioned with overflow:auto, visual styles (padding, background, border) if (scrollable) { // Split user styles: layout/sizing to wrapper, visual styles to content const styleObj = flatStyle; const { // Sizing - goes to wrapper width, height, minWidth, minHeight, maxWidth, maxHeight, // Flex properties - goes to wrapper flex, flexGrow, flexShrink, flexBasis, alignSelf, // Flex content alignment - goes to content (for aligning children) alignItems, justifyContent, // Margin - goes to wrapper (positioning in parent) margin, marginTop, marginRight, marginBottom, marginLeft, marginBlock, marginInline, // Everything else (padding, background, border, etc.) - goes to content ...contentStyles } = styleObj; // Determine flex behavior: // - If explicit flex is provided, use it // - If explicit width/height are provided (without flex), disable flex // - Otherwise, let Unistyles' flex:1 apply const hasExplicitSize = width !== undefined || height !== undefined; const flexValue = flex !== undefined ? flex : (hasExplicitSize ? 'none' : undefined); // Only include defined values in wrapper styles const wrapperUserStyles: React.CSSProperties = { ...(width !== undefined && { width }), ...(height !== undefined && { height }), ...(minWidth !== undefined && { minWidth }), ...(minHeight !== undefined && { minHeight }), ...(maxWidth !== undefined && { maxWidth }), ...(maxHeight !== undefined && { maxHeight }), ...(flexValue !== undefined && { flex: flexValue }), ...(flexGrow !== undefined && { flexGrow }), ...(flexShrink !== undefined && { flexShrink }), ...(flexBasis !== undefined && { flexBasis }), ...(alignSelf !== undefined && { alignSelf }), ...(margin !== undefined && { margin }), ...(marginTop !== undefined && { marginTop }), ...(marginRight !== undefined && { marginRight }), ...(marginBottom !== undefined && { marginBottom }), ...(marginLeft !== undefined && { marginLeft }), ...(marginBlock !== undefined && { marginBlock }), ...(marginInline !== undefined && { marginInline }), }; return (
{children}
); } return (
{children}
); }); View.displayName = 'View'; export default View;