Rework Example navigation, add LineBreakMode

This commit is contained in:
Max Desiatov 2019-02-14 13:42:40 +00:00
parent a5fa21e84d
commit 89cde27de5
No known key found for this signature in database
GPG Key ID: FE08EBF9CF58CBA2
19 changed files with 613 additions and 398 deletions

View File

@ -13,10 +13,17 @@
607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; };
607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; };
607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; };
D126CB7421ECF7A60097300E /* GluonApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E0C86021DA54780042A261 /* GluonApp.swift */; };
D1BFAF772215795900845EA0 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BFAF762215795900845EA0 /* Router.swift */; };
D1BFAF792215800A00845EA0 /* Counter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BFAF782215800A00845EA0 /* Counter.swift */; };
D1BFAF7B22158B4000845EA0 /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1BFAF7A22158B4000845EA0 /* List.swift */; };
D1DEEC2922009E8000C525EE /* NavRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1DEEC2822009E8000C525EE /* NavRouter.swift */; };
D1E6BCD221AD4CD6002769E3 /* GluonTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1E6BCD121AD4CD6002769E3 /* GluonTest.swift */; };
D1F2C3272214407B008358DC /* TableModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F2C3262214407B008358DC /* TableModal.swift */; };
D1F7185322159E09004E5951 /* Controls.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F7185222159E09004E5951 /* Controls.swift */; };
D1F7185522159EAD004E5951 /* DatePickers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F7185422159EAD004E5951 /* DatePickers.swift */; };
D1F7185D2215A4A1004E5951 /* LayerProps.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F7185C2215A4A1004E5951 /* LayerProps.swift */; };
D1F7185F2215A5D0004E5951 /* Constraints.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F7185E2215A5D0004E5951 /* Constraints.swift */; };
D1F718612215A617004E5951 /* Modals.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F718602215A617004E5951 /* Modals.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -45,10 +52,17 @@
8CADEBB8BFF6F2621CA49E8F /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
C1B4A8D80770145FDAFEAE64 /* Pods_Gluon_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Gluon_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C6DA99382B6892EAB361742F /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
D1BFAF762215795900845EA0 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = "<group>"; };
D1BFAF782215800A00845EA0 /* Counter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Counter.swift; sourceTree = "<group>"; };
D1BFAF7A22158B4000845EA0 /* List.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = List.swift; sourceTree = "<group>"; };
D1DEEC2822009E8000C525EE /* NavRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavRouter.swift; sourceTree = "<group>"; };
D1E0C86021DA54780042A261 /* GluonApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GluonApp.swift; sourceTree = "<group>"; };
D1E6BCD121AD4CD6002769E3 /* GluonTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GluonTest.swift; sourceTree = "<group>"; };
D1F2C3262214407B008358DC /* TableModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableModal.swift; sourceTree = "<group>"; };
D1F7185222159E09004E5951 /* Controls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Controls.swift; sourceTree = "<group>"; };
D1F7185422159EAD004E5951 /* DatePickers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatePickers.swift; sourceTree = "<group>"; };
D1F7185C2215A4A1004E5951 /* LayerProps.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayerProps.swift; sourceTree = "<group>"; };
D1F7185E2215A5D0004E5951 /* Constraints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constraints.swift; sourceTree = "<group>"; };
D1F718602215A617004E5951 /* Modals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modals.swift; sourceTree = "<group>"; };
E7F808D1B3715FF294877289 /* Pods-Gluon_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Gluon_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Gluon_Example/Pods-Gluon_Example.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -98,10 +112,9 @@
isa = PBXGroup;
children = (
607FACD51AFB9204008FA782 /* AppDelegate.swift */,
D1F7185122159D6E004E5951 /* Examples */,
607FACD71AFB9204008FA782 /* ViewController.swift */,
D1E0C86021DA54780042A261 /* GluonApp.swift */,
D1DEEC2822009E8000C525EE /* NavRouter.swift */,
D1F2C3262214407B008358DC /* TableModal.swift */,
D1BFAF762215795900845EA0 /* Router.swift */,
607FACD91AFB9204008FA782 /* Main.storyboard */,
607FACDC1AFB9204008FA782 /* Images.xcassets */,
607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
@ -155,6 +168,22 @@
name = Frameworks;
sourceTree = "<group>";
};
D1F7185122159D6E004E5951 /* Examples */ = {
isa = PBXGroup;
children = (
D1BFAF7A22158B4000845EA0 /* List.swift */,
D1DEEC2822009E8000C525EE /* NavRouter.swift */,
D1F2C3262214407B008358DC /* TableModal.swift */,
D1BFAF782215800A00845EA0 /* Counter.swift */,
D1F7185222159E09004E5951 /* Controls.swift */,
D1F7185422159EAD004E5951 /* DatePickers.swift */,
D1F7185C2215A4A1004E5951 /* LayerProps.swift */,
D1F7185E2215A5D0004E5951 /* Constraints.swift */,
D1F718602215A617004E5951 /* Modals.swift */,
);
path = Examples;
sourceTree = "<group>";
};
D360275A0620D06017CFB567 /* Pods */ = {
isa = PBXGroup;
children = (
@ -291,11 +320,18 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D126CB7421ECF7A60097300E /* GluonApp.swift in Sources */,
D1BFAF7B22158B4000845EA0 /* List.swift in Sources */,
D1F2C3272214407B008358DC /* TableModal.swift in Sources */,
607FACD81AFB9204008FA782 /* ViewController.swift in Sources */,
D1F7185F2215A5D0004E5951 /* Constraints.swift in Sources */,
D1F7185322159E09004E5951 /* Controls.swift in Sources */,
D1DEEC2922009E8000C525EE /* NavRouter.swift in Sources */,
D1BFAF772215795900845EA0 /* Router.swift in Sources */,
607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */,
D1F7185D2215A4A1004E5951 /* LayerProps.swift in Sources */,
D1BFAF792215800A00845EA0 /* Counter.swift in Sources */,
D1F718612215A617004E5951 /* Modals.swift in Sources */,
D1F7185522159EAD004E5951 /* DatePickers.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -0,0 +1,38 @@
//
// Constraints.swift
// Gluon_Example
//
// Created by Max Desiatov on 14/02/2019.
// Copyright © 2019 Gluon. All rights reserved.
//
import Gluon
struct Constraints: LeafComponent {
typealias Props = Null
static func render(props: Props, hooks: Hooks) -> AnyNode {
let left = hooks.state(0.5 as Float)
return StackView.node(.init(
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
), [
Slider.node(.init(
value: left.value,
valueHandler: Handler(left.set),
Style(Width.equal(to: .parent))
)),
View.node(
.init(Style(backgroundColor: .red)),
Label.node(.init(
alignment: .center,
textColor: .white,
Style(Left.equal(to: .parent, constant: Double(left.value) * 200))
), "\(left.value)")
),
])
}
}

View File

@ -0,0 +1,97 @@
//
// Controls.swift
// Gluon_Example
//
// Created by Max Desiatov on 14/02/2019.
// Copyright © 2019 Gluon. All rights reserved.
//
import Gluon
struct Controls: LeafComponent {
typealias Props = Null
static func render(props: Props, hooks: Hooks) -> AnyNode {
let sliding = hooks.state(0.5 as Float)
let switchState = hooks.state(true)
let stepperState = hooks.state(0.0)
let isEnabled = hooks.state(true)
let children = [
StackView.node(
.init(
alignment: .center,
axis: .horizontal,
spacing: 10.0
), [
Switch.node(
.init(
value: isEnabled.value,
valueHandler: Handler(isEnabled.set)
)
),
Label.node(
.init(alignment: .center),
"isEnabled \(isEnabled.value)"
),
]
),
Slider.node(.init(
isEnabled: isEnabled.value,
value: sliding.value,
valueHandler: Handler(sliding.set),
Style(Width.equal(to: .parent))
)),
Label.node(.init(alignment: .center), "\(sliding.value)"),
StackView.node(
.init(
alignment: .center,
axis: .horizontal,
spacing: 10.0
), [
Switch.node(
.init(
isEnabled: isEnabled.value,
value: switchState.value,
valueHandler: Handler(switchState.set)
)
),
Label.node(.init(alignment: .center), "\(switchState.value)"),
]
),
StackView.node(
.init(
alignment: .center,
axis: .horizontal,
spacing: 10.0
), [
Stepper.node(
.init(
isEnabled: isEnabled.value,
value: stepperState.value,
valueHandler: Handler(stepperState.set)
)
),
Label.node(.init(alignment: .center), "\(stepperState.value)"),
]
),
]
return StackView.node(
.init(
alignment: .center,
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
),
children
)
}
}

View File

@ -0,0 +1,33 @@
//
// Counter.swift
// Gluon_Example
//
// Created by Max Desiatov on 14/02/2019.
// Copyright © 2019 Gluon. All rights reserved.
//
import Gluon
struct Counter: LeafComponent {
struct Props: Equatable {
let countFrom: Int
}
static func render(props: Counter.Props, hooks: Hooks) -> AnyNode {
let count = hooks.state(props.countFrom)
return StackView.node(.init(
alignment: .center,
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
), [
Button.node(
.init(onPress: Handler { count.set { $0 + 1 } }),
"Increment"
),
Label.node(.init(alignment: .center), "\(count.value)"),
])
}
}

View File

@ -0,0 +1,61 @@
//
// DatePicker.swift
// Gluon_Example
//
// Created by Max Desiatov on 14/02/2019.
// Copyright © 2019 Gluon. All rights reserved.
//
import Gluon
struct DatePickers: LeafComponent {
typealias Props = Null
static func render(props: Props, hooks: Hooks) -> AnyNode {
let currentDateTime = Date()
let date = hooks.state(currentDateTime)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .medium
let formattedDate = dateFormatter.string(from: date.value)
let labelProps = Label.Props(
alignment: .center,
lineBreakMode: .wordWrap,
numberOfLines: 0
)
return StackView.node(.init(
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
), [
Label.node(
labelProps,
"\(formattedDate)"
),
Label.node(
labelProps,
"This picker doesn't animate state changes in the next picker"
),
DatePicker.node(
.init(
value: date.value,
valueHandler: Handler(date.set),
Style(Width.equal(to: .parent))
)
),
Label.node(
labelProps,
"This picker animates state changes in the previous picker"
),
DatePicker.node(
.init(
isAnimated: false,
value: date.value,
valueHandler: Handler(date.set),
Style(Width.equal(to: .parent))
)
),
])
}
}

View File

@ -0,0 +1,50 @@
//
// LayerProps.swift
// Gluon_Example
//
// Created by Max Desiatov on 14/02/2019.
// Copyright © 2019 Gluon. All rights reserved.
//
import Gluon
struct LayerProps: LeafComponent {
typealias Props = Null
static func render(props: Null, hooks: Hooks) -> AnyNode {
let state = hooks.state(0.5 as Float)
return
StackView.node(.init(
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
), [
Slider.node(.init(
value: state.value,
valueHandler: Handler(state.set),
Style(Width.equal(to: .parent))
)),
View.node(
.init(
Style(
backgroundColor: .red,
borderWidth: Float(state.value * 10),
cornerRadius: Float(state.value * 10),
opacity: Float(state.value),
Width.equal(to: .parent)
)
),
Label.node(.init(
alignment: .center,
textColor: .white,
Style(
[Top.equal(to: .parent, constant: Double(state.value) * 100),
Left.equal(to: .parent, constant: Double(state.value) * 200)]
)
), "\(state.value)")
),
])
}
}

View File

@ -0,0 +1,42 @@
//
// List.swift
// Gluon_Example
//
// Created by Max Desiatov on 14/02/2019.
// Copyright © 2019 Gluon. All rights reserved.
//
import Gluon
private struct Cells: SimpleCellProvider {
static func cell(
props: Null,
item: AppRoute,
path: CellPath
) -> AnyNode {
return Label.node(.init(Style(
[CenterY.equal(to: .parent),
Height.equal(to: 44),
Leading.equal(to: .parent, constant: 20)]
)), "\(item.description)")
}
typealias Props = Null
typealias Model = [[AppRoute]]
}
struct List: PureLeafComponent {
struct Props: Equatable {
let model: [AppRoute]
let onSelect: Handler<CellPath>
}
static func render(props: Props) -> AnyNode {
return ListView<Cells>.node(.init(
model: [props.model],
onSelect: props.onSelect,
Style(Edges.equal(to: .parent))
))
}
}

View File

@ -0,0 +1,111 @@
//
// Modals.swift
// Gluon_Example
//
// Created by Max Desiatov on 14/02/2019.
// Copyright © 2019 Gluon. All rights reserved.
//
import Gluon
struct NavigationModal: PureLeafComponent {
struct Props: Equatable {
let isPresented: State<Bool>
}
static func render(props: Props) -> AnyNode {
return props.isPresented.value ?
ModalPresenter.node(
NavigationPresenter<NavRouter>.node(
.init(
initial: .first,
prefersLargeTitles: true,
routerProps: .init(
onPress: Handler { props.isPresented.set(false) }
)
)
)
) : Null.node()
}
}
struct SimpleModal: LeafComponent {
struct Props: Equatable {
let isPresented: State<Bool>
}
private static let colors: [(Color, String)] = [
(.white, "white"),
(.red, "red"),
(.green, "green"),
(.blue, "blue"),
]
static func render(props: Props, hooks: Hooks) -> AnyNode {
let backgroundColor = hooks.state(0)
return props.isPresented.value ? ModalPresenter.node(
View.node(
.init(Style(
backgroundColor: colors[backgroundColor.value].0,
Edges.equal(to: .parent)
)),
StackView.node(.init(
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
), [
Button.node(.init(
onPress: Handler { props.isPresented.set(false) }
), "Close Modal"),
SegmentedControl.node(
.init(value: backgroundColor.value,
valueHandler: Handler(backgroundColor.set)),
colors.map { $0.1 }
),
])
)
) : Null.node()
}
}
struct Modals: LeafComponent {
typealias Props = Null
static func render(props: Props, hooks: Hooks) -> AnyNode {
let isStackModalPresented = hooks.state(false)
let isAnimationModalPresented = hooks.state(false)
let isTableModalPresented = hooks.state(false)
return StackView.node(
.init(
alignment: .center,
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
),
[
Button.node(
.init(onPress: Handler { isAnimationModalPresented.set(true) }),
"Present Simple Modal"
),
Button.node(
.init(onPress: Handler { isStackModalPresented.set(true) }),
"Present Navigation Modal"
),
Button.node(
.init(onPress: Handler { isTableModalPresented.set(true) }),
"Present Table Modal"
),
NavigationModal.node(.init(isPresented: isStackModalPresented)),
SimpleModal.node(.init(isPresented: isAnimationModalPresented)),
TableModal.node(.init(isPresented: isTableModalPresented)),
]
)
}
}

View File

@ -1,390 +0,0 @@
//
// GluonApp.swift
// Gluon_Example
//
// Created by Max Desiatov on 31/12/2018.
// Copyright © 2018 Max Desiatov. All rights reserved.
//
import Gluon
struct NavigationModal: PureLeafComponent {
struct Props: Equatable {
let isPresented: State<Bool>
}
static func render(props: Props) -> AnyNode {
return props.isPresented.value ?
ModalPresenter.node(
NavigationPresenter<NavRouter>.node(
.init(
initial: .first,
prefersLargeTitles: true,
routerProps: .init(
onPress: Handler { props.isPresented.set(false) }
)
)
)
) : Null.node()
}
}
struct SimpleModal: LeafComponent {
struct Props: Equatable {
let isPresented: State<Bool>
}
private static let colors: [(Color, String)] = [
(.white, "white"),
(.red, "red"),
(.green, "green"),
(.blue, "blue"),
]
static func render(props: Props, hooks: Hooks) -> AnyNode {
let backgroundColor = hooks.state(0)
return props.isPresented.value ? ModalPresenter.node(
View.node(
.init(Style(
backgroundColor: colors[backgroundColor.value].0,
Edges.equal(to: .parent)
)),
StackView.node(.init(
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
), [
Button.node(.init(
onPress: Handler { props.isPresented.set(false) }
), "Close Modal"),
SegmentedControl.node(
.init(value: backgroundColor.value,
valueHandler: Handler(backgroundColor.set)),
colors.map { $0.1 }
),
])
)
) : Null.node()
}
}
struct ConstraintModal: LeafComponent {
struct Props: Equatable {
let isPresented: State<Bool>
}
static func render(props: Props, hooks: Hooks) -> AnyNode {
let left = hooks.state(0.5 as Float)
return props.isPresented.value ? ModalPresenter.node(
View.node(
.init(Style(backgroundColor: .white, Edges.equal(to: .parent))),
StackView.node(.init(
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
), [
Button.node(.init(
onPress: Handler { props.isPresented.set(false) }
), "Close Modal"),
Slider.node(.init(
value: left.value,
valueHandler: Handler(left.set),
Style(Width.equal(to: .parent))
)),
View.node(
.init(Style(backgroundColor: .red)),
Label.node(.init(
alignment: .center,
textColor: .white,
Style(Left.equal(to: .parent, constant: Double(left.value) * 200))
), "\(left.value)")
),
])
)
) : Null.node()
}
}
struct DatePickerModal: LeafComponent {
struct Props: Equatable {
let isPresented: State<Bool>
}
static func render(props: Props, hooks: Hooks) -> AnyNode {
let currentDateTime = Date()
let date = hooks.state(currentDateTime)
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
dateFormatter.timeStyle = .medium
let formattedDate = dateFormatter.string(from: date.value)
return props.isPresented.value ? ModalPresenter.node(
View.node(
.init(Style(backgroundColor: .white, Edges.equal(to: .parent))),
StackView.node(.init(
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
), [
Button.node(.init(
onPress: Handler { props.isPresented.set(false) }
), "Close Modal"),
Label.node(
.init(alignment: .center),
"\(formattedDate)"
),
DatePicker.node(
.init(
value: date.value,
valueHandler: Handler(date.set),
Style(Width.equal(to: .parent))
)
),
DatePicker.node(
.init(
isAnimated: false,
value: date.value,
valueHandler: Handler(date.set),
Style(Width.equal(to: .parent))
)
),
])
)
) : Null.node()
}
}
struct CALayerModal: LeafComponent {
struct Props: Equatable {
let isPresented: State<Bool>
}
static func render(props: Props, hooks: Hooks) -> AnyNode {
let state = hooks.state(0.5 as Float)
return props.isPresented.value ? ModalPresenter.node(
View.node(
.init(Style(backgroundColor: .white, Edges.equal(to: .parent))),
StackView.node(.init(
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
), [
Button.node(.init(
onPress: Handler { props.isPresented.set(false) }
), "Close Modal"),
Slider.node(.init(
value: state.value,
valueHandler: Handler(state.set),
Style(Width.equal(to: .parent))
)),
View.node(
.init(
Style(
backgroundColor: .red,
borderWidth: Float(state.value * 10),
cornerRadius: Float(state.value * 10),
opacity: Float(state.value),
Width.equal(to: .parent)
)
),
Label.node(.init(
alignment: .center,
textColor: .white,
Style(
[Top.equal(to: .parent, constant: Double(state.value) * 100),
Left.equal(to: .parent, constant: Double(state.value) * 200)]
)
), "\(state.value)")
),
])
)
) : Null.node()
}
}
struct Counter: LeafComponent {
struct Props: Equatable {
let initial: Int
}
static func render(props: Props, hooks: Hooks) -> AnyNode {
let count = hooks.state(props.initial)
let sliding = hooks.state(0.5 as Float)
let isStackModalPresented = hooks.state(false)
let isAnimationModalPresented = hooks.state(false)
let isTableModalPresented = hooks.state(false)
let isConstraintModalPresented = hooks.state(false)
let isDatePickerModalPresented = hooks.state(false)
let isCALayerModalPresented = hooks.state(false)
let switchState = hooks.state(true)
let stepperState = hooks.state(0.0)
let isEnabled = hooks.state(true)
let children = [
StackView.node(
.init(
alignment: .center,
axis: .horizontal,
spacing: 10.0
), [
Switch.node(
.init(
value: isEnabled.value,
valueHandler: Handler(isEnabled.set)
)
),
Label.node(
.init(alignment: .center),
"isEnabled \(isEnabled.value)"
),
]
),
Button.node(
.init(
isEnabled: isEnabled.value,
onPress: Handler { isStackModalPresented.set(true) }
),
"Present Navigation Modal"
),
Button.node(
.init(
isEnabled: isEnabled.value,
onPress: Handler { isAnimationModalPresented.set(true) }
),
"Present Simple Modal"
),
Button.node(
.init(
isEnabled: isEnabled.value,
onPress: Handler { isTableModalPresented.set(true) }
),
"Present Table Modal"
),
Button.node(
.init(
isEnabled: isEnabled.value,
onPress: Handler { isConstraintModalPresented.set(true) }
),
"Present Constraint Modal"
),
Button.node(
.init(
isEnabled: isEnabled.value,
onPress: Handler { isDatePickerModalPresented.set(true) }
),
"Present DatePicker Modal"
),
Button.node(
.init(
isEnabled: isEnabled.value,
onPress: Handler { isCALayerModalPresented.set(true) }
),
"Present Core Animation Layer Modal"
),
NavigationModal.node(.init(isPresented: isStackModalPresented)),
SimpleModal.node(.init(isPresented: isAnimationModalPresented)),
TableModal.node(.init(isPresented: isTableModalPresented)),
ConstraintModal.node(.init(isPresented: isConstraintModalPresented)),
DatePickerModal.node(.init(isPresented: isDatePickerModalPresented)),
CALayerModal.node(.init(isPresented: isCALayerModalPresented)),
] + (count.value < 15 ? [
StackView.node(
.init(
alignment: .center,
axis: .horizontal,
spacing: 10.0
), [
Button.node(
.init(
isEnabled: isEnabled.value,
onPress: Handler { count.set { $0 + 1 } }
),
"Increment"
),
Label.node(.init(alignment: .center), "\(count.value)"),
]
),
Slider.node(.init(
isEnabled: isEnabled.value,
value: sliding.value,
valueHandler: Handler(sliding.set),
Style(Width.equal(to: .parent))
)),
Label.node(.init(alignment: .center), "\(sliding.value)"),
StackView.node(
.init(
alignment: .center,
axis: .horizontal,
spacing: 10.0
), [
Switch.node(
.init(
isEnabled: isEnabled.value,
value: switchState.value,
valueHandler: Handler(switchState.set)
)
),
Label.node(.init(alignment: .center), "\(switchState.value)"),
]
),
StackView.node(
.init(
alignment: .center,
axis: .horizontal,
spacing: 10.0
), [
Stepper.node(
.init(
isEnabled: isEnabled.value,
value: stepperState.value,
valueHandler: Handler(stepperState.set)
)
),
Label.node(.init(alignment: .center), "\(stepperState.value)"),
]
),
] : [])
return StackView.node(
.init(
alignment: .center,
axis: .vertical,
distribution: .fillEqually,
Edges.equal(to: .parent)
),
children
)
}
}
struct App: PureLeafComponent {
typealias Props = Null
static func render(props _: Props) -> AnyNode {
return Counter.node(.init(initial: 1))
}
}

View File

@ -0,0 +1,66 @@
//
// RootRouter.swift
// Gluon_Example
//
// Created by Max Desiatov on 14/02/2019.
// Copyright © 2019 Gluon. All rights reserved.
//
import Gluon
enum AppRoute: String, CaseIterable {
case list = "Examples"
case counter
case controls
case constraints = "Auto Layout Constraints"
case modals = "Modal Presentation"
case datePicker = "Date Picker"
case layerProps = "Layer Props"
}
extension AppRoute: CustomStringConvertible {
var description: String { return rawValue.localizedCapitalized }
}
struct Router: NavigationRouter {
typealias Route = AppRoute
typealias Props = Null
static func route(
props: Null,
route: AppRoute,
push: @escaping (AppRoute) -> (),
pop: @escaping () -> (),
hooks: Hooks
) -> AnyNode {
let result: AnyNode
switch route {
case .list:
let model = AppRoute.allCases.filter { $0 != .list }
result = List.node(.init(
model: model,
onSelect: Handler { push(model[$0.item]) }
))
case .counter:
result = Counter.node(.init(countFrom: 1))
case .controls:
result = Controls.node()
case .constraints:
result = Constraints.node()
case .modals:
result = Modals.node()
case .datePicker:
result = DatePickers.node()
case .layerProps:
result = LayerProps.node()
}
return NavigationItem.node(
.init(title: route.description),
View.node(
.init(Style(backgroundColor: .white, Edges.equal(to: .parent))),
result
)
)
}
}

View File

@ -12,7 +12,7 @@ import UIKit
final class ViewController: GluonViewController {
override var node: AnyNode {
return App.node()
return NavigationPresenter<Router>.node(.init(initial: .list))
}
}

View File

@ -75,6 +75,8 @@
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 */; };
D1F718592215A074004E5951 /* LineBreakMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F718582215A073004E5951 /* LineBreakMode.swift */; };
D1F7185B2215A116004E5951 /* LineBreakMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1F7185A2215A116004E5951 /* LineBreakMode.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 */; };
@ -229,6 +231,8 @@
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>"; };
D1F718582215A073004E5951 /* LineBreakMode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineBreakMode.swift; sourceTree = "<group>"; };
D1F7185A2215A116004E5951 /* LineBreakMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LineBreakMode.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; };
@ -453,6 +457,7 @@
OBJ_20 /* Event.swift */,
OBJ_21 /* Rectangle.swift */,
D1C3A71F21F0C31600C6B884 /* Constraint */,
D1F7185A2215A116004E5951 /* LineBreakMode.swift */,
);
path = Props;
sourceTree = "<group>";
@ -547,11 +552,12 @@
D1EF77502205C579008829EC /* Constraint */,
OBJ_58 /* Color.swift */,
OBJ_59 /* Event.swift */,
D1C3A72221F0DC6300C6B884 /* Insets.swift */,
D1F718582215A073004E5951 /* LineBreakMode.swift */,
OBJ_60 /* Rectangle.swift */,
OBJ_61 /* Second.swift */,
OBJ_62 /* Style.swift */,
OBJ_63 /* TextAlignment.swift */,
D1C3A72221F0DC6300C6B884 /* Insets.swift */,
);
path = Props;
sourceTree = "<group>";
@ -809,6 +815,7 @@
A633561D21FB5C5400BCC525 /* Right.swift in Sources */,
D1EF77792205FD56008829EC /* Constrainable.swift in Sources */,
D1EF777D220745AB008829EC /* BaselineConstraint.swift in Sources */,
D1F7185B2215A116004E5951 /* LineBreakMode.swift in Sources */,
A633562521FB96EE00BCC525 /* CenterX.swift in Sources */,
OBJ_176 /* Slider.swift in Sources */,
OBJ_177 /* NavigationController.swift in Sources */,
@ -853,6 +860,7 @@
OBJ_101 /* Button.swift in Sources */,
OBJ_102 /* Label.swift in Sources */,
A62891FF21F5F07F001BE090 /* Stepper.swift in Sources */,
D1F718592215A074004E5951 /* LineBreakMode.swift in Sources */,
OBJ_104 /* ModalPresenter.swift in Sources */,
D1EC92F021F5B8000026C207 /* ListView.swift in Sources */,
D1EF77522205C579008829EC /* Constraint.swift in Sources */,

View File

@ -12,15 +12,21 @@ public struct Label: HostComponent {
}
public let alignment: TextAlignment
public let lineBreakMode: LineBreakMode
public let numberOfLines: Int
public let style: Style?
public let textColor: Color?
public init(
alignment: TextAlignment = .natural,
lineBreakMode: LineBreakMode = .truncateTail,
numberOfLines: Int = 1,
textColor: Color? = nil,
_ style: Style? = nil
) {
self.alignment = alignment
self.lineBreakMode = lineBreakMode
self.numberOfLines = numberOfLines
self.textColor = textColor
self.style = style
}

View File

@ -0,0 +1,15 @@
//
// LineBreakMode.swift
// Gluon
//
// Created by Max Desiatov on 14/02/2019.
//
public enum LineBreakMode {
case wordWrap
case charWrap
case clip
case truncateHead
case truncateTail
case truncateMiddle
}

View File

@ -37,6 +37,8 @@ extension Label: UIViewComponent {
_ children: String) {
let view = box.view
view.textAlignment = NSTextAlignment(props.alignment)
view.numberOfLines = props.numberOfLines
view.lineBreakMode = NSLineBreakMode(props.lineBreakMode)
props.textColor.flatMap { view.textColor = UIColor($0) }
view.text = children
}

View File

@ -9,6 +9,9 @@ import Gluon
import UIKit
final class GluonNavigationController: UINavigationController {
/// Used to prevent calling `onPop` if this navigation view controller is not
/// mounted yet
var isMounted = false
private let onPop: () -> ()
init(onPop: @escaping () -> ()) {
@ -24,6 +27,7 @@ final class GluonNavigationController: UINavigationController {
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
guard isMounted else { return }
onPop()
}
}
@ -65,6 +69,14 @@ extension NavigationController: UIHostComponent {
animated: props.presentAnimated,
completion: nil)
}
case let box as ViewBox<UIView>:
result.viewController.willMove(toParent: box.viewController)
// FIXME: replace with constraints
result.viewController.view.frame = box.view.frame
box.view.addSubview(result.viewController.view)
box.viewController.addChild(result.viewController)
result.viewController.didMove(toParent: box.viewController)
result.containerViewController.isMounted = true
default:
parentAssertionFailure()
}

View File

@ -0,0 +1,28 @@
//
// LineBreakMode.swift
// GluonUIKit
//
// Created by Max Desiatov on 14/02/2019.
//
import Gluon
import UIKit
extension NSLineBreakMode {
public init(_ mode: LineBreakMode) {
switch mode {
case .wordWrap:
self = .byWordWrapping
case .charWrap:
self = .byCharWrapping
case .clip:
self = .byClipping
case .truncateHead:
self = .byTruncatingHead
case .truncateTail:
self = .byTruncatingTail
case .truncateMiddle:
self = .byTruncatingMiddle
}
}
}