CKSwift/ViewLayoutModifier.swift (135 lines of code) (raw):
/*
* Copyright (c) 2014-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
import Foundation
import ComponentKit
typealias ViewLayoutCenteringOptions = CenterLayoutComponent.CenteringOptions
typealias ViewLayoutCenteringSizingOptions = CenterLayoutComponent.SizingOptions
public struct ViewLayoutModifier<Inflatable: ComponentInflatable> : ComponentInflatable {
enum Directive {
case frame(ComponentSize)
case padding(top: Dimension, left: Dimension, bottom: Dimension, right: Dimension)
case ratio(CGFloat)
case center(centeringOptions: ViewLayoutCenteringOptions, sizingOptions: ViewLayoutCenteringSizingOptions)
case background(() -> Component)
case overlay(() -> Component)
fileprivate func build(_ content: Component) -> Component {
switch self {
case let .frame(size):
return SizingComponent(swiftSize: size.componentSize, component: content)
case let .padding(top, left, bottom, right):
return InsetComponent(swiftView: nil,
top: top.dimension,
left: left.dimension,
bottom: bottom.dimension,
right: right.dimension,
component: content)
case let .ratio(ratio):
return RatioLayoutComponent(ratio: ratio, swiftSize: nil, component: content)
case let .center(centeringOptions, sizingOptions):
return CenterLayoutComponent(centeringOptions: centeringOptions,
sizingOptions: sizingOptions,
child: content,
swiftSize: nil)
case let .background(background):
return BackgroundLayoutComponent(component: content, background: background())
case let .overlay(overlay):
return OverlayLayoutComponent(component: content, overlay: overlay())
}
}
}
let inflatable: Inflatable
let directive: Directive
// MARK: ComponentInflatable
public func inflateComponent(with model: SwiftComponentModel?) -> Component {
let inflatedTarget = inflatable.inflateComponent(with: nil)
return directive.build(inflatedTarget)
.inflateComponent(with: model)
}
}
extension ComponentInflatable {
public func frame(width: CGFloat? = nil,
height: CGFloat? = nil,
minWidth: CGFloat? = nil,
minHeight: CGFloat? = nil,
maxWidth: CGFloat? = nil,
maxHeight: CGFloat? = nil) -> ViewLayoutModifier<Self> {
ViewLayoutModifier(
inflatable: self,
directive: .frame(
ComponentSize(
width: width.map { .points($0) },
height: height.map { .points($0) },
minWidth: minWidth.map { .points($0) },
minHeight: minHeight.map { .points($0) },
maxWidth: maxWidth.map { .points($0) },
maxHeight: maxHeight.map { .points($0) })
)
)
}
public func relativeFrame(width: CGFloat? = nil,
height: CGFloat? = nil,
minWidth: CGFloat? = nil,
minHeight: CGFloat? = nil,
maxWidth: CGFloat? = nil,
maxHeight: CGFloat? = nil) -> ViewLayoutModifier<Self> {
ViewLayoutModifier(
inflatable: self,
directive: .frame(
ComponentSize(
width: width.map { .percent($0) },
height: height.map { .percent($0) },
minWidth: minWidth.map { .percent($0) },
minHeight: minHeight.map { .percent($0) },
maxWidth: maxWidth.map { .percent($0) },
maxHeight: maxHeight.map { .percent($0) })
)
)
}
public func overlay(@ComponentBuilder _ overlay: @escaping () -> Component) -> ViewLayoutModifier<Self> {
ViewLayoutModifier(inflatable: self, directive: .overlay(overlay))
}
public func background(@ComponentBuilder _ background: @escaping () -> Component) -> ViewLayoutModifier<Self> {
ViewLayoutModifier(inflatable: self, directive: .background(background))
}
// TODO: CenterLayoutComponent options shouldn't leak
public func center(centeringOptions: CenterLayoutComponent.CenteringOptions = [],
sizingOptions: CenterLayoutComponent.SizingOptions = []) -> ViewLayoutModifier<Self> {
ViewLayoutModifier(inflatable: self, directive: .center(centeringOptions: centeringOptions, sizingOptions: sizingOptions))
}
public func ratio(_ ratio: CGFloat) -> ViewLayoutModifier<Self> {
ViewLayoutModifier(inflatable: self, directive: .ratio(ratio))
}
public func padding(top: CGFloat = 0,
left: CGFloat = 0,
bottom: CGFloat = 0,
right: CGFloat = 0) -> ViewLayoutModifier<Self> {
ViewLayoutModifier(
inflatable: self,
directive: .padding(
top: .points(top),
left: .points(left),
bottom: .points(bottom),
right: .points(right)
)
)
}
public func padding(_ insets: UIEdgeInsets) -> ViewLayoutModifier<Self> {
padding(top: insets.top, left: insets.left, bottom: insets.bottom, right: insets.right)
}
public func padding(_ length: CGFloat = 0) -> ViewLayoutModifier<Self> {
padding(top: length, left: length, bottom: length, right: length)
}
public func relativePadding(top: CGFloat = 0,
left: CGFloat = 0,
bottom: CGFloat = 0,
right: CGFloat = 0) -> ViewLayoutModifier<Self> {
ViewLayoutModifier(
inflatable: self,
directive: .padding(
top: .percent(top),
left: .percent(left),
bottom: .percent(bottom),
right: .percent(right)
)
)
}
public func relativePadding(_ length: CGFloat = 0) -> ViewLayoutModifier<Self> {
relativePadding(top: length, left: length, bottom: length, right: length)
}
}