Skip to content

Commit

Permalink
Add HStack SwiftUI convenience APIs and rename .mono to .monospaced. (#…
Browse files Browse the repository at this point in the history
…115)

Both changes are being made to align with SwiftUI's APIs more closely.
  • Loading branch information
jverkoey authored Aug 7, 2024
1 parent a4035be commit 9477c5f
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 71 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Slipstream for SwiftUI developers

An exhaustive comparison of Slipstream features to equivalent SwiftUI features.

Slipstream provides types that match SwiftUI types in both name and functionality whenever possible.

Because of this, Slipstream and SwiftUI aren't meant to be used together in the same source file.
For example, if you try to import both:

```swift
import Slipstream
import SwiftUI

struct HelloWorld: View { // 'View' is ambiguous for type lookup in this context
var body: some View {
Text("Hello, world!")
}
}
```

This is working as intended. The trade-off here is that Slipstream feels familiar if you already
know SwiftUI.

If you must use SwiftUI and Slipstream in the same `.swift` file, you can disambiguate types like
so:

```swift
import Slipstream
import SwiftUI

struct HelloWorld: SwiftUI.View {
var body: some SwiftUI.View {
SwiftUI.Text("Hello, world!")
}
}
```

### How to read this guide

Code snippets with an `import SwiftUI` statement are SwiftUI examples, and snippets with an
`import Slipstream` statement are Slipstream examples. If no import is provided, then the snippet
works identically across both SwiftUI and Slipstream.

## Typography

SwiftUI's [Text](https://developer.apple.com/documentation/swiftui/text) is the building block for
most text in an application. In most cases, each Text instance will represent a distinct row of
text. This behavior is essentically identical with Slipstream's ``Text``, which corresponds to an
HTML `<p>` element.

```swift
struct ContentView: View {
var body: some View {
Text("Line 1")
Text("Line 2")
}
}
```

### Formatting text

#### Font design

SwiftUI and Slipstream both provide a ``View/fontDesign(_:condition:)-n2pt`` modifier:

```swift
struct ContentView: View {
var body: some View {
Text("Monospaced")
.fontDesign(.monospaced)
Text("Serif")
.fontDesign(.serif)
}
}
```

#### Font weight

SwiftUI and Slipstream both provide a ``View/fontWeight(_:condition:)-5zmpr`` modifier:

```swift
struct ContentView: View {
var body: some View {
Text("Bold")
.fontWeight(.bold)
}
}
```

They also both provide a ``View/bold(condition:)`` modifier:

```swift
struct ContentView: View {
var body: some View {
Text("Bold")
.bold()
}
}
```

## Stacks

SwiftUI's [HStack](https://developer.apple.com/documentation/swiftui/hstack) and
[VStack](https://developer.apple.com/documentation/swiftui/vstack) define essential primitives for
creating structured layouts, and equivalent types are provided in Slipstream.

### HStack

```swift
var body: some View {
HStack(
alignment: .top,
spacing: 10
) {
Text("Column 1")
Text("Column 2")
}
}
```

### VStack

Note: Slipstream's default alignment for VStack is ``VStackAlignment/leading`` instead of
``AlignItems/center``, which is SwiftUI's VStack alignment default.

```swift
var body: some View {
VStack(
alignment: .leading,
spacing: 10
) {
Text("Line 1")
Text("Line 2")
}
}
```
5 changes: 4 additions & 1 deletion Sources/Slipstream/Documentation.docc/Slipstream.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@ print(try renderHTML(HelloWorld()))

## Topics

### Guides

- <doc:SlipstreamforSwiftUIDevelopers>

### Architecture

- <doc:HowSlipstreamWorks>
- <doc:HowEnvironmentWorks>
- <doc:SlipstreamforSwiftUIDevelopers>

### Data and storage

Expand Down
39 changes: 39 additions & 0 deletions Sources/Slipstream/TailwindCSS/Layout/HStack.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
/// Constants that provide SwiftUI-like constants for specifying HStack item alignment.
@available(iOS 17.0, macOS 14.0, *)
public enum HStackAlignment {
/// Equivalent to ``AlignItems/start``.
case top

/// Equivalent to ``AlignItems/end``.
case bottom

var alignItemsEquivalent: AlignItems {
switch self {
case .top: return .start
case .bottom: return .end
}
}
}

/// A flex view that positions its views horizontally in the same direction as text.
///
/// ```swift
Expand Down Expand Up @@ -42,6 +59,28 @@ public struct HStack<Content: View>: View {
self.content = content
}

/// Creates a HStack using SwiftUI-like alignment terminology.
///
/// - Parameters:
/// - alignment: Determines how items within the stack are positioned along the y axis.
/// - spacing: If provided, the amount of spacing to add between child views. The value is
/// expressed in points, and mapped to the closest Tailwind CSS spacing class.
/// - reversed: If true, the contents will be arranged in the reverse direction of text.
/// - content: The content to display with this view.
public init(
alignment: HStackAlignment,
spacing: Double? = nil,
reversed: Bool = false,
@ViewBuilder content: @escaping () -> Content
) {
self.init(
alignment: alignment.alignItemsEquivalent,
spacing: spacing,
reversed: reversed,
content: content
)
}

@_documentation(visibility: private)
public var body: some View {
let container = Div { content() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
public enum FontDesign: String {
case sans
case serif
case mono
case monospaced = "mono"
}

extension View {
Expand Down
2 changes: 1 addition & 1 deletion Tests/SlipstreamTests/Sites/CatalogSiteTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private struct CatalogSite: View {
HStack(spacing: 10) {
H1("Heading 1")
.fontSize(.extraLarge)
.fontDesign(.mono)
.fontDesign(.monospaced)
.bold()
.textAlignment(.leading)
H2 {
Expand Down
10 changes: 10 additions & 0 deletions Tests/SlipstreamTests/TailwindCSS/Layout/HStackTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ struct HStackTests {
try #expect(renderHTML(HStack(reversed: true) {}) == #"<div class="flex flex-row-reverse items-center"></div>"#)
}

@Test func alignment() throws {
try #expect(renderHTML(HStack(alignment: .stretch) {}) == #"<div class="flex flex-row items-stretch"></div>"#)
try #expect(renderHTML(HStack(alignment: .start) {}) == #"<div class="flex flex-row items-start"></div>"#)
try #expect(renderHTML(HStack(alignment: .center) {}) == #"<div class="flex flex-row items-center"></div>"#)
try #expect(renderHTML(HStack(alignment: .end) {}) == #"<div class="flex flex-row items-end"></div>"#)
try #expect(renderHTML(HStack(alignment: .baseline) {}) == #"<div class="flex flex-row items-baseline"></div>"#)
try #expect(renderHTML(HStack(alignment: .top) {}) == #"<div class="flex flex-row items-start"></div>"#)
try #expect(renderHTML(HStack(alignment: .bottom) {}) == #"<div class="flex flex-row items-end"></div>"#)
}

@Test func withText() throws {
try #expect(renderHTML(HStack {
DOMString("Hello, world!")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ struct FontDesignTests {
@Test func enumerations() throws {
try #expect(renderHTML(Div {}.fontDesign(.sans)) == #"<div class="font-sans"></div>"#)
try #expect(renderHTML(Div {}.fontDesign(.serif)) == #"<div class="font-serif"></div>"#)
try #expect(renderHTML(Div {}.fontDesign(.mono)) == #"<div class="font-mono"></div>"#)
try #expect(renderHTML(Div {}.fontDesign(.monospaced)) == #"<div class="font-mono"></div>"#)
}

@Test func custom() throws {
Expand Down

0 comments on commit 9477c5f

Please sign in to comment.