turbo-native-initializer 0.0.12 → 0.0.14

Sign up to get free protection for your applications and to get access to all the features.
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