diff --git a/src/registry/index.ts b/src/registry/index.ts index aef5bc6..f41f113 100644 --- a/src/registry/index.ts +++ b/src/registry/index.ts @@ -58,6 +58,7 @@ import { inputRegistry } from './input.js'; import { chartsRegistry } from './charts/index.js'; import { alertDialogRegistry } from './alert-dialog.js'; import { avoidKeyboardRegistry } from './avoid-keyboard.js'; +import { stackRegistry } from './stack.js'; export const REGISTRY: Record = { ...accordionRegistry, @@ -115,6 +116,7 @@ export const REGISTRY: Record = { ...hooksRegistry, ...themeRegistry, ...chartsRegistry, + ...stackRegistry, }; /// Helper functions for component registry diff --git a/src/registry/stack.ts b/src/registry/stack.ts new file mode 100644 index 0000000..ed34160 --- /dev/null +++ b/src/registry/stack.ts @@ -0,0 +1,39 @@ +// Registry configuration for stack (XStack, YStack, ZStack) layout components + +export const stackRegistry = { + // Main stack component + stack: { + name: 'stack', + description: + 'Layout primitives for horizontal (XStack), vertical (YStack), and overlay (ZStack) stacking with gap, alignment, and shorthand props.', + type: 'registry:ui', + dependencies: [], + registryDependencies: [], + hooks: [], + theme: [], + files: [ + { + type: 'registry:ui', + path: 'templates/components/ui/stack.tsx', + target: 'components/ui/stack.tsx', + }, + ], + }, + + // Default demo + 'stack-demo': { + name: 'stack-demo', + description: 'Basic examples demonstrating XStack, YStack, and ZStack usage', + type: 'registry:example', + registryDependencies: ['stack', 'view', 'text'], + hooks: [], + theme: [], + files: [ + { + type: 'registry:example', + path: 'templates/demo/stack/stack-demo.tsx', + target: 'components/demo/stack/stack-demo.tsx', + }, + ], + }, +}; diff --git a/templates/components/ui/stack.tsx b/templates/components/ui/stack.tsx new file mode 100644 index 0000000..801a918 --- /dev/null +++ b/templates/components/ui/stack.tsx @@ -0,0 +1,278 @@ +// stack.tsx +import * as React from "react"; +import { + View, + StyleSheet, + type ViewProps, + type ViewStyle, + type StyleProp, +} from "react-native"; +type Direction = "row" | "column"; + +export type BaseStackProps = Omit & { + /** Spacing between children (in dp). */ + gap?: number; + /** Reverse the main-axis direction (row-reverse/column-reverse). */ + reverse?: boolean; + /** Shorthand: center both axes. */ + center?: boolean; + /** Shorthand: fill available space (true → flex: 1). */ + flex?: number | boolean; + /** Container style. */ + style?: StyleProp; + + // Native padding properties + padding?: ViewStyle["padding"]; + paddingTop?: ViewStyle["paddingTop"]; + paddingBottom?: ViewStyle["paddingBottom"]; + paddingLeft?: ViewStyle["paddingLeft"]; + paddingRight?: ViewStyle["paddingRight"]; + paddingHorizontal?: ViewStyle["paddingHorizontal"]; + paddingVertical?: ViewStyle["paddingVertical"]; + + // Basic style props that can be passed directly + height?: ViewStyle["height"]; + width?: ViewStyle["width"]; + backgroundColor?: ViewStyle["backgroundColor"]; + borderTopWidth?: ViewStyle["borderTopWidth"]; + borderBottomWidth?: ViewStyle["borderBottomWidth"]; + borderLeftWidth?: ViewStyle["borderLeftWidth"]; + borderRightWidth?: ViewStyle["borderRightWidth"]; + borderWidth?: ViewStyle["borderWidth"]; + borderColor?: ViewStyle["borderColor"]; + borderTopColor?: ViewStyle["borderTopColor"]; + borderBottomColor?: ViewStyle["borderBottomColor"]; + borderLeftColor?: ViewStyle["borderLeftColor"]; + borderRightColor?: ViewStyle["borderRightColor"]; + borderRadius?: ViewStyle["borderRadius"]; + borderTopLeftRadius?: ViewStyle["borderTopLeftRadius"]; + borderTopRightRadius?: ViewStyle["borderTopRightRadius"]; + borderBottomLeftRadius?: ViewStyle["borderBottomLeftRadius"]; + borderBottomRightRadius?: ViewStyle["borderBottomRightRadius"]; + margin?: ViewStyle["margin"]; + marginTop?: ViewStyle["marginTop"]; + marginBottom?: ViewStyle["marginBottom"]; + marginLeft?: ViewStyle["marginLeft"]; + marginRight?: ViewStyle["marginRight"]; + marginHorizontal?: ViewStyle["marginHorizontal"]; + marginVertical?: ViewStyle["marginVertical"]; + minHeight?: ViewStyle["minHeight"]; + minWidth?: ViewStyle["minWidth"]; + maxHeight?: ViewStyle["maxHeight"]; + maxWidth?: ViewStyle["maxWidth"]; + opacity?: ViewStyle["opacity"]; + overflow?: ViewStyle["overflow"]; + position?: ViewStyle["position"]; + top?: ViewStyle["top"]; + bottom?: ViewStyle["bottom"]; + left?: ViewStyle["left"]; + right?: ViewStyle["right"]; + zIndex?: ViewStyle["zIndex"]; + alignItems?: ViewStyle["alignItems"]; + justifyContent?: ViewStyle["justifyContent"]; + +}; + +type InternalProps = BaseStackProps & { + __direction: Direction; +}; + +const BaseStack = React.forwardRef(function BaseStack( + { + __direction, + gap = 0, + reverse, + center, + flex, + style, + children, + // Native padding properties + padding, + paddingTop, + paddingBottom, + paddingLeft, + paddingRight, + paddingHorizontal, + paddingVertical, + // Basic style props + height, + width, + backgroundColor, + borderTopWidth, + borderBottomWidth, + borderLeftWidth, + borderRightWidth, + borderWidth, + borderColor, + borderTopColor, + borderBottomColor, + borderLeftColor, + borderRightColor, + borderRadius, + borderTopLeftRadius, + borderTopRightRadius, + borderBottomLeftRadius, + borderBottomRightRadius, + margin, + marginTop, + marginBottom, + marginLeft, + marginRight, + marginHorizontal, + marginVertical, + minHeight, + minWidth, + maxHeight, + maxWidth, + opacity, + overflow, + position, + top, + bottom, + left, + right, + zIndex, + alignItems, + justifyContent, + ...viewProps + }, + ref +) { + const dir = reverse + ? __direction === "row" + ? "row-reverse" + : "column-reverse" + : __direction; + + const resolvedFlex: number | undefined = + typeof flex === "boolean" ? (flex ? 1 : undefined) : flex; + + + // Direct style props + const directStyleProps: ViewStyle = { + height, + width, + backgroundColor, + borderTopWidth, + borderBottomWidth, + borderLeftWidth, + borderRightWidth, + borderWidth, + borderColor, + borderTopColor, + borderBottomColor, + borderLeftColor, + borderRightColor, + borderRadius, + borderTopLeftRadius, + borderTopRightRadius, + borderBottomLeftRadius, + borderBottomRightRadius, + margin, + marginTop, + marginBottom, + marginLeft, + marginRight, + marginHorizontal, + marginVertical, + minHeight, + minWidth, + maxHeight, + maxWidth, + opacity, + overflow, + position, + top, + bottom, + left, + right, + zIndex, + // Native padding properties + padding, + paddingTop, + paddingBottom, + paddingLeft, + paddingRight, + paddingHorizontal, + paddingVertical, + }; + + // Base container style + const containerStyle: ViewStyle = { + flexDirection: dir, + alignItems: center ? "center" : alignItems, + justifyContent: center ? "center" : justifyContent, + flex: resolvedFlex, + ...directStyleProps, + }; + + // Apply a gap if it's not 0 + if (gap != null) { + containerStyle.gap = gap; + } + + // Normalize children + const kids = React.Children.toArray(children).filter( + (c) => c !== null && c !== undefined && !!c + ); + + return ( + + {kids} + + ); +}); + +export type XStackProps = BaseStackProps; +export type YStackProps = BaseStackProps; +export type ZStackProps = BaseStackProps; + +export const XStack = React.forwardRef(function XStack( + props, + ref +) { + return ; +}); + +export const YStack = React.forwardRef(function YStack( + props, + ref +) { + return ; +}); + +export const ZStack = React.forwardRef(function ZStack( + { children, style, gap, ...rest }, + ref +) { + // Normalize children + const kids = React.Children.toArray(children).filter( + (c) => c !== null && c !== undefined && !!c + ); + + // Render: first child in normal flow, later children absolutely fill + return ( + + {kids.map((child, idx) => + idx === 0 ? ( + child as React.ReactElement + ) : ( + + {child} + + ) + )} + + ); +}); diff --git a/templates/demo/stack/stack-demo.tsx b/templates/demo/stack/stack-demo.tsx new file mode 100644 index 0000000..6a6f07f --- /dev/null +++ b/templates/demo/stack/stack-demo.tsx @@ -0,0 +1,35 @@ +import { View } from '@/components/ui/view'; +import { Text } from '@/components/ui/text'; +import { XStack, YStack, ZStack } from '@/components/ui/stack'; + +export default function StackDemo() { + return ( + + XStack (row) + + + + + + + YStack (column) + + + + + + + ZStack (overlay) + + + + + Overlay + + + + ); +}