Skip to content

Commit b0a1dff

Browse files
committed
Add Menu control
1 parent 9a1c2e8 commit b0a1dff

1 file changed

Lines changed: 48 additions & 0 deletions

File tree

  • AndroidSwiftUICore/Sources/AndroidSwiftUICore/Primitives
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//
2+
// Menu.swift
3+
// AndroidSwiftUICore
4+
//
5+
// A button that presents a dropdown of actions. The content's buttons become
6+
// the menu items; the interpreter shows them in a Compose DropdownMenu.
7+
//
8+
9+
public struct Menu<Label: View, Content: View>: View {
10+
11+
internal let label: Label
12+
internal let content: Content
13+
14+
public init(@ViewBuilder content: () -> Content, @ViewBuilder label: () -> Label) {
15+
self.content = content()
16+
self.label = label()
17+
}
18+
19+
public typealias Body = Never
20+
}
21+
22+
public extension Menu where Label == Text {
23+
init<S: StringProtocol>(_ title: S, @ViewBuilder content: () -> Content) {
24+
self.init(content: content) { Text(title) }
25+
}
26+
}
27+
28+
extension Menu: PrimitiveView {
29+
public func _render(in context: ResolveContext) -> RenderNode {
30+
let labelNodes = Evaluator.resolveChildren(label, context.descending("label"))
31+
let title = labelNodes.lazy.compactMap(firstTextString).first ?? ""
32+
return RenderNode(
33+
type: "Menu",
34+
id: context.path,
35+
props: ["label": .string(title)],
36+
children: Evaluator.resolveChildren(content, context.descending("content"))
37+
)
38+
}
39+
}
40+
41+
/// The first Text content found in a node subtree, if any.
42+
internal func firstTextString(_ node: RenderNode) -> String? {
43+
if node.type == "Text", case .string(let text)? = node.props["text"] { return text }
44+
for child in node.children {
45+
if let text = firstTextString(child) { return text }
46+
}
47+
return nil
48+
}

0 commit comments

Comments
 (0)