diff --git a/android/src/main/java/voltra/generated/ShortNames.kt b/android/src/main/java/voltra/generated/ShortNames.kt index 2503b98..43cea8c 100644 --- a/android/src/main/java/voltra/generated/ShortNames.kt +++ b/android/src/main/java/voltra/generated/ShortNames.kt @@ -16,7 +16,9 @@ object ShortNames { private val shortToName: Map = mapOf( "ap" to "absolutePosition", + "ai" to "alignItems", "al" to "alignment", + "as" to "alignSelf", "ar" to "aspectRatio", "an" to "assetName", "bkg" to "background", @@ -53,8 +55,11 @@ object ShortNames { "fsh" to "fixedSizeHorizontal", "fsv" to "fixedSizeVertical", "fl" to "flex", + "fb" to "flexBasis", + "fdir" to "flexDirection", "fg" to "flexGrow", "fgw" to "flexGrowWidth", + "fk" to "flexShrink", "fnt" to "font", "ff" to "fontFamily", "fs" to "fontSize", @@ -62,6 +67,7 @@ object ShortNames { "fw" to "fontWeight", "fgs" to "foregroundStyle", "f" to "frame", + "g" to "gap", "gs" to "gaugeStyle", "ge" to "glassEffect", "h" to "height", @@ -70,8 +76,10 @@ object ShortNames { "ic" to "icon", "id" to "id", "it" to "italic", + "jc" to "justifyContent", "kern" to "kerning", "lbl" to "label", + "ly" to "layout", "lp" to "layoutPriority", "l" to "left", "ls" to "letterSpacing", diff --git a/data/components.json b/data/components.json index 9e8e858..0fe879b 100644 --- a/data/components.json +++ b/data/components.json @@ -116,6 +116,14 @@ "bottom": "b", "flex": "fl", "flexGrow": "fg", + "flexShrink": "fk", + "flexBasis": "fb", + "alignItems": "ai", + "alignSelf": "as", + "justifyContent": "jc", + "layout": "ly", + "flexDirection": "fdir", + "gap": "g", "lineHeight": "lh", "textAlign": "ta", "textDecorationLine": "tdl", @@ -764,18 +772,19 @@ "swiftAvailability": "iOS 13.0, macOS 10.15", "hasChildren": true, "parameters": { - "spacing": { - "type": "number", - "default": 0, - "optional": true, - "description": "Spacing between children" - }, "alignment": { "type": "string", "default": "center", "optional": true, "enum": ["leading", "center", "trailing"], "description": "Horizontal alignment" + }, + "layout": { + "type": "string", + "default": "stack", + "optional": true, + "enum": ["stack", "flex"], + "description": "Layout mode. 'stack' uses native SwiftUI stacks. 'flex' uses RN-like flexbox." } } }, @@ -785,21 +794,29 @@ "swiftAvailability": "iOS 13.0, macOS 10.15", "hasChildren": true, "parameters": { - "spacing": { - "type": "number", - "default": 0, - "optional": true, - "description": "Spacing between children" - }, "alignment": { "type": "string", "default": "center", "optional": true, "enum": ["top", "center", "bottom"], "description": "Vertical alignment" + }, + "layout": { + "type": "string", + "default": "stack", + "optional": true, + "enum": ["stack", "flex"], + "description": "Layout mode. 'stack' uses native SwiftUI stacks. 'flex' uses RN-like flexbox." } } }, + { + "name": "View", + "description": "Flex container with configurable direction. Always uses flexbox layout.", + "swiftAvailability": "iOS 16.0, macOS 13.0", + "hasChildren": true, + "parameters": {} + }, { "name": "ZStack", "description": "Depth-based stack container", diff --git a/docs/FLEXBOX.md b/docs/FLEXBOX.md new file mode 100644 index 0000000..d43b837 --- /dev/null +++ b/docs/FLEXBOX.md @@ -0,0 +1,366 @@ +# Flexbox Layout Implementation + +## Overview + +Voltra implements a React Native-compatible flexbox layout system for iOS using SwiftUI's `Layout` protocol. This provides precise control over layout behavior and matches RN's flexbox semantics more closely than SwiftUI's native layout system. + +## Architecture + +### Core Components + +#### 1. `VoltraFlexStackLayout` (Layout Protocol) + +The main layout engine that implements single-axis flexbox. It: + +- Computes child sizes based on flex properties +- Distributes free space using `flexGrow` and `flexShrink` +- Positions children according to `justifyContent` and `alignItems` +- Handles gaps, padding, and margins + +**Location:** `ios/ui/Layout/VoltraFlexStackLayout.swift` + +#### 2. `FlexItemLayoutKey` + `FlexItemValues` + +SwiftUI's `LayoutValueKey` mechanism for passing flex properties from child to parent: + +```swift +struct FlexItemValues { + var flexGrow: CGFloat = 0 + var flexShrink: CGFloat = 0 + var flexBasis: SizeValue? + var width: SizeValue? + var height: SizeValue? + // ... other properties +} +``` + +Children set these via `.layoutValue(key: FlexItemLayoutKey.self, value: ...)` and the parent layout reads them. + +**Location:** `ios/ui/Style/FlexEnvironment.swift` + +#### 3. Container Components + +- **`VoltraFlexView`** - The `` component (dynamic flexDirection) +- **`VoltraVStack`** - Vertical stack (flexDirection: column) +- **`VoltraHStack`** - Horizontal stack (flexDirection: row) + +All use `VoltraFlexStackLayout` when in flex mode. + +#### 4. Style System Integration + +- **`StyleConverter`** - Parses JSON styles, applies defaults +- **`CompositeStyleModifier`** - Applies styles to children, sets `FlexItemLayoutKey` +- **`FlexContainerStyleModifier`** - Applies container styles AND sets `FlexItemLayoutKey` for nested containers +- **`isInFlexContainer` environment variable** - Switches between legacy and flex layout paths + +## How It Works + +### 1. Style Parsing Flow + +``` +JSON from RN + ↓ +ShortNames.expand ("fg" → "flexGrow") + ↓ +StyleConverter.parseLayout + • Parses flex/flexGrow/flexShrink from JSON + • Applies defaults (0 for grow/shrink) + • Handles flex shorthand (flex: 1 → grow=1, shrink=1, basis=0) + ↓ +LayoutStyle (non-optional flexGrow/flexShrink with defaults applied) +``` + +### 2. Layout Flow + +``` +VoltraFlexView creates VoltraFlexStackLayout + ↓ +Children rendered with .environment(\.isInFlexContainer, true) + ↓ +Each child applies FlexContainerStyleModifier or CompositeStyleModifier + • Sets .layoutValue(key: FlexItemLayoutKey.self, value: FlexItemValues(...)) + ↓ +VoltraFlexStackLayout.sizeThatFits + • Reads FlexItemLayoutKey from each child + • Computes base sizes (flexBasis or intrinsic) + • Distributes free space via flexGrow/flexShrink + • Returns container size + ↓ +VoltraFlexStackLayout.placeSubviews + • Positions children based on justifyContent + • Aligns children on cross-axis via alignItems/alignSelf + • Places each child with computed size +``` + +### 3. Flex Algorithm (Simplified) + +**Phase 1: Compute Base Sizes** + +- For each child, determine `flexBasis`: + - If `flexBasis` is set: use it + - If `width`/`height` (depending on axis): use it + - Otherwise: measure intrinsic size +- Apply `minWidth`/`maxWidth`/`minHeight`/`maxHeight` constraints + +**Phase 2: Distribute Free Space** + +```swift +totalUsed = sum(baseSizes) + sum(margins) + gaps +freeSpace = availableSpace - totalUsed + +if freeSpace > 0 { + // Grow: distribute proportionally by flexGrow + for child with flexGrow > 0 { + extraSize = freeSpace * (child.flexGrow / totalFlexGrow) + finalSize = baseSize + extraSize + } +} else if freeSpace < 0 { + // Shrink: reduce proportionally by flexShrink + for child with flexShrink > 0 { + reduction = abs(freeSpace) * (child.flexShrink / totalFlexShrink) + finalSize = baseSize - reduction + } +} +``` + +**Phase 3: Position on Main Axis** +Based on `justifyContent`: + +- `flex-start`: Start at container's leading edge +- `flex-end`: Start at trailing edge +- `center`: Center items +- `space-between`: Distribute items evenly, no space at edges +- `space-around`: Distribute with half-space at edges +- `space-evenly`: Equal space between all items and edges + +**Phase 4: Position on Cross Axis** +Based on `alignItems` (or per-child `alignSelf`): + +- `flex-start`: Align to leading edge +- `flex-end`: Align to trailing edge +- `center`: Center on cross-axis +- `stretch`: Expand to fill cross-axis (if no explicit size) + +## Differences from React Native Flexbox + +### ✅ Supported (RN-Compatible) + +| Feature | Status | Notes | +| --------------------- | ------ | ----------------------------------------------------------------------------------------------- | +| `flexDirection` | ✅ | `row`, `column` | +| `flexGrow` | ✅ | Full support | +| `flexShrink` | ✅ | Full support | +| `flexBasis` | ✅ | `auto`, fixed, `fill`, `wrap` | +| `flex` shorthand | ✅ | `flex: 1` → `grow=1, shrink=1, basis=0` | +| `justifyContent` | ✅ | All values: `flex-start`, `center`, `flex-end`, `space-between`, `space-around`, `space-evenly` | +| `alignItems` | ✅ | `flex-start`, `center`, `flex-end`, `stretch` | +| `alignSelf` | ✅ | Per-child override of `alignItems` | +| `gap` | ✅ | Spacing between children | +| `padding` | ✅ | Inner spacing (handled by flex engine) | +| `margin` | ✅ | Outer spacing on children | +| `width`/`height` | ✅ | Fixed sizes, `fill` (`100%`), `wrap` (`auto`) | +| `minWidth`/`maxWidth` | ✅ | Size constraints | +| `aspectRatio` | ✅ | Maintain aspect ratio | + +### ❌ Not Supported + +| Feature | Status | Workaround | +| --------------------------------- | ------ | ------------------------------ | +| `flexWrap` | ❌ | Single-axis only - no wrapping | +| `flexDirection: 'row-reverse'` | ❌ | Not implemented | +| `flexDirection: 'column-reverse'` | ❌ | Not implemented | +| `alignContent` | ❌ | Only relevant with wrapping | + +### 🔄 Implementation Differences + +1. **No Multi-Line Wrapping** + + - RN's `flexWrap: 'wrap'` is not supported + - All items remain on a single axis + - Use nested containers for multi-line layouts + +2. **Environment-Based Mode Switching** + + - Uses `isInFlexContainer` environment variable + - Legacy layout still exists for backward compatibility + - Children automatically switch to flex mode when inside flex container + +3. **Padding Handling** + + - Padding is handled by the flex layout engine (container padding) + - Not applied as SwiftUI `.padding()` modifier in flex mode + - This matches RN's box model more closely + +4. **Two-Phase Layout** + - SwiftUI Layout protocol requires two phases: + 1. `sizeThatFits`: Compute ideal size + 2. `placeSubviews`: Position children + - RN uses Yoga which has a different algorithm structure + +## Enabling Flexbox by Default + +### Current State + +Flexbox is **partially enabled**: + +- `` components use flexbox (`VoltraFlexView`) +- `` and `` use flexbox when `layout="flex"` prop is set +- Legacy layout (`.frame()` + `.layoutPriority()`) still exists for backward compatibility + +### Migration Checklist + +To make flexbox the default and remove the legacy system: + +#### 1. **Remove Legacy Layout Path** ⚠️ Breaking Change + +- [ ] Remove `LayoutModifier` (the legacy `.frame()` based system) +- [ ] Remove the `else` branch in `CompositeStyleModifier` (non-flex path) +- [ ] Remove `layoutPriority` property from `LayoutStyle` +- [ ] Remove `flex` shorthand property (superseded by `flexGrow`/`flexShrink`) + +**Files to modify:** + +- `ios/ui/Style/LayoutStyle.swift` - Remove `LayoutModifier` struct +- `ios/ui/Style/CompositeStyle.swift` - Remove legacy path +- `ios/ui/Style/StyleConverter.swift` - Remove `layoutPriority` parsing + +#### 2. **Enable Flex by Default in VStack/HStack** + +- [ ] Remove `layout="flex"` parameter requirement +- [ ] Always use `VoltraFlexStackLayout` in VStack/HStack +- [ ] Remove legacy VStack/HStack rendering paths + +**Files to modify:** + +- `ios/ui/Views/VoltraVStack.swift` - Remove `legacyBody`, always use `flexBody` +- `ios/ui/Views/VoltraHStack.swift` - Remove `legacyBody`, always use `flexBody` +- `data/components.json` - Remove `layout` prop from VStack/HStack schemas + +#### 3. **Remove Environment Variable** + +- [ ] Remove `isInFlexContainer` environment variable (always true) +- [ ] Simplify `CompositeStyleModifier` to always use flex child path + +**Files to modify:** + +- `ios/ui/Style/FlexEnvironment.swift` - Remove `IsInFlexContainerKey` +- `ios/ui/Style/CompositeStyle.swift` - Remove conditional logic +- All container components - Remove `.environment(\.isInFlexContainer, true)` calls + +#### 4. **Testing & Validation** + +- [ ] Test all example screens with flexbox-only layout +- [ ] Verify nested flex containers work correctly +- [ ] Test edge cases: zero-sized containers, deeply nested layouts +- [ ] Check performance with complex layouts (100+ views) +- [ ] Verify `alignItems: stretch` works correctly +- [ ] Test with dynamic content (text wrapping, images) + +#### 5. **Documentation & Migration Guide** + +- [ ] Update main README with flexbox-only approach +- [ ] Document breaking changes in CHANGELOG +- [ ] Provide migration guide for users relying on legacy layout +- [ ] Add examples showing flexbox patterns + +### Potential Issues + +1. **Breaking Changes for Existing Code** + + - Code relying on `layoutPriority` will break + - VStack/HStack without explicit sizing may render differently + - Need to provide clear migration path + +2. **Performance Considerations** + + - Flex layout is more expensive than simple `.frame()` calls + - May need optimization for deeply nested layouts + - Consider caching layout calculations + +3. **Missing Features** + + - No `flexWrap` support (users may expect it) + - No reverse directions (need to document workarounds) + +4. **SwiftUI Interop** + - Need to ensure flexbox plays well with native SwiftUI views + - May need special handling for SwiftUI components in flex containers + +## Future Enhancements + +### High Priority + +1. **FlexWrap Support** - Enable multi-line layouts +2. **Reverse Directions** - `row-reverse`, `column-reverse` +3. **Performance Optimization** - Layout caching, incremental updates + +### Medium Priority + +4. **Absolute Positioning** - Children with `position: absolute` +5. **Percentage-Based Sizing** - `width: "50%"`, `height: "25%"` +6. **Advanced Alignment** - `baseline` alignment for text + +### Low Priority + +7. **Animation Support** - Smooth transitions when flex properties change +8. **Debug Visualization** - Visual overlay showing flex boxes +9. **Layout Inspector** - Tool to debug flex layout issues + +## Best Practices + +### DO ✅ + +- Use `flexGrow` and `flexShrink` for responsive layouts +- Prefer `gap` over margin for spacing between items +- Use `alignSelf` to override alignment for specific children +- Set explicit `flexBasis` or `width`/`height` when you know the size +- Use `minWidth`/`maxWidth` to constrain flexible items + +### DON'T ❌ + +- Don't mix legacy and flex layouts in the same container +- Don't rely on `layoutPriority` (it's legacy) +- Don't use large `flexGrow` values (1-3 is usually sufficient) +- Don't nest many flex containers without testing performance +- Don't expect `flexWrap` to work (not implemented) + +## Debugging + +### Common Issues + +**1. Child not growing despite `flexGrow: 1`** + +- ✅ Check that the container has defined size (`width: '100%'` or `height: '100%'`) +- ✅ Ensure child doesn't have explicit size that prevents growth + +**2. Items not aligned correctly** + +- Check `alignItems` on container vs `alignSelf` on child +- Verify you're using the right alignment for the axis (main vs cross) + +**3. Unexpected spacing** + +- Check if `gap` is set on container +- Verify margins on children +- Ensure `justifyContent` is what you expect + +**4. Layout not updating** + +- Flex layout may not react to content changes +- Force re-render by changing container key + +## Related Files + +- `ios/ui/Layout/VoltraFlexStackLayout.swift` - Main layout engine +- `ios/ui/Layout/FlexContainerHelper.swift` - Container utilities +- `ios/ui/Style/FlexEnvironment.swift` - Flex item values +- `ios/ui/Style/CompositeStyle.swift` - Child style application +- `ios/ui/Views/VoltraFlexView.swift` - View component +- `ios/ui/Views/VoltraVStack.swift` - VStack component +- `ios/ui/Views/VoltraHStack.swift` - HStack component + +## Resources + +- [React Native Flexbox Guide](https://reactnative.dev/docs/flexbox) +- [SwiftUI Layout Protocol](https://developer.apple.com/documentation/swiftui/layout) +- [CSS Flexbox Spec](https://www.w3.org/TR/css-flexbox-1/) diff --git a/example/app/testing-grounds/flex-playground.tsx b/example/app/testing-grounds/flex-playground.tsx new file mode 100644 index 0000000..c15b990 --- /dev/null +++ b/example/app/testing-grounds/flex-playground.tsx @@ -0,0 +1,5 @@ +import FlexPlaygroundScreen from '~/screens/testing-grounds/flex-playground/FlexPlaygroundScreen' + +export default function FlexPlaygroundIndex() { + return +} diff --git a/example/package-lock.json b/example/package-lock.json index dcc67f1..5616587 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -2541,9 +2541,9 @@ } }, "node_modules/@jest/core/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -2598,9 +2598,9 @@ } }, "node_modules/@jest/core/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -2778,9 +2778,9 @@ } }, "node_modules/@jest/core/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "peer": true, @@ -3006,9 +3006,9 @@ } }, "node_modules/@jest/globals/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -3039,9 +3039,9 @@ } }, "node_modules/@jest/globals/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -3273,9 +3273,9 @@ } }, "node_modules/@jest/reporters/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -3316,9 +3316,9 @@ } }, "node_modules/@jest/reporters/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -3477,9 +3477,9 @@ } }, "node_modules/@jest/reporters/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "peer": true, @@ -3586,9 +3586,9 @@ } }, "node_modules/@jest/snapshot-utils/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -3727,17 +3727,17 @@ } }, "node_modules/@jest/test-sequencer/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true }, "node_modules/@jest/test-sequencer/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -7119,9 +7119,9 @@ } }, "node_modules/expect/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -7141,9 +7141,9 @@ } }, "node_modules/expect/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -8574,17 +8574,17 @@ } }, "node_modules/jest-changed-files/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true }, "node_modules/jest-changed-files/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -8735,9 +8735,9 @@ } }, "node_modules/jest-circus/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -8768,9 +8768,9 @@ } }, "node_modules/jest-circus/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -8940,9 +8940,9 @@ } }, "node_modules/jest-cli/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -8976,9 +8976,9 @@ } }, "node_modules/jest-cli/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -9212,9 +9212,9 @@ } }, "node_modules/jest-config/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -9335,9 +9335,9 @@ } }, "node_modules/jest-config/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -9571,9 +9571,9 @@ } }, "node_modules/jest-config/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "peer": true, @@ -9662,9 +9662,9 @@ } }, "node_modules/jest-diff/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -9766,9 +9766,9 @@ } }, "node_modules/jest-each/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -9788,9 +9788,9 @@ } }, "node_modules/jest-each/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -9934,9 +9934,9 @@ } }, "node_modules/jest-leak-detector/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -10003,9 +10003,9 @@ } }, "node_modules/jest-matcher-utils/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -10184,9 +10184,9 @@ } }, "node_modules/jest-resolve/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -10220,9 +10220,9 @@ } }, "node_modules/jest-resolve/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -10510,9 +10510,9 @@ } }, "node_modules/jest-runner/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -10578,9 +10578,9 @@ } }, "node_modules/jest-runner/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -10794,9 +10794,9 @@ } }, "node_modules/jest-runner/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "peer": true, @@ -10996,9 +10996,9 @@ } }, "node_modules/jest-runtime/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -11050,9 +11050,9 @@ } }, "node_modules/jest-runtime/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -11227,9 +11227,9 @@ } }, "node_modules/jest-runtime/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "peer": true, @@ -11369,9 +11369,9 @@ } }, "node_modules/jest-snapshot/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true @@ -11412,9 +11412,9 @@ } }, "node_modules/jest-snapshot/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -11573,9 +11573,9 @@ } }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "peer": true, @@ -11732,17 +11732,17 @@ } }, "node_modules/jest-watcher/node_modules/@sinclair/typebox": { - "version": "0.34.47", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.47.tgz", - "integrity": "sha512-ZGIBQ+XDvO5JQku9wmwtabcVTHJsgSWAHYtVuM9pBNNR5E88v6Jcj/llpmsjivig5X8A8HHOb4/mbEKPS5EvAw==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT", "peer": true }, "node_modules/jest-watcher/node_modules/ci-info": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.1.tgz", - "integrity": "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", + "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "dev": true, "funding": [ { @@ -12381,9 +12381,9 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", "peer": true, diff --git a/example/screens/live-activities/LiveActivitiesScreen.tsx b/example/screens/live-activities/LiveActivitiesScreen.tsx index a97bf44..b32fb2c 100644 --- a/example/screens/live-activities/LiveActivitiesScreen.tsx +++ b/example/screens/live-activities/LiveActivitiesScreen.tsx @@ -3,9 +3,9 @@ import React, { useCallback, useMemo, useRef, useState } from 'react' import { ScrollView, StyleSheet, Text, View } from 'react-native' import { endAllLiveActivities } from 'voltra/client' +import { ActiveWidgetsIOSCard } from '~/components/ActiveWidgetsIOSCard' import { Button } from '~/components/Button' import { Card } from '~/components/Card' -import { ActiveWidgetsIOSCard } from '~/components/ActiveWidgetsIOSCard' import { NotificationsCard } from '~/components/NotificationsCard' import BasicLiveActivity from '~/screens/live-activities/BasicLiveActivity' import CompassLiveActivity from '~/screens/live-activities/CompassLiveActivity' diff --git a/example/screens/testing-grounds/TestingGroundsScreen.tsx b/example/screens/testing-grounds/TestingGroundsScreen.tsx index c546097..4fcbcfc 100644 --- a/example/screens/testing-grounds/TestingGroundsScreen.tsx +++ b/example/screens/testing-grounds/TestingGroundsScreen.tsx @@ -48,6 +48,13 @@ const TESTING_GROUNDS_SECTIONS = [ 'Explore all available Voltra components including Button, Text, VStack, HStack, ZStack, Image, and more.', route: '/testing-grounds/components', }, + { + id: 'flex-playground', + title: 'Flex Layout Playground', + description: + 'Interactive playground for experimenting with flex layout properties. Test alignItems, justifyContent, flexDirection, spacing, and padding with live visual feedback.', + route: '/testing-grounds/flex-playground', + }, { id: 'image-preloading', title: 'Image Preloading', diff --git a/example/screens/testing-grounds/flex-playground/FlexPlaygroundScreen.tsx b/example/screens/testing-grounds/flex-playground/FlexPlaygroundScreen.tsx new file mode 100644 index 0000000..50a34c8 --- /dev/null +++ b/example/screens/testing-grounds/flex-playground/FlexPlaygroundScreen.tsx @@ -0,0 +1,240 @@ +import { Link } from 'expo-router' +import React, { useState } from 'react' +import { ScrollView, StyleSheet, Text, View } from 'react-native' +import { Voltra } from 'voltra' +import { VoltraView } from 'voltra/client' + +import { Button } from '~/components/Button' +import { Card } from '~/components/Card' + +type FlexDirection = 'row' | 'column' +type AlignItems = 'flex-start' | 'center' | 'flex-end' | 'stretch' +type JustifyContent = 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly' + +const FLEX_DIRECTION_OPTIONS: FlexDirection[] = ['column', 'row'] +const ALIGN_ITEMS_OPTIONS: AlignItems[] = ['flex-start', 'center', 'flex-end', 'stretch'] +const JUSTIFY_CONTENT_OPTIONS: JustifyContent[] = [ + 'flex-start', + 'center', + 'flex-end', + 'space-between', + 'space-around', + 'space-evenly', +] + +const FLEX_DIRECTION_LABELS: Record = { + column: 'Column', + row: 'Row', +} + +const ALIGN_ITEMS_LABELS: Record = { + 'flex-start': 'flex-start', + center: 'center', + 'flex-end': 'flex-end', + stretch: 'stretch', +} + +const JUSTIFY_CONTENT_LABELS: Record = { + 'flex-start': 'flex-start', + center: 'center', + 'flex-end': 'flex-end', + 'space-between': 'space-between', + 'space-around': 'space-around', + 'space-evenly': 'space-evenly', +} + +export default function FlexPlaygroundScreen() { + const [flexDirection, setFlexDirection] = useState('column') + const [alignItems, setAlignItems] = useState('stretch') + const [justifyContent, setJustifyContent] = useState('flex-start') + const [gap, setGap] = useState(8) + const [containerPadding, setContainerPadding] = useState(16) + + const cycleFlexDirection = () => { + const currentIndex = FLEX_DIRECTION_OPTIONS.indexOf(flexDirection) + const nextIndex = (currentIndex + 1) % FLEX_DIRECTION_OPTIONS.length + setFlexDirection(FLEX_DIRECTION_OPTIONS[nextIndex]) + } + + const cycleAlignItems = () => { + const currentIndex = ALIGN_ITEMS_OPTIONS.indexOf(alignItems) + const nextIndex = (currentIndex + 1) % ALIGN_ITEMS_OPTIONS.length + setAlignItems(ALIGN_ITEMS_OPTIONS[nextIndex]) + } + + const cycleJustifyContent = () => { + const currentIndex = JUSTIFY_CONTENT_OPTIONS.indexOf(justifyContent) + const nextIndex = (currentIndex + 1) % JUSTIFY_CONTENT_OPTIONS.length + setJustifyContent(JUSTIFY_CONTENT_OPTIONS[nextIndex]) + } + + const increaseGap = () => setGap((prev) => Math.min(prev + 4, 32)) + const decreaseGap = () => setGap((prev) => Math.max(prev - 4, 0)) + + const increasePadding = () => setContainerPadding((prev) => Math.min(prev + 4, 32)) + const decreasePadding = () => setContainerPadding((prev) => Math.max(prev - 4, 0)) + + return ( + + + Flex Layout Playground + + Experiment with flex properties using the new View component with dynamic flexDirection. + + + {/* Controls */} + + Controls + + {/* Flex Direction */} + + Flex Direction: +