turbo-native-initializer 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt +1 -0
  4. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponent.kt.tt +78 -0
  5. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponentAdapter.kt.tt +57 -0
  6. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt +1 -1
  7. data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/menu_component_adapter_row.xml +22 -0
  8. data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/menu_component_bottom_sheet.xml +33 -0
  9. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt +1 -0
  10. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponent.kt.tt +78 -0
  11. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponentAdapter.kt.tt +57 -0
  12. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt +1 -1
  13. data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/menu_component_adapter_row.xml +22 -0
  14. data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/menu_component_bottom_sheet.xml +33 -0
  15. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/BridgeComponent+App.swift +6 -1
  16. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/MenuComponent.swift +77 -0
  17. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.pbxproj.tt +4 -0
  18. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/BridgeComponent+App.swift +6 -1
  19. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/MenuComponent.swift +77 -0
  20. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.pbxproj.tt +4 -0
  21. data/lib/turbo_native_initializer/version.rb +1 -1
  22. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c1f5d9801c82d83f38a465a6375d87b1b90ac03a19bcf292b78555f9bc925cee
4
- data.tar.gz: 5405c39714a62d1f5823d46be8bb9935ac52743f2c02d9a0b544b82f73fd6f40
3
+ metadata.gz: d89f212990b6d8acf8b1f37073d8b892b7f043bdf4a19d11997fabf620ba4ca1
4
+ data.tar.gz: d2b4ea5ee37d49c0d3eb23d4bb56894407a3ff5d535058ac21c54292439eae45
5
5
  SHA512:
6
- metadata.gz: 65fbcd8000abe1b6c9c4c0c0ca4ab715ecbc8ac828ca1b859bc93b7d9a32718163913b12b881ee6ea2e41c244df50c7d869dc8fe420d86a09e9dfcd50b274a28
7
- data.tar.gz: d3c907983b7c31767b99c9bd34bb0b0185fbb4bb8028c19a6c5e6648c739baf99a08e6a4318207ed25644a41c2a6d4d84bcabd529288dd85004c42b52f21f0a4
6
+ metadata.gz: 3002e6b664dd0c92f04c2535707e8008a5c38e6d61e129bf3d376aa586bf2906228323a6947910b7a9d107dbcc2e33403ee1a2de508d444d97d37cea54fa1434
7
+ data.tar.gz: 6884c3e84fef8d9462387a1ba5c787befe6b79053378bf6d21c5e65f68a3d31f117abf94a9eb0fb671581317968c17a3727247598b657b1171bf149316dbe00c
data/README.md CHANGED
@@ -12,7 +12,7 @@ A turbo native project generator for iOS and Android.
12
12
  - Added `visitable` property in order to avoid visits. (iOS)
13
13
  - Added support for tab navigation. (iOS/Android)
14
14
  - Added support for flash messages. (iOS/Android)
15
- - Added navigation bar button component. (iOS/Android)
15
+ - Integrated with the gem [strada-rails](https://github.com/lazaronixon/strada-rails).
16
16
 
17
17
  ## Installation
18
18
 
@@ -6,4 +6,5 @@ val bridgeComponentFactories = listOf(
6
6
  BridgeComponentFactory("form", ::FormComponent),
7
7
  BridgeComponentFactory("nav-button", ::NavButtonComponent),
8
8
  BridgeComponentFactory("flash-message", ::FlashMessageComponent),
9
+ BridgeComponentFactory("menu", ::MenuComponent)
9
10
  )
@@ -0,0 +1,78 @@
1
+ package <%= package_name %>.strada
2
+
3
+ import android.util.Log
4
+ import android.view.LayoutInflater
5
+ import androidx.fragment.app.Fragment
6
+ import androidx.recyclerview.widget.LinearLayoutManager
7
+ import com.google.android.material.bottomsheet.BottomSheetDialog
8
+ import dev.hotwire.strada.BridgeComponent
9
+ import dev.hotwire.strada.BridgeDelegate
10
+ import dev.hotwire.strada.Message
11
+ import <%= package_name %>.base.NavDestination
12
+ import <%= package_name %>.databinding.MenuComponentBottomSheetBinding
13
+ import kotlinx.serialization.Serializable
14
+
15
+ class MenuComponent(
16
+ name: String,
17
+ private val delegate: BridgeDelegate<NavDestination>
18
+ ) : BridgeComponent<NavDestination>(name, delegate) {
19
+
20
+ private val fragment: Fragment
21
+ get() = delegate.destination.fragment
22
+
23
+ override fun onReceive(message: Message) {
24
+ if (message.event == "display") {
25
+ handleDisplayEvent(message)
26
+ } else {
27
+ Log.w("TurboNative", "Unknown event for message: $message")
28
+ }
29
+ }
30
+
31
+ private fun handleDisplayEvent(message: Message) {
32
+ val data = message.data<MessageData>() ?: return
33
+ showBottomSheet(data.title, data.items)
34
+ }
35
+
36
+ private fun showBottomSheet(title: String, items: List<Item>) {
37
+ val view = fragment.view?.rootView ?: return
38
+ val inflater = LayoutInflater.from(view.context)
39
+ val bottomSheet = BottomSheetDialog(view.context)
40
+ val binding = MenuComponentBottomSheetBinding.inflate(inflater)
41
+
42
+ binding.toolbar.title = title
43
+ binding.recyclerView.layoutManager = LinearLayoutManager(view.context)
44
+ binding.recyclerView.adapter = MenuComponentAdapter().apply {
45
+ setData(items)
46
+ setListener {
47
+ bottomSheet.dismiss()
48
+ onItemSelected(it)
49
+ }
50
+ }
51
+
52
+ bottomSheet.apply {
53
+ setContentView(binding.root)
54
+ show()
55
+ }
56
+ }
57
+
58
+ private fun onItemSelected(item: Item) {
59
+ replyTo("display", SelectionMessageData(item.index))
60
+ }
61
+
62
+ @Serializable
63
+ data class MessageData(
64
+ val title: String,
65
+ val items: List<Item>
66
+ )
67
+
68
+ @Serializable
69
+ data class Item(
70
+ val title: String,
71
+ val index: Int
72
+ )
73
+
74
+ @Serializable
75
+ data class SelectionMessageData(
76
+ val selectedIndex: Int
77
+ )
78
+ }
@@ -0,0 +1,57 @@
1
+ package <%= package_name %>.strada
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.view.LayoutInflater
5
+ import android.view.View
6
+ import android.view.ViewGroup
7
+ import androidx.recyclerview.widget.RecyclerView
8
+ import com.google.android.material.textview.MaterialTextView
9
+ import <%= package_name %>.R
10
+
11
+ class MenuComponentAdapter : RecyclerView.Adapter<MenuComponentAdapter.ViewHolder>() {
12
+ private val type = R.layout.menu_component_adapter_row
13
+ private var action: ((MenuComponent.Item) -> Unit)? = null
14
+
15
+ private var items = emptyList<MenuComponent.Item>()
16
+ @SuppressLint("NotifyDataSetChanged")
17
+ set(value) {
18
+ field = value
19
+ notifyDataSetChanged()
20
+ }
21
+
22
+ fun setData(items: List<MenuComponent.Item>) {
23
+ this.items = items
24
+ }
25
+
26
+ fun setListener(action: (item: MenuComponent.Item) -> Unit) {
27
+ this.action = action
28
+ }
29
+
30
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
31
+ holder.bind(items[position])
32
+ }
33
+
34
+ override fun getItemCount(): Int {
35
+ return items.count()
36
+ }
37
+
38
+ override fun getItemViewType(position: Int): Int {
39
+ return type
40
+ }
41
+
42
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
43
+ val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
44
+ return ViewHolder(view)
45
+ }
46
+
47
+ inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
48
+ private val textView: MaterialTextView = view.findViewById(R.id.title)
49
+
50
+ fun bind(item: MenuComponent.Item) {
51
+ textView.text = item.title
52
+ itemView.setOnClickListener {
53
+ action?.invoke(item)
54
+ }
55
+ }
56
+ }
57
+ }
@@ -19,7 +19,7 @@ class NavButtonComponent(
19
19
  private val delegate: BridgeDelegate<NavDestination>
20
20
  ) : BridgeComponent<NavDestination>(name, delegate) {
21
21
 
22
- private val navButtonItemId = 20
22
+ private val navButtonItemId = 10
23
23
  private var navButtonMenuItem: MenuItem? = null
24
24
  private val fragment: Fragment
25
25
  get() = delegate.destination.fragment
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <androidx.constraintlayout.widget.ConstraintLayout
3
+ xmlns:android="http://schemas.android.com/apk/res/android"
4
+ xmlns:app="http://schemas.android.com/apk/res-auto"
5
+ android:layout_width="match_parent"
6
+ android:layout_height="wrap_content"
7
+ android:background="?selectableItemBackground">
8
+
9
+ <com.google.android.material.textview.MaterialTextView
10
+ style="@style/TextAppearance.AppCompat.Menu"
11
+ android:id="@+id/title"
12
+ android:layout_width="match_parent"
13
+ android:layout_height="wrap_content"
14
+ android:minHeight="48dp"
15
+ android:paddingLeft="16dp"
16
+ android:paddingTop="8dp"
17
+ android:paddingRight="16dp"
18
+ android:paddingBottom="8dp"
19
+ android:textSize="18sp"
20
+ app:layout_constraintTop_toTopOf="parent" />
21
+
22
+ </androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <androidx.constraintlayout.widget.ConstraintLayout
3
+ xmlns:android="http://schemas.android.com/apk/res/android"
4
+ xmlns:app="http://schemas.android.com/apk/res-auto"
5
+ android:layout_width="match_parent"
6
+ android:layout_height="match_parent"
7
+ android:background="@color/transparent">
8
+
9
+ <com.google.android.material.appbar.AppBarLayout
10
+ android:id="@+id/app_bar"
11
+ android:layout_width="match_parent"
12
+ android:layout_height="wrap_content"
13
+ android:background="@color/transparent"
14
+ android:stateListAnimator="@null"
15
+ app:layout_constraintEnd_toEndOf="parent"
16
+ app:layout_constraintStart_toStartOf="parent"
17
+ app:layout_constraintTop_toTopOf="parent">
18
+
19
+ <com.google.android.material.appbar.MaterialToolbar
20
+ android:id="@+id/toolbar"
21
+ android:layout_width="match_parent"
22
+ android:layout_height="wrap_content" />
23
+
24
+ </com.google.android.material.appbar.AppBarLayout>
25
+
26
+ <androidx.recyclerview.widget.RecyclerView
27
+ android:id="@+id/recycler_view"
28
+ android:layout_width="match_parent"
29
+ android:layout_height="wrap_content"
30
+ app:layout_constraintBottom_toBottomOf="parent"
31
+ app:layout_constraintTop_toBottomOf="@+id/app_bar" />
32
+
33
+ </androidx.constraintlayout.widget.ConstraintLayout>
@@ -6,4 +6,5 @@ val bridgeComponentFactories = listOf(
6
6
  BridgeComponentFactory("form", ::FormComponent),
7
7
  BridgeComponentFactory("nav-button", ::NavButtonComponent),
8
8
  BridgeComponentFactory("flash-message", ::FlashMessageComponent),
9
+ BridgeComponentFactory("menu", ::MenuComponent)
9
10
  )
@@ -0,0 +1,78 @@
1
+ package <%= package_name %>.strada
2
+
3
+ import android.util.Log
4
+ import android.view.LayoutInflater
5
+ import androidx.fragment.app.Fragment
6
+ import androidx.recyclerview.widget.LinearLayoutManager
7
+ import com.google.android.material.bottomsheet.BottomSheetDialog
8
+ import dev.hotwire.strada.BridgeComponent
9
+ import dev.hotwire.strada.BridgeDelegate
10
+ import dev.hotwire.strada.Message
11
+ import <%= package_name %>.base.NavDestination
12
+ import <%= package_name %>.databinding.MenuComponentBottomSheetBinding
13
+ import kotlinx.serialization.Serializable
14
+
15
+ class MenuComponent(
16
+ name: String,
17
+ private val delegate: BridgeDelegate<NavDestination>
18
+ ) : BridgeComponent<NavDestination>(name, delegate) {
19
+
20
+ private val fragment: Fragment
21
+ get() = delegate.destination.fragment
22
+
23
+ override fun onReceive(message: Message) {
24
+ if (message.event == "display") {
25
+ handleDisplayEvent(message)
26
+ } else {
27
+ Log.w("TurboNative", "Unknown event for message: $message")
28
+ }
29
+ }
30
+
31
+ private fun handleDisplayEvent(message: Message) {
32
+ val data = message.data<MessageData>() ?: return
33
+ showBottomSheet(data.title, data.items)
34
+ }
35
+
36
+ private fun showBottomSheet(title: String, items: List<Item>) {
37
+ val view = fragment.view?.rootView ?: return
38
+ val inflater = LayoutInflater.from(view.context)
39
+ val bottomSheet = BottomSheetDialog(view.context)
40
+ val binding = MenuComponentBottomSheetBinding.inflate(inflater)
41
+
42
+ binding.toolbar.title = title
43
+ binding.recyclerView.layoutManager = LinearLayoutManager(view.context)
44
+ binding.recyclerView.adapter = MenuComponentAdapter().apply {
45
+ setData(items)
46
+ setListener {
47
+ bottomSheet.dismiss()
48
+ onItemSelected(it)
49
+ }
50
+ }
51
+
52
+ bottomSheet.apply {
53
+ setContentView(binding.root)
54
+ show()
55
+ }
56
+ }
57
+
58
+ private fun onItemSelected(item: Item) {
59
+ replyTo("display", SelectionMessageData(item.index))
60
+ }
61
+
62
+ @Serializable
63
+ data class MessageData(
64
+ val title: String,
65
+ val items: List<Item>
66
+ )
67
+
68
+ @Serializable
69
+ data class Item(
70
+ val title: String,
71
+ val index: Int
72
+ )
73
+
74
+ @Serializable
75
+ data class SelectionMessageData(
76
+ val selectedIndex: Int
77
+ )
78
+ }
@@ -0,0 +1,57 @@
1
+ package <%= package_name %>.strada
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.view.LayoutInflater
5
+ import android.view.View
6
+ import android.view.ViewGroup
7
+ import androidx.recyclerview.widget.RecyclerView
8
+ import com.google.android.material.textview.MaterialTextView
9
+ import <%= package_name %>.R
10
+
11
+ class MenuComponentAdapter : RecyclerView.Adapter<MenuComponentAdapter.ViewHolder>() {
12
+ private val type = R.layout.menu_component_adapter_row
13
+ private var action: ((MenuComponent.Item) -> Unit)? = null
14
+
15
+ private var items = emptyList<MenuComponent.Item>()
16
+ @SuppressLint("NotifyDataSetChanged")
17
+ set(value) {
18
+ field = value
19
+ notifyDataSetChanged()
20
+ }
21
+
22
+ fun setData(items: List<MenuComponent.Item>) {
23
+ this.items = items
24
+ }
25
+
26
+ fun setListener(action: (item: MenuComponent.Item) -> Unit) {
27
+ this.action = action
28
+ }
29
+
30
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
31
+ holder.bind(items[position])
32
+ }
33
+
34
+ override fun getItemCount(): Int {
35
+ return items.count()
36
+ }
37
+
38
+ override fun getItemViewType(position: Int): Int {
39
+ return type
40
+ }
41
+
42
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
43
+ val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
44
+ return ViewHolder(view)
45
+ }
46
+
47
+ inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
48
+ private val textView: MaterialTextView = view.findViewById(R.id.title)
49
+
50
+ fun bind(item: MenuComponent.Item) {
51
+ textView.text = item.title
52
+ itemView.setOnClickListener {
53
+ action?.invoke(item)
54
+ }
55
+ }
56
+ }
57
+ }
@@ -19,7 +19,7 @@ class NavButtonComponent(
19
19
  private val delegate: BridgeDelegate<NavDestination>
20
20
  ) : BridgeComponent<NavDestination>(name, delegate) {
21
21
 
22
- private val navButtonItemId = 20
22
+ private val navButtonItemId = 10
23
23
  private var navButtonMenuItem: MenuItem? = null
24
24
  private val fragment: Fragment
25
25
  get() = delegate.destination.fragment
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <androidx.constraintlayout.widget.ConstraintLayout
3
+ xmlns:android="http://schemas.android.com/apk/res/android"
4
+ xmlns:app="http://schemas.android.com/apk/res-auto"
5
+ android:layout_width="match_parent"
6
+ android:layout_height="wrap_content"
7
+ android:background="?selectableItemBackground">
8
+
9
+ <com.google.android.material.textview.MaterialTextView
10
+ style="@style/TextAppearance.AppCompat.Menu"
11
+ android:id="@+id/title"
12
+ android:layout_width="match_parent"
13
+ android:layout_height="wrap_content"
14
+ android:minHeight="48dp"
15
+ android:paddingLeft="16dp"
16
+ android:paddingTop="8dp"
17
+ android:paddingRight="16dp"
18
+ android:paddingBottom="8dp"
19
+ android:textSize="18sp"
20
+ app:layout_constraintTop_toTopOf="parent" />
21
+
22
+ </androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,33 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <androidx.constraintlayout.widget.ConstraintLayout
3
+ xmlns:android="http://schemas.android.com/apk/res/android"
4
+ xmlns:app="http://schemas.android.com/apk/res-auto"
5
+ android:layout_width="match_parent"
6
+ android:layout_height="match_parent"
7
+ android:background="@color/transparent">
8
+
9
+ <com.google.android.material.appbar.AppBarLayout
10
+ android:id="@+id/app_bar"
11
+ android:layout_width="match_parent"
12
+ android:layout_height="wrap_content"
13
+ android:background="@color/transparent"
14
+ android:stateListAnimator="@null"
15
+ app:layout_constraintEnd_toEndOf="parent"
16
+ app:layout_constraintStart_toStartOf="parent"
17
+ app:layout_constraintTop_toTopOf="parent">
18
+
19
+ <com.google.android.material.appbar.MaterialToolbar
20
+ android:id="@+id/toolbar"
21
+ android:layout_width="match_parent"
22
+ android:layout_height="wrap_content" />
23
+
24
+ </com.google.android.material.appbar.AppBarLayout>
25
+
26
+ <androidx.recyclerview.widget.RecyclerView
27
+ android:id="@+id/recycler_view"
28
+ android:layout_width="match_parent"
29
+ android:layout_height="wrap_content"
30
+ app:layout_constraintBottom_toBottomOf="parent"
31
+ app:layout_constraintTop_toBottomOf="@+id/app_bar" />
32
+
33
+ </androidx.constraintlayout.widget.ConstraintLayout>
@@ -3,6 +3,11 @@ import Strada
3
3
 
4
4
  extension BridgeComponent {
5
5
  static var allTypes: [BridgeComponent.Type] {
6
- [FormComponent.self, NavButtonComponent.self, FlashMessageComponent.self]
6
+ [
7
+ FormComponent.self,
8
+ NavButtonComponent.self,
9
+ MenuComponent.self,
10
+ FlashMessageComponent.self
11
+ ]
7
12
  }
8
13
  }
@@ -0,0 +1,77 @@
1
+ import Foundation
2
+ import Strada
3
+ import UIKit
4
+
5
+ final class MenuComponent: BridgeComponent {
6
+ override class var name: String { "menu" }
7
+
8
+ override func onReceive(message: Message) {
9
+ guard let event = Event(rawValue: message.event) else {
10
+ return
11
+ }
12
+
13
+ if event == .display {
14
+ handleDisplayEvent(message: message)
15
+ }
16
+ }
17
+
18
+ // MARK: Private
19
+
20
+ private var viewController: UIViewController? {
21
+ delegate.destination as? UIViewController
22
+ }
23
+
24
+ private func handleDisplayEvent(message: Message) {
25
+ guard let data: MessageData = message.data() else { return }
26
+ showAlertSheet(with: data.title, items: data.items)
27
+ }
28
+
29
+ private func showAlertSheet(with title: String, items: [Item]) {
30
+ let alertController = UIAlertController(title: title,
31
+ message: nil,
32
+ preferredStyle: .actionSheet)
33
+
34
+ for item in items {
35
+ let action = UIAlertAction(title: item.title, style: .default) {[weak self] _ in
36
+ self?.onItemSelected(item: item)
37
+ }
38
+ alertController.addAction(action)
39
+ }
40
+
41
+ let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
42
+ alertController.addAction(cancelAction)
43
+
44
+ viewController?.present(alertController, animated: true)
45
+ }
46
+
47
+ private func onItemSelected(item: Item) {
48
+ reply(to: Event.display.rawValue,
49
+ with: SelectionMessageData(selectedIndex: item.index))
50
+ }
51
+ }
52
+
53
+ // MARK: Events
54
+
55
+ private extension MenuComponent {
56
+ enum Event: String {
57
+ case display
58
+ }
59
+ }
60
+
61
+ // MARK: Message data
62
+
63
+ private extension MenuComponent {
64
+ struct MessageData: Decodable {
65
+ let title: String
66
+ let items: [Item]
67
+ }
68
+
69
+ struct Item: Decodable {
70
+ let title: String
71
+ let index: Int
72
+ }
73
+
74
+ struct SelectionMessageData: Encodable {
75
+ let selectedIndex:Int
76
+ }
77
+ }
@@ -14,6 +14,7 @@
14
14
  5D281BB42ABC0CB0001CE599 /* BridgeComponent+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D281BB32ABC0CB0001CE599 /* BridgeComponent+App.swift */; };
15
15
  5D281BB62ABC0CB9001CE599 /* FormComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D281BB52ABC0CB9001CE599 /* FormComponent.swift */; };
16
16
  5D91C5402AC538960046D872 /* FlashMessageComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D91C53F2AC538960046D872 /* FlashMessageComponent.swift */; };
17
+ 5DA9C92F2AC7E38100FEA7E6 /* MenuComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA9C92E2AC7E38100FEA7E6 /* MenuComponent.swift */; };
17
18
  5DAF71B72AC3FB53002D04FE /* UIViewController+Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAF71B62AC3FB53002D04FE /* UIViewController+Toast.swift */; };
18
19
  5DCC50D42A959DF900B529A0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DCC50D32A959DF900B529A0 /* AppDelegate.swift */; };
19
20
  5DCC50D62A959DF900B529A0 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DCC50D52A959DF900B529A0 /* SceneDelegate.swift */; };
@@ -35,6 +36,7 @@
35
36
  5D281BB32ABC0CB0001CE599 /* BridgeComponent+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BridgeComponent+App.swift"; sourceTree = "<group>"; };
36
37
  5D281BB52ABC0CB9001CE599 /* FormComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormComponent.swift; sourceTree = "<group>"; };
37
38
  5D91C53F2AC538960046D872 /* FlashMessageComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlashMessageComponent.swift; sourceTree = "<group>"; };
39
+ 5DA9C92E2AC7E38100FEA7E6 /* MenuComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuComponent.swift; sourceTree = "<group>"; };
38
40
  5DAF71B62AC3FB53002D04FE /* UIViewController+Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Toast.swift"; sourceTree = "<group>"; };
39
41
  5DCC50D02A959DF900B529A0 /* <%= name %>.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = <%= name %>.app; sourceTree = BUILT_PRODUCTS_DIR; };
40
42
  5DCC50D32A959DF900B529A0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -70,6 +72,7 @@
70
72
  5D281BB52ABC0CB9001CE599 /* FormComponent.swift */,
71
73
  5D1F2EC02ABEB12300B2819A /* NavButtonComponent.swift */,
72
74
  5D91C53F2AC538960046D872 /* FlashMessageComponent.swift */,
75
+ 5DA9C92E2AC7E38100FEA7E6 /* MenuComponent.swift */,
73
76
  );
74
77
  path = Strada;
75
78
  sourceTree = "<group>";
@@ -224,6 +227,7 @@
224
227
  isa = PBXSourcesBuildPhase;
225
228
  buildActionMask = 2147483647;
226
229
  files = (
230
+ 5DA9C92F2AC7E38100FEA7E6 /* MenuComponent.swift in Sources */,
227
231
  5DCC50D82A959DF900B529A0 /* TurboWebViewController.swift in Sources */,
228
232
  5DDD58812AA9A8BE00FAC961 /* NumbersViewController.swift in Sources */,
229
233
  5D91C5402AC538960046D872 /* FlashMessageComponent.swift in Sources */,
@@ -3,6 +3,11 @@ import Strada
3
3
 
4
4
  extension BridgeComponent {
5
5
  static var allTypes: [BridgeComponent.Type] {
6
- [FormComponent.self, NavButtonComponent.self, FlashMessageComponent.self]
6
+ [
7
+ FormComponent.self,
8
+ NavButtonComponent.self,
9
+ MenuComponent.self,
10
+ FlashMessageComponent.self
11
+ ]
7
12
  }
8
13
  }
@@ -0,0 +1,77 @@
1
+ import Foundation
2
+ import Strada
3
+ import UIKit
4
+
5
+ final class MenuComponent: BridgeComponent {
6
+ override class var name: String { "menu" }
7
+
8
+ override func onReceive(message: Message) {
9
+ guard let event = Event(rawValue: message.event) else {
10
+ return
11
+ }
12
+
13
+ if event == .display {
14
+ handleDisplayEvent(message: message)
15
+ }
16
+ }
17
+
18
+ // MARK: Private
19
+
20
+ private var viewController: UIViewController? {
21
+ delegate.destination as? UIViewController
22
+ }
23
+
24
+ private func handleDisplayEvent(message: Message) {
25
+ guard let data: MessageData = message.data() else { return }
26
+ showAlertSheet(with: data.title, items: data.items)
27
+ }
28
+
29
+ private func showAlertSheet(with title: String, items: [Item]) {
30
+ let alertController = UIAlertController(title: title,
31
+ message: nil,
32
+ preferredStyle: .actionSheet)
33
+
34
+ for item in items {
35
+ let action = UIAlertAction(title: item.title, style: .default) {[weak self] _ in
36
+ self?.onItemSelected(item: item)
37
+ }
38
+ alertController.addAction(action)
39
+ }
40
+
41
+ let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
42
+ alertController.addAction(cancelAction)
43
+
44
+ viewController?.present(alertController, animated: true)
45
+ }
46
+
47
+ private func onItemSelected(item: Item) {
48
+ reply(to: Event.display.rawValue,
49
+ with: SelectionMessageData(selectedIndex: item.index))
50
+ }
51
+ }
52
+
53
+ // MARK: Events
54
+
55
+ private extension MenuComponent {
56
+ enum Event: String {
57
+ case display
58
+ }
59
+ }
60
+
61
+ // MARK: Message data
62
+
63
+ private extension MenuComponent {
64
+ struct MessageData: Decodable {
65
+ let title: String
66
+ let items: [Item]
67
+ }
68
+
69
+ struct Item: Decodable {
70
+ let title: String
71
+ let index: Int
72
+ }
73
+
74
+ struct SelectionMessageData: Encodable {
75
+ let selectedIndex:Int
76
+ }
77
+ }
@@ -15,6 +15,7 @@
15
15
  5D8AB2FC2ABC117F00C6A82F /* BridgeComponent+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D8AB2FA2ABC117F00C6A82F /* BridgeComponent+App.swift */; };
16
16
  5D8AB2FE2ABC119900C6A82F /* WKWebViewConfiguration+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D8AB2FD2ABC119900C6A82F /* WKWebViewConfiguration+App.swift */; };
17
17
  5D8AB3012ABC127300C6A82F /* Strada in Frameworks */ = {isa = PBXBuildFile; productRef = 5D8AB3002ABC127300C6A82F /* Strada */; };
18
+ 5DA9C9352AC874D200FEA7E6 /* MenuComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DA9C9342AC874D200FEA7E6 /* MenuComponent.swift */; };
18
19
  5DCC50D42A959DF900B529A0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DCC50D32A959DF900B529A0 /* AppDelegate.swift */; };
19
20
  5DCC50D62A959DF900B529A0 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DCC50D52A959DF900B529A0 /* SceneDelegate.swift */; };
20
21
  5DCC50D82A959DF900B529A0 /* TurboWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DCC50D72A959DF900B529A0 /* TurboWebViewController.swift */; };
@@ -36,6 +37,7 @@
36
37
  5D8AB2F92ABC117F00C6A82F /* FormComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormComponent.swift; sourceTree = "<group>"; };
37
38
  5D8AB2FA2ABC117F00C6A82F /* BridgeComponent+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BridgeComponent+App.swift"; sourceTree = "<group>"; };
38
39
  5D8AB2FD2ABC119900C6A82F /* WKWebViewConfiguration+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WKWebViewConfiguration+App.swift"; sourceTree = "<group>"; };
40
+ 5DA9C9342AC874D200FEA7E6 /* MenuComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuComponent.swift; sourceTree = "<group>"; };
39
41
  5DCC50D02A959DF900B529A0 /* <%= name %>.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = <%= name %>.app; sourceTree = BUILT_PRODUCTS_DIR; };
40
42
  5DCC50D32A959DF900B529A0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
41
43
  5DCC50D52A959DF900B529A0 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
@@ -79,6 +81,7 @@
79
81
  5D8AB2F92ABC117F00C6A82F /* FormComponent.swift */,
80
82
  5D1F2EC02ABEB12300B2819A /* NavButtonComponent.swift */,
81
83
  5D2381402AC53D8900FABA1F /* FlashMessageComponent.swift */,
84
+ 5DA9C9342AC874D200FEA7E6 /* MenuComponent.swift */,
82
85
  );
83
86
  path = Strada;
84
87
  sourceTree = "<group>";
@@ -224,6 +227,7 @@
224
227
  isa = PBXSourcesBuildPhase;
225
228
  buildActionMask = 2147483647;
226
229
  files = (
230
+ 5DA9C9352AC874D200FEA7E6 /* MenuComponent.swift in Sources */,
227
231
  5DCC50D82A959DF900B529A0 /* TurboWebViewController.swift in Sources */,
228
232
  5DDD58812AA9A8BE00FAC961 /* NumbersViewController.swift in Sources */,
229
233
  5D2381412AC53D8900FABA1F /* FlashMessageComponent.swift in Sources */,
@@ -1,3 +1,3 @@
1
1
  module TurboNativeInitializer
2
- VERSION = "0.0.12"
2
+ VERSION = "0.0.14"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbo-native-initializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-29 00:00:00.000000000 Z
11
+ date: 2023-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -52,6 +52,8 @@ files:
52
52
  - lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt
53
53
  - lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FlashMessageComponent.kt.tt
54
54
  - lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FormComponent.kt.tt
55
+ - lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponent.kt.tt
56
+ - lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponentAdapter.kt.tt
55
57
  - lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt
56
58
  - lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Constants.kt.tt
57
59
  - lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Extension.kt.tt
@@ -73,6 +75,8 @@ files:
73
75
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/form_component_submit.xml
74
76
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/fragment_native.xml
75
77
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/fragment_web_home.xml
78
+ - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/menu_component_adapter_row.xml
79
+ - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/menu_component_bottom_sheet.xml
76
80
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/nav_button_component.xml
77
81
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
78
82
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -105,6 +109,8 @@ files:
105
109
  - lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt
106
110
  - lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FlashMessageComponent.kt.tt
107
111
  - lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FormComponent.kt.tt
112
+ - lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponent.kt.tt
113
+ - lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponentAdapter.kt.tt
108
114
  - lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt
109
115
  - lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Constants.kt.tt
110
116
  - lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Extension.kt.tt
@@ -128,6 +134,8 @@ files:
128
134
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/form_component_submit.xml
129
135
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/fragment_native.xml
130
136
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/fragment_web_home.xml
137
+ - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/menu_component_adapter_row.xml
138
+ - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/menu_component_bottom_sheet.xml
131
139
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/nav_button_component.xml
132
140
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/menu/bottom_navigation_menu.xml
133
141
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -173,6 +181,7 @@ files:
173
181
  - lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/BridgeComponent+App.swift
174
182
  - lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/FlashMessageComponent.swift
175
183
  - lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/FormComponent.swift
184
+ - lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/MenuComponent.swift
176
185
  - lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/NavButtonComponent.swift
177
186
  - lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/TurboNativeProject.swift.tt
178
187
  - lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.pbxproj.tt
@@ -200,6 +209,7 @@ files:
200
209
  - lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/BridgeComponent+App.swift
201
210
  - lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/FlashMessageComponent.swift
202
211
  - lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/FormComponent.swift
212
+ - lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/MenuComponent.swift
203
213
  - lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/NavButtonComponent.swift
204
214
  - lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/TurboNativeProject.swift.tt
205
215
  - lib/turbo_native_initializer/version.rb