Skip to content

Commit 0e1ded8

Browse files
authored
Merge pull request #150 from liveviewnative/menu
Add `Menu` and prototype for slot syntax
2 parents d1be5b1 + b1e2990 commit 0e1ded8

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
lines changed

Sources/LiveViewNative/BuiltinRegistry.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ struct BuiltinRegistry {
6060
EditButton()
6161
case "toggle":
6262
Toggle(element: element, context: context)
63+
case "menu":
64+
Menu(element: element, context: context)
6365
case "phx-form":
6466
PhxForm<R>(element: element, context: context)
6567
case "phx-submit-button":

Sources/LiveViewNative/LiveContext.swift

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77

88
import SwiftUI
9+
import LiveViewNativeCore
910

1011
/// The context provides information at initialization-time to views in a LiveView.
1112
public struct LiveContext<R: CustomRegistry> {
@@ -45,4 +46,39 @@ public struct LiveContext<R: CustomRegistry> {
4546
public func buildChildren(of element: ElementNode) -> some View {
4647
return coordinator.builder.fromNodes(element.children(), context: self)
4748
}
49+
50+
public func buildChildren(
51+
of element: ElementNode,
52+
withTagName tagName: String,
53+
namespace: String? = nil,
54+
includeDefaultSlot: Bool = false
55+
) -> some View {
56+
let children = element.children()
57+
let namedSlotChildren = children.filter({ child in
58+
if case let .element(element) = child.data,
59+
element.namespace == namespace,
60+
element.tag == tagName
61+
{
62+
return true
63+
} else {
64+
return false
65+
}
66+
})
67+
if namedSlotChildren.isEmpty && includeDefaultSlot {
68+
let defaultSlotChildren = children.filter({
69+
if case let .element(element) = $0.data {
70+
return element.namespace != tagName
71+
} else {
72+
return true
73+
}
74+
})
75+
return ForEach(defaultSlotChildren.map({ ($0.id, $0.children()) }), id: \.0) { subChildren in
76+
coordinator.builder.fromNodes(subChildren.1, context: self)
77+
}
78+
} else {
79+
return ForEach(namedSlotChildren.map({ ($0.id, $0.children()) }), id: \.0) { subChildren in
80+
coordinator.builder.fromNodes(subChildren.1, context: self)
81+
}
82+
}
83+
}
4884
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// Menu.swift
3+
//
4+
//
5+
// Created by Carson Katri on 1/19/23.
6+
//
7+
8+
import SwiftUI
9+
10+
struct Menu<R: CustomRegistry>: View {
11+
@ObservedElement private var element: ElementNode
12+
let context: LiveContext<R>
13+
14+
init(element: ElementNode, context: LiveContext<R>) {
15+
self.context = context
16+
}
17+
18+
public var body: some View {
19+
SwiftUI.Menu {
20+
context.buildChildren(of: element, withTagName: "content", namespace: "menu")
21+
} label: {
22+
context.buildChildren(of: element, withTagName: "label", namespace: "menu", includeDefaultSlot: true)
23+
}
24+
}
25+
}
26+
27+
fileprivate enum MenuStyle: String {
28+
case automatic
29+
case borderlessButton = "borderless-button"
30+
case button
31+
}
32+
33+
fileprivate extension View {
34+
@ViewBuilder
35+
func applyMenuStyle(_ style: MenuStyle) -> some View {
36+
switch style {
37+
case .automatic:
38+
self.menuStyle(.automatic)
39+
case .borderlessButton:
40+
self.menuStyle(.borderlessButton)
41+
case .button:
42+
self.menuStyle(.button)
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)