Skip to content

Commit

Permalink
Add ItemOffset component
Browse files Browse the repository at this point in the history
  • Loading branch information
lkzhao committed Jul 31, 2024
1 parent 1395a85 commit 0a624ce
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 9 deletions.
19 changes: 12 additions & 7 deletions Sources/UIComponent/Components/Layout/Other/Offset.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,29 @@ public struct DynamicOffset: Component {
}
}

struct OffsetRenderNode: RenderNode {
typealias View = UIView
public struct OffsetRenderNode: RenderNode {
public typealias View = UIView

let content: any RenderNode
let offset: CGPoint
public let content: any RenderNode
public let offset: CGPoint

public init(content: any RenderNode, offset: CGPoint) {
self.content = content
self.offset = offset
}

/// The size of the render node, adjusted for the insets.
var size: CGSize {
public var size: CGSize {
content.size
}

/// The content render nodes of this render node.
var children: [any RenderNode] {
public var children: [any RenderNode] {
[content]
}

/// The positions of the content render nodes within this render node.
var positions: [CGPoint] {
public var positions: [CGPoint] {
[offset]
}
}
55 changes: 55 additions & 0 deletions Sources/UIComponent/Components/Layout/Stack/StackItemOffset.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Created by Luke Zhao on 7/29/24.

import UIKit
import simd

public struct VStackItemOffset: Component {
public let offset: CGFloat
public let stack: VStack
public init(offset: CGFloat, stack: VStack) {
self.offset = offset
self.stack = stack
}
public func layout(_ constraint: Constraint) -> some RenderNode {
let renderNode = stack.layout(constraint)
guard !renderNode.children.isEmpty else { return OffsetRenderNode(content: renderNode, offset: .zero) }
let previousIndex = Int(offset).clamp(0, stack.children.count - 1)
let nextIndex = min(previousIndex + 1, stack.children.count - 1)
let previousPosition = renderNode.positions[previousIndex].y
let nextPosition = nextIndex == previousIndex ? renderNode.positions[previousIndex].y + renderNode.children[previousIndex].size.height + stack.spacing : renderNode.positions[nextIndex].y
let interpolatedOffset = simd_mix(previousPosition, nextPosition, offset - CGFloat(previousIndex))
return OffsetRenderNode(content: renderNode, offset: CGPoint(x: 0, y: -interpolatedOffset))
}
}

public extension VStack {
func offsetByItem(_ offset: CGFloat) -> some Component {
VStackItemOffset(offset: offset, stack: self)
}
}

public struct HStackItemOffset: Component {
public let offset: CGFloat
public let stack: HStack
public init(offset: CGFloat, stack: HStack) {
self.offset = offset
self.stack = stack
}
public func layout(_ constraint: Constraint) -> some RenderNode {
let renderNode = stack.layout(constraint)
guard !renderNode.children.isEmpty else { return OffsetRenderNode(content: renderNode, offset: .zero) }
let previousIndex = Int(offset).clamp(0, stack.children.count - 1)
let nextIndex = min(previousIndex + 1, stack.children.count - 1)
let previousPosition = renderNode.positions[previousIndex].x
let nextPosition = nextIndex == previousIndex ? renderNode.positions[previousIndex].x + renderNode.children[previousIndex].size.width + stack.spacing : renderNode.positions[nextIndex].x
let interpolatedOffset = simd_mix(previousPosition, nextPosition, offset - CGFloat(previousIndex))
return OffsetRenderNode(content: renderNode, offset: CGPoint(x: -interpolatedOffset, y: 0))
}
}

public extension HStack {
func offsetByItem(_ offset: CGFloat) -> some Component {
HStackItemOffset(offset: offset, stack: self)
}
}

3 changes: 1 addition & 2 deletions Sources/UIComponent/Core/Model/RenderNode/RenderNode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ extension RenderNode {
let frame = adjustVisibleFrame(frame: frame)
let children = visibleChildren(in: frame)
for child in children {
let childFrame = CGRect(origin: child.position, size: child.renderNode.size)
let childVisibleFrame = frame.intersection(childFrame) - child.position
let childVisibleFrame = frame - child.position
let childRenderables = child.renderNode._visibleRenderables(in: childVisibleFrame).map {
Renderable(id: $0.renderNode.id ?? "item-\(child.index)-\($0.id)", frame: $0.frame + child.position, renderNode: $0.renderNode)
}
Expand Down

0 comments on commit 0a624ce

Please sign in to comment.