Add NavigationItem host component (#27)
Resolves #22 * Add StackControllerItem host component * Move {Stack,Navigation}Presenter, related changes * Update StandardComponents doc with new name * Fix styling after view controller behavior change * Remove style from StackView.Props.init * Add `viewDidLoad` comment to UIViewComponent * Fix tests not compiling * Implement basic UIHostComponent for NavigationItem * Implement prefersLargeTitles, test item titles * Remove unused public func isSubtypeOf<T, U, V, W> * Remove unused NavigationItemBox, fix formatting * Remove missing file from the project
This commit is contained in:
parent
dfe658fe5b
commit
e413b06683
|
@ -8,7 +8,7 @@
|
|||
|
||||
import Gluon
|
||||
|
||||
struct StackModal: PureLeafComponent {
|
||||
struct NavigationModal: PureLeafComponent {
|
||||
struct Props: Equatable {
|
||||
let isPresented: State<Bool>
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ struct StackModal: PureLeafComponent {
|
|||
static func render(props: Props) -> AnyNode {
|
||||
return props.isPresented.value ?
|
||||
ModalPresenter.node(
|
||||
StackPresenter<NavRouter>.node(
|
||||
NavigationPresenter<NavRouter>.node(
|
||||
.init(
|
||||
initial: .first,
|
||||
prefersLargeTitles: true,
|
||||
routerProps: .init(
|
||||
onPress: Handler { props.isPresented.set(false) }
|
||||
)
|
||||
|
@ -44,11 +45,15 @@ struct SimpleModal: LeafComponent {
|
|||
let backgroundColor = hooks.state(0)
|
||||
|
||||
return props.isPresented.value ? ModalPresenter.node(
|
||||
View.node(.init(Style(backgroundColor: colors[backgroundColor.value].0)),
|
||||
View.node(
|
||||
.init(Style(
|
||||
backgroundColor: colors[backgroundColor.value].0,
|
||||
Edges.equal(to: .parent)
|
||||
)),
|
||||
StackView.node(.init(
|
||||
axis: .vertical,
|
||||
distribution: .fillEqually,
|
||||
Style(Edges.equal(to: .parent))
|
||||
Edges.equal(to: .parent)
|
||||
), [
|
||||
Button.node(.init(
|
||||
onPress: Handler { props.isPresented.set(false) }
|
||||
|
@ -58,7 +63,8 @@ struct SimpleModal: LeafComponent {
|
|||
valueHandler: Handler(backgroundColor.set)),
|
||||
colors.map { $0.1 }
|
||||
),
|
||||
]))
|
||||
])
|
||||
)
|
||||
) : Null.node()
|
||||
}
|
||||
}
|
||||
|
@ -78,12 +84,15 @@ struct TableModal: PureLeafComponent {
|
|||
}
|
||||
|
||||
static func render(props: Props) -> AnyNode {
|
||||
let list = ListView<ListProvider>.node(.init(singleSection: [1, 2, 3]))
|
||||
let list = ListView<ListProvider>.node(.init(
|
||||
singleSection: [1, 2, 3],
|
||||
Style(Edges.equal(to: .parent))
|
||||
))
|
||||
return props.isPresented.value ? ModalPresenter.node(list) : Null.node()
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstrainModal: LeafComponent {
|
||||
struct ConstraintModal: LeafComponent {
|
||||
struct Props: Equatable {
|
||||
let isPresented: State<Bool>
|
||||
}
|
||||
|
@ -93,11 +102,11 @@ struct ConstrainModal: LeafComponent {
|
|||
|
||||
return props.isPresented.value ? ModalPresenter.node(
|
||||
View.node(
|
||||
.init(Style(backgroundColor: .white)),
|
||||
.init(Style(backgroundColor: .white, Edges.equal(to: .parent))),
|
||||
StackView.node(.init(
|
||||
axis: .vertical,
|
||||
distribution: .fillEqually,
|
||||
Style(Edges.equal(to: .parent))
|
||||
Edges.equal(to: .parent)
|
||||
), [
|
||||
Button.node(.init(
|
||||
onPress: Handler { props.isPresented.set(false) }
|
||||
|
@ -136,11 +145,11 @@ struct DatePickerModal: LeafComponent {
|
|||
let formattedDate = dateFormatter.string(from: date.value)
|
||||
return props.isPresented.value ? ModalPresenter.node(
|
||||
View.node(
|
||||
.init(Style(backgroundColor: .white)),
|
||||
.init(Style(backgroundColor: .white, Edges.equal(to: .parent))),
|
||||
StackView.node(.init(
|
||||
axis: .vertical,
|
||||
distribution: .fillEqually,
|
||||
Style(Edges.equal(to: .parent))
|
||||
Edges.equal(to: .parent)
|
||||
), [
|
||||
Button.node(.init(
|
||||
onPress: Handler { props.isPresented.set(false) }
|
||||
|
@ -180,11 +189,11 @@ struct CALayerModal: LeafComponent {
|
|||
|
||||
return props.isPresented.value ? ModalPresenter.node(
|
||||
View.node(
|
||||
.init(Style(backgroundColor: .white)),
|
||||
.init(Style(backgroundColor: .white, Edges.equal(to: .parent))),
|
||||
StackView.node(.init(
|
||||
axis: .vertical,
|
||||
distribution: .fillEqually,
|
||||
Style(Edges.equal(to: .parent))
|
||||
Edges.equal(to: .parent)
|
||||
), [
|
||||
Button.node(.init(
|
||||
onPress: Handler { props.isPresented.set(false) }
|
||||
|
@ -202,7 +211,7 @@ struct CALayerModal: LeafComponent {
|
|||
borderWidth: Float(state.value * 10),
|
||||
cornerRadius: Float(state.value * 10),
|
||||
opacity: Float(state.value),
|
||||
Width.equal(to: .parent, constant: 10)
|
||||
Width.equal(to: .parent)
|
||||
)
|
||||
),
|
||||
Label.node(.init(
|
||||
|
@ -231,7 +240,7 @@ struct Counter: LeafComponent {
|
|||
let isStackModalPresented = hooks.state(false)
|
||||
let isAnimationModalPresented = hooks.state(false)
|
||||
let isTableModalPresented = hooks.state(false)
|
||||
let isConstrainModalPresented = hooks.state(false)
|
||||
let isConstraintModalPresented = hooks.state(false)
|
||||
let isDatePickerModalPresented = hooks.state(false)
|
||||
let isCALayerModalPresented = hooks.state(false)
|
||||
let switchState = hooks.state(true)
|
||||
|
@ -264,7 +273,7 @@ struct Counter: LeafComponent {
|
|||
isEnabled: isEnabled.value,
|
||||
onPress: Handler { isStackModalPresented.set(true) }
|
||||
),
|
||||
"Present Stack Modal"
|
||||
"Present Navigation Modal"
|
||||
),
|
||||
|
||||
Button.node(
|
||||
|
@ -286,9 +295,9 @@ struct Counter: LeafComponent {
|
|||
Button.node(
|
||||
.init(
|
||||
isEnabled: isEnabled.value,
|
||||
onPress: Handler { isConstrainModalPresented.set(true) }
|
||||
onPress: Handler { isConstraintModalPresented.set(true) }
|
||||
),
|
||||
"Present Constrain Modal"
|
||||
"Present Constraint Modal"
|
||||
),
|
||||
|
||||
Button.node(
|
||||
|
@ -307,13 +316,13 @@ struct Counter: LeafComponent {
|
|||
"Present Core Animation Layer Modal"
|
||||
),
|
||||
|
||||
StackModal.node(.init(isPresented: isStackModalPresented)),
|
||||
NavigationModal.node(.init(isPresented: isStackModalPresented)),
|
||||
|
||||
SimpleModal.node(.init(isPresented: isAnimationModalPresented)),
|
||||
|
||||
TableModal.node(.init(isPresented: isTableModalPresented)),
|
||||
|
||||
ConstrainModal.node(.init(isPresented: isConstrainModalPresented)),
|
||||
ConstraintModal.node(.init(isPresented: isConstraintModalPresented)),
|
||||
|
||||
DatePickerModal.node(.init(isPresented: isDatePickerModalPresented)),
|
||||
|
||||
|
@ -388,7 +397,7 @@ struct Counter: LeafComponent {
|
|||
alignment: .center,
|
||||
axis: .vertical,
|
||||
distribution: .fillEqually,
|
||||
Style(Edges.equal(to: .parent))
|
||||
Edges.equal(to: .parent)
|
||||
),
|
||||
children
|
||||
)
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import Gluon
|
||||
|
||||
struct NavRouter: StackRouter {
|
||||
struct NavRouter: NavigationRouter {
|
||||
enum Route {
|
||||
case first
|
||||
case second
|
||||
|
@ -32,32 +32,35 @@ struct NavRouter: StackRouter {
|
|||
), "Close Modal")
|
||||
switch route {
|
||||
case .first:
|
||||
return View.node(
|
||||
.init(Style(backgroundColor: .white)), [
|
||||
return
|
||||
NavigationItem.node(
|
||||
.init(title: "First", titleMode: .standard),
|
||||
View.node(
|
||||
.init(Style(backgroundColor: .white, Edges.equal(to: .parent))), [
|
||||
close,
|
||||
Label.node(.init(
|
||||
alignment: .center,
|
||||
Style(Rectangle(Point(x: 0, y: 200),
|
||||
Size(width: 200, height: 200)))
|
||||
), "first"),
|
||||
Button.node(.init(
|
||||
onPress: Handler { push(.second) },
|
||||
Style(Rectangle(Point(x: 0, y: 400),
|
||||
Size(width: 200, height: 200)))
|
||||
), "second"),
|
||||
), "Go to Second"),
|
||||
]
|
||||
)
|
||||
)
|
||||
case .second:
|
||||
return View.node(
|
||||
.init(Style(backgroundColor: .white)), [
|
||||
return
|
||||
NavigationItem.node(
|
||||
.init(title: "Second", titleMode: .large),
|
||||
View.node(
|
||||
.init(Style(backgroundColor: .white, Edges.equal(to: .parent))), [
|
||||
close,
|
||||
Label.node(.init(
|
||||
alignment: .center,
|
||||
Style(Rectangle(Point(x: 0, y: 200),
|
||||
Size(width: 200, height: 200)))
|
||||
), "second"),
|
||||
), "This is second"),
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,4 +19,4 @@ SPEC CHECKSUMS:
|
|||
|
||||
PODFILE CHECKSUM: 506176d9f57e313b696258608414e29b871b813e
|
||||
|
||||
COCOAPODS: 1.6.0.rc.2
|
||||
COCOAPODS: 1.6.0
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
D1C3A72421F0DE5300C6B884 /* Insets.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1C3A72221F0DC6300C6B884 /* Insets.swift */; };
|
||||
D1CB7D2921E136010075C0C3 /* SegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1CB7D2821E136010075C0C3 /* SegmentedControl.swift */; };
|
||||
D1CB7D2B21E136520075C0C3 /* SegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1CB7D2A21E136520075C0C3 /* SegmentedControl.swift */; };
|
||||
D1CB7D2E21E29CC80075C0C3 /* StackController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1CB7D2D21E29CC80075C0C3 /* StackController.swift */; };
|
||||
D1CB7D2E21E29CC80075C0C3 /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1CB7D2D21E29CC80075C0C3 /* NavigationController.swift */; };
|
||||
D1E8755521EF2ED200D4A7BA /* HooksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E8755421EF2ED200D4A7BA /* HooksTests.swift */; };
|
||||
D1EC92F021F5B8000026C207 /* ListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1EC92EF21F5B8000026C207 /* ListView.swift */; };
|
||||
D1EF77522205C579008829EC /* Constraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1EF77512205C579008829EC /* Constraint.swift */; };
|
||||
|
@ -72,12 +72,15 @@
|
|||
D1EF777B220744FC008829EC /* FirstBaseline.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1EF777A220744FB008829EC /* FirstBaseline.swift */; };
|
||||
D1EF777D220745AB008829EC /* BaselineConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1EF777C220745AB008829EC /* BaselineConstraint.swift */; };
|
||||
D1EF777F220745F4008829EC /* LastBaseline.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1EF777E220745F4008829EC /* LastBaseline.swift */; };
|
||||
D1EF77812208AEF5008829EC /* NavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1EF77802208AEF5008829EC /* NavigationItem.swift */; };
|
||||
D1EF77852208BB10008829EC /* TabItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1EF77842208BB10008829EC /* TabItem.swift */; };
|
||||
D1EF77862208BB13008829EC /* NavigationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1EF77822208BAF6008829EC /* NavigationItem.swift */; };
|
||||
OBJ_100 /* Animated.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_48 /* Animated.swift */; };
|
||||
OBJ_101 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_49 /* Button.swift */; };
|
||||
OBJ_102 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_50 /* Label.swift */; };
|
||||
OBJ_104 /* ModalPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_53 /* ModalPresenter.swift */; };
|
||||
OBJ_105 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_54 /* Router.swift */; };
|
||||
OBJ_106 /* StackPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_55 /* StackPresenter.swift */; };
|
||||
OBJ_106 /* NavigationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_55 /* NavigationPresenter.swift */; };
|
||||
OBJ_107 /* TabPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_56 /* TabPresenter.swift */; };
|
||||
OBJ_108 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_58 /* Color.swift */; };
|
||||
OBJ_109 /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_59 /* Event.swift */; };
|
||||
|
@ -115,7 +118,7 @@
|
|||
OBJ_174 /* UIValueComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_26 /* UIValueComponent.swift */; };
|
||||
OBJ_175 /* UIViewComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_27 /* UIViewComponent.swift */; };
|
||||
OBJ_176 /* Slider.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_28 /* Slider.swift */; };
|
||||
OBJ_177 /* StackController.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* StackController.swift */; };
|
||||
OBJ_177 /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_29 /* NavigationController.swift */; };
|
||||
OBJ_178 /* StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_30 /* StackView.swift */; };
|
||||
OBJ_179 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_31 /* View.swift */; };
|
||||
OBJ_180 /* ContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_33 /* ContainerViewController.swift */; };
|
||||
|
@ -198,7 +201,7 @@
|
|||
D1C3A72221F0DC6300C6B884 /* Insets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Insets.swift; sourceTree = "<group>"; };
|
||||
D1CB7D2821E136010075C0C3 /* SegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedControl.swift; sourceTree = "<group>"; };
|
||||
D1CB7D2A21E136520075C0C3 /* SegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SegmentedControl.swift; sourceTree = "<group>"; };
|
||||
D1CB7D2D21E29CC80075C0C3 /* StackController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackController.swift; sourceTree = "<group>"; };
|
||||
D1CB7D2D21E29CC80075C0C3 /* NavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = "<group>"; };
|
||||
D1E8755421EF2ED200D4A7BA /* HooksTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HooksTests.swift; sourceTree = "<group>"; };
|
||||
D1EC92EF21F5B8000026C207 /* ListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListView.swift; sourceTree = "<group>"; };
|
||||
D1EF77512205C579008829EC /* Constraint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constraint.swift; sourceTree = "<group>"; };
|
||||
|
@ -223,6 +226,9 @@
|
|||
D1EF777A220744FB008829EC /* FirstBaseline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstBaseline.swift; sourceTree = "<group>"; };
|
||||
D1EF777C220745AB008829EC /* BaselineConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaselineConstraint.swift; sourceTree = "<group>"; };
|
||||
D1EF777E220745F4008829EC /* LastBaseline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastBaseline.swift; sourceTree = "<group>"; };
|
||||
D1EF77802208AEF5008829EC /* NavigationItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItem.swift; sourceTree = "<group>"; };
|
||||
D1EF77822208BAF6008829EC /* NavigationItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationItem.swift; sourceTree = "<group>"; };
|
||||
D1EF77842208BB10008829EC /* TabItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabItem.swift; sourceTree = "<group>"; };
|
||||
"Gluon::Gluon::Product" /* Gluon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Gluon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
"Gluon::GluonTestRenderer::Product" /* GluonTestRenderer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = GluonTestRenderer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
"Gluon::GluonTests::Product" /* GluonTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = GluonTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -240,7 +246,7 @@
|
|||
OBJ_26 /* UIValueComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIValueComponent.swift; sourceTree = "<group>"; };
|
||||
OBJ_27 /* UIViewComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewComponent.swift; sourceTree = "<group>"; };
|
||||
OBJ_28 /* Slider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Slider.swift; sourceTree = "<group>"; };
|
||||
OBJ_29 /* StackController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackController.swift; sourceTree = "<group>"; };
|
||||
OBJ_29 /* NavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = "<group>"; };
|
||||
OBJ_30 /* StackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackView.swift; sourceTree = "<group>"; };
|
||||
OBJ_31 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = "<group>"; };
|
||||
OBJ_33 /* ContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContainerViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -259,7 +265,7 @@
|
|||
OBJ_50 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = "<group>"; };
|
||||
OBJ_53 /* ModalPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalPresenter.swift; sourceTree = "<group>"; };
|
||||
OBJ_54 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
|
||||
OBJ_55 /* StackPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackPresenter.swift; sourceTree = "<group>"; };
|
||||
OBJ_55 /* NavigationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationPresenter.swift; sourceTree = "<group>"; };
|
||||
OBJ_56 /* TabPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPresenter.swift; sourceTree = "<group>"; };
|
||||
OBJ_58 /* Color.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Color.swift; sourceTree = "<group>"; };
|
||||
OBJ_59 /* Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = "<group>"; };
|
||||
|
@ -357,7 +363,7 @@
|
|||
D1EC92EF21F5B8000026C207 /* ListView.swift */,
|
||||
D1CB7D2821E136010075C0C3 /* SegmentedControl.swift */,
|
||||
OBJ_64 /* Slider.swift */,
|
||||
D1CB7D2D21E29CC80075C0C3 /* StackController.swift */,
|
||||
D1CB7D2D21E29CC80075C0C3 /* NavigationController.swift */,
|
||||
OBJ_65 /* StackView.swift */,
|
||||
OBJ_66 /* View.swift */,
|
||||
A62891F921F5DB02001BE090 /* Switch.swift */,
|
||||
|
@ -375,7 +381,8 @@
|
|||
OBJ_17 /* ModalPresenter.swift */,
|
||||
D1CB7D2A21E136520075C0C3 /* SegmentedControl.swift */,
|
||||
OBJ_28 /* Slider.swift */,
|
||||
OBJ_29 /* StackController.swift */,
|
||||
OBJ_29 /* NavigationController.swift */,
|
||||
D1EF77802208AEF5008829EC /* NavigationItem.swift */,
|
||||
OBJ_30 /* StackView.swift */,
|
||||
OBJ_31 /* View.swift */,
|
||||
A62891F721F5DAEE001BE090 /* Switch.swift */,
|
||||
|
@ -526,8 +533,10 @@
|
|||
children = (
|
||||
OBJ_53 /* ModalPresenter.swift */,
|
||||
OBJ_54 /* Router.swift */,
|
||||
OBJ_55 /* StackPresenter.swift */,
|
||||
OBJ_55 /* NavigationPresenter.swift */,
|
||||
OBJ_56 /* TabPresenter.swift */,
|
||||
D1EF77822208BAF6008829EC /* NavigationItem.swift */,
|
||||
D1EF77842208BB10008829EC /* TabItem.swift */,
|
||||
);
|
||||
path = Presenters;
|
||||
sourceTree = "<group>";
|
||||
|
@ -802,11 +811,12 @@
|
|||
D1EF777D220745AB008829EC /* BaselineConstraint.swift in Sources */,
|
||||
A633562521FB96EE00BCC525 /* CenterX.swift in Sources */,
|
||||
OBJ_176 /* Slider.swift in Sources */,
|
||||
OBJ_177 /* StackController.swift in Sources */,
|
||||
OBJ_177 /* NavigationController.swift in Sources */,
|
||||
A633562121FB5C8800BCC525 /* Leading.swift in Sources */,
|
||||
OBJ_178 /* StackView.swift in Sources */,
|
||||
D163F9D621F27F8C00BA464B /* GluonViewController.swift in Sources */,
|
||||
D16FCB7C21FCB3870033C5C0 /* TableViewBox.swift in Sources */,
|
||||
D1EF77812208AEF5008829EC /* NavigationItem.swift in Sources */,
|
||||
OBJ_179 /* View.swift in Sources */,
|
||||
OBJ_180 /* ContainerViewController.swift in Sources */,
|
||||
OBJ_181 /* ControlBox.swift in Sources */,
|
||||
|
@ -850,13 +860,13 @@
|
|||
D1EF775C2205C64D008829EC /* Bottom.swift in Sources */,
|
||||
D1EF776C2205C727008829EC /* LastBaseline.swift in Sources */,
|
||||
D1EF77642205C6C8008829EC /* Trailing.swift in Sources */,
|
||||
OBJ_106 /* StackPresenter.swift in Sources */,
|
||||
OBJ_106 /* NavigationPresenter.swift in Sources */,
|
||||
D1EF776A2205C717008829EC /* FirstBaseline.swift in Sources */,
|
||||
OBJ_107 /* TabPresenter.swift in Sources */,
|
||||
D1BDBBE621E0F07800FBBCDF /* MountedNull.swift in Sources */,
|
||||
OBJ_108 /* Color.swift in Sources */,
|
||||
OBJ_109 /* Event.swift in Sources */,
|
||||
D1CB7D2E21E29CC80075C0C3 /* StackController.swift in Sources */,
|
||||
D1CB7D2E21E29CC80075C0C3 /* NavigationController.swift in Sources */,
|
||||
OBJ_110 /* Rectangle.swift in Sources */,
|
||||
OBJ_111 /* Second.swift in Sources */,
|
||||
OBJ_112 /* Style.swift in Sources */,
|
||||
|
@ -870,6 +880,7 @@
|
|||
D16FCB8121FDD2550033C5C0 /* Weak.swift in Sources */,
|
||||
D1EF77542205C583008829EC /* Edges.swift in Sources */,
|
||||
OBJ_117 /* MountedComponent.swift in Sources */,
|
||||
D1EF77852208BB10008829EC /* TabItem.swift in Sources */,
|
||||
OBJ_118 /* MountedCompositeComponent.swift in Sources */,
|
||||
OBJ_119 /* MountedHostComponent.swift in Sources */,
|
||||
D1EF775A2205C639008829EC /* Top.swift in Sources */,
|
||||
|
@ -880,6 +891,7 @@
|
|||
OBJ_123 /* Store.swift in Sources */,
|
||||
OBJ_124 /* Unique.swift in Sources */,
|
||||
D1EF77682205C6F6008829EC /* CenterY.swift in Sources */,
|
||||
D1EF77862208BB13008829EC /* NavigationItem.swift in Sources */,
|
||||
A62891FA21F5DB02001BE090 /* Switch.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
//
|
||||
// StackController.swift
|
||||
// NavigationController.swift
|
||||
// Gluon
|
||||
//
|
||||
// Created by Max Desiatov on 06/01/2019.
|
||||
//
|
||||
|
||||
public struct StackController: HostComponent {
|
||||
public struct NavigationController: HostComponent {
|
||||
public typealias Children = [AnyNode]
|
||||
|
||||
public struct Props: Equatable {
|
||||
public let hidesBarsWhenKeyboardAppears: Bool?
|
||||
public let popAnimated: Bool
|
||||
public let prefersLargeTitles: Bool
|
||||
public let pushAnimated: Bool
|
||||
public let onPop: Handler<()>
|
||||
}
|
|
@ -35,18 +35,50 @@ public struct StackView: HostComponent {
|
|||
public let alignment: Alignment
|
||||
public let axis: Axis
|
||||
public let distribution: Distribution
|
||||
public let style: Style?
|
||||
public let spacing: Double
|
||||
|
||||
public init(alignment: Alignment = .fill,
|
||||
/// not exposing style: UIStackView is a non-rendering subclass of UIView
|
||||
/// https://useyourloaf.com/blog/stack-view-background-color/
|
||||
public let style: Style?
|
||||
|
||||
public init(
|
||||
alignment: Alignment = .fill,
|
||||
axis: Axis = .horizontal,
|
||||
distribution: Distribution = .fill,
|
||||
spacing: Double = 0,
|
||||
_ style: Style? = nil) {
|
||||
_ frame: Rectangle
|
||||
) {
|
||||
self.alignment = alignment
|
||||
self.axis = axis
|
||||
self.distribution = distribution
|
||||
self.style = style
|
||||
style = Style(frame)
|
||||
self.spacing = spacing
|
||||
}
|
||||
|
||||
public init(
|
||||
alignment: Alignment = .fill,
|
||||
axis: Axis = .horizontal,
|
||||
distribution: Distribution = .fill,
|
||||
spacing: Double = 0,
|
||||
_ constraint: Constraint
|
||||
) {
|
||||
self.alignment = alignment
|
||||
self.axis = axis
|
||||
self.distribution = distribution
|
||||
style = Style(constraint)
|
||||
self.spacing = spacing
|
||||
}
|
||||
|
||||
public init(
|
||||
alignment: Alignment = .fill,
|
||||
axis: Axis = .horizontal,
|
||||
distribution: Distribution = .fill,
|
||||
spacing: Double = 0
|
||||
) {
|
||||
self.alignment = alignment
|
||||
self.axis = axis
|
||||
self.distribution = distribution
|
||||
style = nil
|
||||
self.spacing = spacing
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
//
|
||||
// NavigationItem.swift
|
||||
// GluonUIKit
|
||||
//
|
||||
// Created by Max Desiatov on 04/02/2019.
|
||||
//
|
||||
|
||||
public struct NavigationItem: HostComponent {
|
||||
public struct Props: Equatable {
|
||||
/// The mode to use when displaying the title of the navigation bar.
|
||||
public enum TitleMode {
|
||||
case automatic
|
||||
case large
|
||||
case standard
|
||||
}
|
||||
|
||||
public let title: String?
|
||||
public let titleMode: TitleMode
|
||||
|
||||
public init(title: String? = nil, titleMode: TitleMode = .automatic) {
|
||||
self.title = title
|
||||
self.titleMode = titleMode
|
||||
}
|
||||
}
|
||||
|
||||
public typealias Children = AnyNode
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
//
|
||||
// StackPresenter.swift
|
||||
// NavigationPresenter.swift
|
||||
// Gluon
|
||||
//
|
||||
// Created by Max Desiatov on 31/12/2018.
|
||||
//
|
||||
|
||||
public protocol StackRouter: Router {
|
||||
public protocol NavigationRouter: Router {
|
||||
static func route(
|
||||
props: Props,
|
||||
route: Route,
|
||||
|
@ -15,11 +15,12 @@ public protocol StackRouter: Router {
|
|||
) -> AnyNode
|
||||
}
|
||||
|
||||
public struct StackPresenter<T: StackRouter>: LeafComponent {
|
||||
public struct NavigationPresenter<T: NavigationRouter>: LeafComponent {
|
||||
public struct Props: Equatable {
|
||||
public let hidesBarsWhenKeyboardAppears: Bool?
|
||||
public let initial: T.Route
|
||||
public let popAnimated: Bool
|
||||
public let prefersLargeTitles: Bool
|
||||
public let pushAnimated: Bool
|
||||
public let routerProps: T.Props
|
||||
|
||||
|
@ -27,12 +28,14 @@ public struct StackPresenter<T: StackRouter>: LeafComponent {
|
|||
hidesBarsWhenKeyboardAppears: Bool? = nil,
|
||||
initial: T.Route,
|
||||
popAnimated: Bool = true,
|
||||
prefersLargeTitles: Bool = false,
|
||||
pushAnimated: Bool = true,
|
||||
routerProps: T.Props
|
||||
) {
|
||||
self.hidesBarsWhenKeyboardAppears = hidesBarsWhenKeyboardAppears
|
||||
self.initial = initial
|
||||
self.popAnimated = popAnimated
|
||||
self.prefersLargeTitles = prefersLargeTitles
|
||||
self.pushAnimated = pushAnimated
|
||||
self.routerProps = routerProps
|
||||
}
|
||||
|
@ -43,10 +46,11 @@ public struct StackPresenter<T: StackRouter>: LeafComponent {
|
|||
|
||||
let pop = { stack.set(Array(stack.value.dropLast())) }
|
||||
|
||||
return StackController.node(
|
||||
return NavigationController.node(
|
||||
.init(
|
||||
hidesBarsWhenKeyboardAppears: props.hidesBarsWhenKeyboardAppears,
|
||||
popAnimated: props.popAnimated,
|
||||
prefersLargeTitles: props.prefersLargeTitles,
|
||||
pushAnimated: props.pushAnimated,
|
||||
onPop: Handler(pop)
|
||||
),
|
||||
|
@ -63,16 +67,18 @@ public struct StackPresenter<T: StackRouter>: LeafComponent {
|
|||
}
|
||||
}
|
||||
|
||||
extension StackPresenter.Props where T.Props == Null {
|
||||
extension NavigationPresenter.Props where T.Props == Null {
|
||||
public init(
|
||||
hidesBarsWhenKeyboardAppears: Bool? = nil,
|
||||
initial: T.Route,
|
||||
popAnimated: Bool = true,
|
||||
prefersLargeTitles: Bool = false,
|
||||
pushAnimated: Bool = true
|
||||
) {
|
||||
self.hidesBarsWhenKeyboardAppears = hidesBarsWhenKeyboardAppears
|
||||
self.initial = initial
|
||||
self.popAnimated = popAnimated
|
||||
self.prefersLargeTitles = prefersLargeTitles
|
||||
self.pushAnimated = pushAnimated
|
||||
routerProps = Null()
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// TabItem.swift
|
||||
// Gluon
|
||||
//
|
||||
// Created by Max Desiatov on 04/02/2019.
|
||||
//
|
||||
|
||||
public struct TabItem: Equatable {
|
||||
public let title: String?
|
||||
|
||||
public init(title: String? = nil) {
|
||||
self.title = title
|
||||
}
|
||||
}
|
|
@ -9,16 +9,14 @@
|
|||
components by `StackReconciler`.
|
||||
*/
|
||||
public final class MountedHostComponent<R: Renderer>: MountedComponent<R> {
|
||||
// FIXME: we probably can avoid making this class public
|
||||
|
||||
private var managedChildren = [Weak<R.Target>: MountedComponent<R>]()
|
||||
private var mountedChildren = [MountedComponent<R>]()
|
||||
|
||||
/** Target of a closest ancestor host component. As a parent of this component
|
||||
might not be a host component, but a composite component, we need to pass
|
||||
around the target of a host component to its closests descendent host
|
||||
comoponents. Thus, a parent target is not the same as a target of a parent
|
||||
component. */
|
||||
comoponents. Thus, a parent target is not always the same as a target of
|
||||
a parent component. */
|
||||
private let parentTarget: R.Target
|
||||
|
||||
/** Target of this host component supplied by a renderer after mounting has
|
||||
|
|
|
@ -21,7 +21,7 @@ final class ContainerViewController: UIViewController {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
view = contained
|
||||
override func viewDidLoad() {
|
||||
view.addSubview(contained)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,10 +95,11 @@ final class TableViewBox<T: CellProvider>: ViewBox<GluonTableView> {
|
|||
return dataSource.props
|
||||
}
|
||||
set {
|
||||
if dataSource.props.model != newValue.model {
|
||||
defer { view.reloadData() }
|
||||
}
|
||||
let oldModel = dataSource.props.model
|
||||
dataSource.props = newValue
|
||||
if oldModel != newValue.model {
|
||||
view.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,9 @@ extension ModalPresenter: UIHostComponent {
|
|||
}
|
||||
|
||||
static func update(target: UITarget,
|
||||
node: AnyNode) {}
|
||||
node: AnyNode) {
|
||||
// FIXME: update presentation-related props on the target here
|
||||
}
|
||||
|
||||
static func unmount(target: UITarget) {
|
||||
target.viewController.dismiss(animated: true)
|
||||
|
|
|
@ -28,7 +28,7 @@ final class GluonNavigationController: UINavigationController {
|
|||
}
|
||||
}
|
||||
|
||||
extension StackController: UIHostComponent {
|
||||
extension NavigationController: UIHostComponent {
|
||||
static func mountTarget(to parent: UITarget,
|
||||
component: UIKitRenderer.MountedHost,
|
||||
_: UIKitRenderer) -> UITarget? {
|
||||
|
@ -44,6 +44,11 @@ extension StackController: UIHostComponent {
|
|||
result.containerViewController.hidesBarsWhenKeyboardAppears = $0
|
||||
}
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
result.containerViewController.navigationBar.prefersLargeTitles =
|
||||
props.prefersLargeTitles
|
||||
}
|
||||
|
||||
switch parent {
|
||||
// FIXME: this `case` handler is duplicated with `UIViewComponent`,
|
||||
// should this be generalised as a protocol?
|
|
@ -0,0 +1,73 @@
|
|||
//
|
||||
// StackControllerItem.swift
|
||||
// GluonUIKit
|
||||
//
|
||||
// Created by Max Desiatov on 04/02/2019.
|
||||
//
|
||||
|
||||
import Gluon
|
||||
import UIKit
|
||||
|
||||
extension UINavigationItem.LargeTitleDisplayMode {
|
||||
init(mode: NavigationItem.Props.TitleMode) {
|
||||
switch mode {
|
||||
case .automatic:
|
||||
self = .automatic
|
||||
case .large:
|
||||
self = .always
|
||||
case .standard:
|
||||
self = .never
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension NavigationItem: UIHostComponent {
|
||||
static func mountTarget(to parent: UITarget,
|
||||
component: UIKitRenderer.MountedHost,
|
||||
_: UIKitRenderer) -> UITarget? {
|
||||
guard
|
||||
let parent = parent as? ViewControllerBox<GluonNavigationController>
|
||||
else {
|
||||
parentAssertionFailure()
|
||||
return nil
|
||||
}
|
||||
|
||||
guard
|
||||
let parentProps = parent.node?.props.value as? NavigationController.Props
|
||||
else {
|
||||
propsAssertionFailure()
|
||||
return nil
|
||||
}
|
||||
|
||||
let viewController = UIViewController()
|
||||
let result = ViewControllerBox(viewController, component.node)
|
||||
update(target: result, node: component.node)
|
||||
|
||||
parent.containerViewController.pushViewController(
|
||||
viewController,
|
||||
animated: parentProps.pushAnimated
|
||||
)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
static func update(target: UITarget, node: AnyNode) {
|
||||
guard let target = target as? ViewControllerBox<UIViewController> else {
|
||||
targetAssertionFailure()
|
||||
return
|
||||
}
|
||||
guard let props = node.props.value as? NavigationItem.Props else {
|
||||
propsAssertionFailure()
|
||||
return
|
||||
}
|
||||
|
||||
let item = target.viewController.navigationItem
|
||||
|
||||
item.title = props.title
|
||||
if #available(iOS 11.0, *) {
|
||||
item.largeTitleDisplayMode = .init(mode: props.titleMode)
|
||||
}
|
||||
}
|
||||
|
||||
static func unmount(target: UITarget) {}
|
||||
}
|
|
@ -31,7 +31,9 @@ private func applyStyle<T: UIView, P: StyleProps>(_ target: ViewBox<T>,
|
|||
|
||||
let view = target.view
|
||||
|
||||
style.allowsEdgeAntialiasing.flatMap { view.layer.allowsEdgeAntialiasing = $0 }
|
||||
style.allowsEdgeAntialiasing.flatMap {
|
||||
view.layer.allowsEdgeAntialiasing = $0
|
||||
}
|
||||
style.allowsGroupOpacity.flatMap { view.layer.allowsGroupOpacity = $0 }
|
||||
style.alpha.flatMap { view.alpha = CGFloat($0) }
|
||||
style.backgroundColor.flatMap { view.backgroundColor = UIColor($0) }
|
||||
|
@ -95,18 +97,19 @@ extension UIViewComponent where Target == Target.DefaultValue,
|
|||
let result: ViewBox<Target>
|
||||
|
||||
let parentRequiresViewController = parent.node?.isSubtypeOf(
|
||||
ModalPresenter.self, or: StackController.self, or: AnyTabPresenter.self
|
||||
ModalPresenter.self,
|
||||
or: NavigationController.self,
|
||||
or: AnyTabPresenter.self
|
||||
) ?? false
|
||||
|
||||
// UIViewController parent target can't present a bare `ViewBox` target,
|
||||
// it needs to be wrapped with `ContainerViewController` first.
|
||||
if parentRequiresViewController {
|
||||
result = box(
|
||||
for: target,
|
||||
ContainerViewController(contained: target),
|
||||
component,
|
||||
renderer
|
||||
)
|
||||
let container = ContainerViewController(contained: target)
|
||||
// trigger `viewDidLoad` on `ContainerViewController` for safe constraints
|
||||
// installation
|
||||
container.loadViewIfNeeded()
|
||||
result = box(for: target, container, component, renderer)
|
||||
} else {
|
||||
result = box(for: target, parent.viewController, component, renderer)
|
||||
}
|
||||
|
@ -123,9 +126,9 @@ extension UIViewComponent where Target == Target.DefaultValue,
|
|||
case let box as ViewBox<GluonTableCell>:
|
||||
box.view.addSubview(target)
|
||||
case let box as ViewControllerBox<GluonNavigationController>
|
||||
where parent.node?.isSubtypeOf(StackController.self) ?? false:
|
||||
where parent.node?.isSubtypeOf(NavigationController.self) ?? false:
|
||||
guard let props = parent.node?.props.value
|
||||
as? StackController.Props else {
|
||||
as? NavigationController.Props else {
|
||||
propsAssertionFailure()
|
||||
return nil
|
||||
}
|
||||
|
@ -136,7 +139,9 @@ extension UIViewComponent where Target == Target.DefaultValue,
|
|||
)
|
||||
case let box as ViewControllerBox<UIViewController>
|
||||
where parent.node?.isSubtypeOf(ModalPresenter.self) ?? false:
|
||||
guard let props = parent.node?.props.value as? ModalPresenter.Props else {
|
||||
guard
|
||||
let props = parent.node?.props.value as? ModalPresenter.Props
|
||||
else {
|
||||
propsAssertionFailure()
|
||||
return nil
|
||||
}
|
||||
|
@ -144,6 +149,9 @@ extension UIViewComponent where Target == Target.DefaultValue,
|
|||
box.viewController.present(result.viewController,
|
||||
animated: props.presentAnimated,
|
||||
completion: nil)
|
||||
case let box as ViewControllerBox<UIViewController>
|
||||
where parent.node?.isSubtypeOf(NavigationItem.self) ?? false:
|
||||
box.viewController.view.addSubview(target)
|
||||
default:
|
||||
parentAssertionFailure()
|
||||
}
|
||||
|
|
|
@ -12,9 +12,10 @@ import UIKit
|
|||
// compiler bug
|
||||
let _modalPresenterWitnessTableHack: UIHostComponent.Type = ModalPresenter.self
|
||||
let _stackControllerWitnessTableHack: UIHostComponent.Type =
|
||||
StackController.self
|
||||
|
||||
let _listViewWitnessTableHack: UIHostComponent.Type = ListView<HackyProvider>.self
|
||||
NavigationController.self
|
||||
let _navigationItemWitnessTableHack: UIHostComponent.Type = NavigationItem.self
|
||||
let _listViewWitnessTableHack: UIHostComponent.Type =
|
||||
ListView<HackyProvider>.self
|
||||
|
||||
struct HackyProvider: SimpleCellProvider {
|
||||
static func cell(
|
||||
|
|
|
@ -33,7 +33,7 @@ struct Counter: LeafComponent {
|
|||
|
||||
return StackView.node(.init(axis: .vertical,
|
||||
distribution: .fillEqually,
|
||||
Style(Edges.equal(to: .parent))),
|
||||
Edges.equal(to: .parent)),
|
||||
children)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
| Gluon Component | Rendered on iOS as |
|
||||
|---|---|
|
||||
| [`ModalPresenter`](https://github.com/MaxDesiatov/Gluon/blob/master/Sources/Gluon/Components/Presenters/ModalPresenter.swift) | [`UIViewController`](https://developer.apple.com/documentation/uikit/uiviewcontroller) with modal presentation|
|
||||
| [`StackPresenter`](https://github.com/MaxDesiatov/Gluon/blob/master/Sources/Gluon/Components/Presenters/StackPresenter.swift) | [`UINavigationController`](https://developer.apple.com/documentation/uikit/uinavigationcontroller) |
|
||||
| [`NavigationPresenter`](https://github.com/MaxDesiatov/Gluon/blob/master/Sources/Gluon/Components/Presenters/NavigationPresenter.swift) | [`UINavigationController`](https://developer.apple.com/documentation/uikit/uinavigationcontroller) |
|
||||
| [`TabPresenter`](https://github.com/MaxDesiatov/Gluon/blob/master/Sources/Gluon/Components/Presenters/TabPresenter.swift) | [`UITabBarController`](https://developer.apple.com/documentation/uikit/uitabbarcontroller) |
|
||||
|
||||
## Style
|
||||
|
|
Loading…
Reference in New Issue