turbo-native-initializer 0.0.11 → 0.0.13

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 (30) 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/features/web/WebFragment.kt.tt +5 -0
  4. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt +1 -0
  5. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponent.kt.tt +78 -0
  6. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponentAdapter.kt.tt +57 -0
  7. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt +14 -21
  8. data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/menu_component_adapter_row.xml +22 -0
  9. data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/menu_component_bottom_sheet.xml +33 -0
  10. data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/menu/web.xml +9 -0
  11. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebFragment.kt.tt +5 -0
  12. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt +1 -0
  13. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponent.kt.tt +78 -0
  14. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/MenuComponentAdapter.kt.tt +57 -0
  15. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt +14 -21
  16. data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/menu_component_adapter_row.xml +22 -0
  17. data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/menu_component_bottom_sheet.xml +33 -0
  18. data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/menu/web.xml +9 -0
  19. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/UIViewController+Toast.swift +28 -8
  20. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/BridgeComponent+App.swift +6 -1
  21. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/MenuComponent.swift +77 -0
  22. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.pbxproj.tt +4 -0
  23. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/UIViewController+Toast.swift +28 -8
  24. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/BridgeComponent+App.swift +6 -1
  25. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/MenuComponent.swift +77 -0
  26. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.pbxproj.tt +4 -0
  27. data/lib/turbo_native_initializer/version.rb +1 -1
  28. metadata +14 -4
  29. data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/nav_button_component.xml +0 -14
  30. data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/nav_button_component.xml +0 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eb141a7b5a2949e9b65bdf004831663bf43377c858891129cd3a6959d73292f2
4
- data.tar.gz: d8636b8bbf702f0483dc737fd49182a887d610d614ae44779fe7191d9391f1c0
3
+ metadata.gz: f6134975dfb64645d0fe32a2dfcaa61efe0719458540d0e294cffc5547646397
4
+ data.tar.gz: df7dada5dc6d57ff85e58e2439c86a9e06cc0e4e89fe8a5547509da483493906
5
5
  SHA512:
6
- metadata.gz: 912afab330194fcaa45897f1f69e92aff243287ada0032322e704163bd6f91be9ab2055df64e635c1ea998de937af603b5b98e8f0add070f7a4507be4d4906f6
7
- data.tar.gz: bf237f3895adbc21f9095ea00ca016ac205ecad78d17da223a5227fa31afe2d207fd69c6c358c278654f897be06a6f50b2909a88618d3b8e342be9e25c330f47
6
+ metadata.gz: 2135c55c9a059bba1907bd90f776e352968d74af49cdc8173d8b6f1d92ecbf5b09e566d3626e02d5842a509fb1dea7b3d7dde0d847046da4cb9740186f5e04b4
7
+ data.tar.gz: 4c648e53161c6ab00f22787b3dc059cf06d8f259a9eabdd8aad0617e33d56ccac29ca6556f4eaa31fd8eb93fbbf50ad15d2d18e5b9f5fe3d1875369679bae5bc
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
 
@@ -25,6 +25,7 @@ open class WebFragment : TurboWebFragment(), NavDestination {
25
25
 
26
26
  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
27
27
  super.onViewCreated(view, savedInstanceState)
28
+ setupMenu()
28
29
  viewLifecycleOwner.lifecycle.addObserver(bridgeDelegate)
29
30
  }
30
31
 
@@ -59,4 +60,8 @@ open class WebFragment : TurboWebFragment(), NavDestination {
59
60
  override fun createErrorView(statusCode: Int): View {
60
61
  return layoutInflater.inflate(R.layout.error_web, null)
61
62
  }
63
+
64
+ private fun setupMenu() {
65
+ toolbarForNavigation()?.inflateMenu(R.menu.web)
66
+ }
62
67
  }
@@ -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
+ }
@@ -1,9 +1,6 @@
1
1
  package <%= package_name %>.strada
2
2
 
3
3
  import android.util.Log
4
- import android.view.LayoutInflater
5
- import android.view.Menu
6
- import android.view.MenuItem
7
4
  import androidx.appcompat.widget.Toolbar
8
5
  import androidx.fragment.app.Fragment
9
6
  import dev.hotwire.strada.BridgeComponent
@@ -11,7 +8,6 @@ import dev.hotwire.strada.BridgeDelegate
11
8
  import dev.hotwire.strada.Message
12
9
  import <%= package_name %>.R
13
10
  import <%= package_name %>.base.NavDestination
14
- import <%= package_name %>.databinding.NavButtonComponentBinding
15
11
  import kotlinx.serialization.Serializable
16
12
 
17
13
  class NavButtonComponent(
@@ -19,8 +15,6 @@ class NavButtonComponent(
19
15
  private val delegate: BridgeDelegate<NavDestination>
20
16
  ) : BridgeComponent<NavDestination>(name, delegate) {
21
17
 
22
- private val navButtonItemId = 20
23
- private var navButtonMenuItem: MenuItem? = null
24
18
  private val fragment: Fragment
25
19
  get() = delegate.destination.fragment
26
20
  private val toolbar: Toolbar?
@@ -40,27 +34,26 @@ class NavButtonComponent(
40
34
  }
41
35
 
42
36
  private fun showToolbarButton(data: MessageData) {
43
- val menu = toolbar?.menu ?: return
44
- val inflater = LayoutInflater.from(fragment.requireContext())
45
- val binding = NavButtonComponentBinding.inflate(inflater)
46
- val order = 999 // Show as the right-most button
37
+ val toolbar = toolbar ?: return
47
38
 
48
- binding.navButton.apply {
49
- text = data.title
50
- setOnClickListener {
51
- performAction()
52
- }
39
+ toolbar.menu.findItem(R.id.nav_button)?.apply {
40
+ isVisible = true
41
+ title = data.title
53
42
  }
54
43
 
55
- menu.removeItem(navButtonItemId)
56
- navButtonMenuItem = menu.add(Menu.NONE, navButtonItemId, order, data.title).apply {
57
- actionView = binding.root
58
- setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
44
+ toolbar.setOnMenuItemClickListener {
45
+ when (it.itemId) {
46
+ R.id.nav_button -> {
47
+ performClick()
48
+ true
49
+ }
50
+ else -> false
51
+ }
59
52
  }
60
53
  }
61
54
 
62
- private fun performAction(): Boolean {
63
- return replyTo("connect")
55
+ private fun performClick() {
56
+ replyTo("connect")
64
57
  }
65
58
 
66
59
  @Serializable
@@ -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>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <menu xmlns:android="http://schemas.android.com/apk/res/android"
3
+ xmlns:app="http://schemas.android.com/apk/res-auto">
4
+ <item
5
+ android:id="@+id/nav_button"
6
+ android:orderInCategory="999"
7
+ android:visible="false"
8
+ app:showAsAction="always" />
9
+ </menu>
@@ -22,6 +22,7 @@ open class WebFragment : TurboWebFragment(), NavDestination {
22
22
 
23
23
  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
24
24
  super.onViewCreated(view, savedInstanceState)
25
+ setupMenu()
25
26
  viewLifecycleOwner.lifecycle.addObserver(bridgeDelegate)
26
27
  }
27
28
 
@@ -49,4 +50,8 @@ open class WebFragment : TurboWebFragment(), NavDestination {
49
50
  override fun createErrorView(statusCode: Int): View {
50
51
  return layoutInflater.inflate(R.layout.error_web, null)
51
52
  }
53
+
54
+ private fun setupMenu() {
55
+ toolbarForNavigation()?.inflateMenu(R.menu.web)
56
+ }
52
57
  }
@@ -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
+ }
@@ -1,9 +1,6 @@
1
1
  package <%= package_name %>.strada
2
2
 
3
3
  import android.util.Log
4
- import android.view.LayoutInflater
5
- import android.view.Menu
6
- import android.view.MenuItem
7
4
  import androidx.appcompat.widget.Toolbar
8
5
  import androidx.fragment.app.Fragment
9
6
  import dev.hotwire.strada.BridgeComponent
@@ -11,7 +8,6 @@ import dev.hotwire.strada.BridgeDelegate
11
8
  import dev.hotwire.strada.Message
12
9
  import <%= package_name %>.R
13
10
  import <%= package_name %>.base.NavDestination
14
- import <%= package_name %>.databinding.NavButtonComponentBinding
15
11
  import kotlinx.serialization.Serializable
16
12
 
17
13
  class NavButtonComponent(
@@ -19,8 +15,6 @@ class NavButtonComponent(
19
15
  private val delegate: BridgeDelegate<NavDestination>
20
16
  ) : BridgeComponent<NavDestination>(name, delegate) {
21
17
 
22
- private val navButtonItemId = 20
23
- private var navButtonMenuItem: MenuItem? = null
24
18
  private val fragment: Fragment
25
19
  get() = delegate.destination.fragment
26
20
  private val toolbar: Toolbar?
@@ -40,27 +34,26 @@ class NavButtonComponent(
40
34
  }
41
35
 
42
36
  private fun showToolbarButton(data: MessageData) {
43
- val menu = toolbar?.menu ?: return
44
- val inflater = LayoutInflater.from(fragment.requireContext())
45
- val binding = NavButtonComponentBinding.inflate(inflater)
46
- val order = 999 // Show as the right-most button
37
+ val toolbar = toolbar ?: return
47
38
 
48
- binding.navButton.apply {
49
- text = data.title
50
- setOnClickListener {
51
- performAction()
52
- }
39
+ toolbar.menu.findItem(R.id.nav_button)?.apply {
40
+ isVisible = true
41
+ title = data.title
53
42
  }
54
43
 
55
- menu.removeItem(navButtonItemId)
56
- navButtonMenuItem = menu.add(Menu.NONE, navButtonItemId, order, data.title).apply {
57
- actionView = binding.root
58
- setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
44
+ toolbar.setOnMenuItemClickListener {
45
+ when (it.itemId) {
46
+ R.id.nav_button -> {
47
+ performClick()
48
+ true
49
+ }
50
+ else -> false
51
+ }
59
52
  }
60
53
  }
61
54
 
62
- private fun performAction(): Boolean {
63
- return replyTo("connect")
55
+ private fun performClick() {
56
+ replyTo("connect")
64
57
  }
65
58
 
66
59
  @Serializable
@@ -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>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <menu xmlns:android="http://schemas.android.com/apk/res/android"
3
+ xmlns:app="http://schemas.android.com/apk/res-auto">
4
+ <item
5
+ android:id="@+id/nav_button"
6
+ android:orderInCategory="999"
7
+ android:visible="false"
8
+ app:showAsAction="always" />
9
+ </menu>
@@ -4,22 +4,44 @@ public extension UIViewController {
4
4
  func presentToast(_ message: String) {
5
5
  guard let root = view.window?.rootViewController else { return }
6
6
 
7
+ removeToastViews(from: root)
8
+
7
9
  let toastView = ToastView(message: message)
8
10
  toastView.translatesAutoresizingMaskIntoConstraints = false
9
11
 
10
12
  root.view.addSubview(toastView)
11
13
 
12
14
  NSLayoutConstraint.activate([
13
- toastView.centerXAnchor.constraint(equalTo: root.view.centerXAnchor),
14
15
  toastView.topAnchor.constraint(equalTo: root.view.safeAreaLayoutGuide.topAnchor),
15
- toastView.widthAnchor.constraint(equalTo: root.view.safeAreaLayoutGuide.widthAnchor, constant: -10)
16
+ toastView.centerXAnchor.constraint(equalTo: root.view.centerXAnchor)
17
+ ])
18
+
19
+ let widthConstraint = toastView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -10)
20
+ widthConstraint.priority = .defaultHigh
21
+
22
+ let maxWidthConstraint = toastView.widthAnchor.constraint(lessThanOrEqualToConstant: 600)
23
+ maxWidthConstraint.priority = .required
24
+
25
+ NSLayoutConstraint.activate([
26
+ widthConstraint, maxWidthConstraint
16
27
  ])
17
28
  }
29
+
30
+ fileprivate func removeToastViews(from root: UIViewController) {
31
+ root.view.subviews.filter({ $0 is ToastView }).forEach({ toast in
32
+ toast.removeFromSuperview()
33
+ })
34
+ }
18
35
  }
19
36
 
20
37
  public class ToastView: UIView {
38
+
39
+ private var duration = 2.5
40
+
21
41
  convenience init(message: String) {
22
42
  self.init(frame: .zero)
43
+
44
+ self.alpha = .zero
23
45
  self.backgroundColor = .black
24
46
  self.layer.cornerRadius = 10
25
47
 
@@ -40,17 +62,15 @@ public class ToastView: UIView {
40
62
 
41
63
  addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismiss)))
42
64
 
43
- self.alpha = .zero
44
-
45
- UIView.animate(withDuration: 0.5, delay: .zero, animations: {
65
+ UIView.animate(withDuration: 0.5, delay: .zero, options: .curveEaseIn, animations: {
46
66
  self.alpha = 0.9
47
67
  }, completion: { _ in
48
- DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { self.dismiss() }
68
+ DispatchQueue.main.asyncAfter(deadline: .now() + self.duration) { self.dismiss() }
49
69
  })
50
70
  }
51
71
 
52
- @objc func dismiss() {
53
- UIView.animate(withDuration: 0.5, delay: .zero, animations: {
72
+ @objc private func dismiss() {
73
+ UIView.animate(withDuration: 0.5, delay: .zero, options: .curveEaseOut, animations: {
54
74
  self.alpha = .zero
55
75
  }, completion: { _ in
56
76
  self.removeFromSuperview()
@@ -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 */,
@@ -4,22 +4,44 @@ public extension UIViewController {
4
4
  func presentToast(_ message: String) {
5
5
  guard let root = view.window?.rootViewController else { return }
6
6
 
7
+ removeToastViews(from: root)
8
+
7
9
  let toastView = ToastView(message: message)
8
10
  toastView.translatesAutoresizingMaskIntoConstraints = false
9
11
 
10
12
  root.view.addSubview(toastView)
11
13
 
12
14
  NSLayoutConstraint.activate([
13
- toastView.centerXAnchor.constraint(equalTo: root.view.centerXAnchor),
14
15
  toastView.topAnchor.constraint(equalTo: root.view.safeAreaLayoutGuide.topAnchor),
15
- toastView.widthAnchor.constraint(equalTo: root.view.safeAreaLayoutGuide.widthAnchor, constant: -10)
16
+ toastView.centerXAnchor.constraint(equalTo: root.view.centerXAnchor)
17
+ ])
18
+
19
+ let widthConstraint = toastView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -10)
20
+ widthConstraint.priority = .defaultHigh
21
+
22
+ let maxWidthConstraint = toastView.widthAnchor.constraint(lessThanOrEqualToConstant: 600)
23
+ maxWidthConstraint.priority = .required
24
+
25
+ NSLayoutConstraint.activate([
26
+ widthConstraint, maxWidthConstraint
16
27
  ])
17
28
  }
29
+
30
+ fileprivate func removeToastViews(from root: UIViewController) {
31
+ root.view.subviews.filter({ $0 is ToastView }).forEach({ toast in
32
+ toast.removeFromSuperview()
33
+ })
34
+ }
18
35
  }
19
36
 
20
37
  public class ToastView: UIView {
38
+
39
+ private var duration = 2.5
40
+
21
41
  convenience init(message: String) {
22
42
  self.init(frame: .zero)
43
+
44
+ self.alpha = .zero
23
45
  self.backgroundColor = .black
24
46
  self.layer.cornerRadius = 10
25
47
 
@@ -40,17 +62,15 @@ public class ToastView: UIView {
40
62
 
41
63
  addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismiss)))
42
64
 
43
- self.alpha = .zero
44
-
45
- UIView.animate(withDuration: 0.5, delay: .zero, animations: {
65
+ UIView.animate(withDuration: 0.5, delay: .zero, options: .curveEaseIn, animations: {
46
66
  self.alpha = 0.9
47
67
  }, completion: { _ in
48
- DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { self.dismiss() }
68
+ DispatchQueue.main.asyncAfter(deadline: .now() + self.duration) { self.dismiss() }
49
69
  })
50
70
  }
51
71
 
52
- @objc func dismiss() {
53
- UIView.animate(withDuration: 0.5, delay: .zero, animations: {
72
+ @objc private func dismiss() {
73
+ UIView.animate(withDuration: 0.5, delay: .zero, options: .curveEaseOut, animations: {
54
74
  self.alpha = .zero
55
75
  }, completion: { _ in
56
76
  self.removeFromSuperview()
@@ -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.11"
2
+ VERSION = "0.0.13"
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.11
4
+ version: 0.0.13
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-28 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,7 +75,9 @@ 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
76
- - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/nav_button_component.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
80
+ - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/menu/web.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
79
83
  - lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/mipmap-xhdpi/ic_launcher.png
@@ -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,8 +134,10 @@ 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
131
- - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/nav_button_component.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
132
139
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/menu/bottom_navigation_menu.xml
140
+ - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/menu/web.xml
133
141
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
134
142
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
135
143
  - lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/mipmap-xhdpi/ic_launcher.png
@@ -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
@@ -1,14 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:layout_width="wrap_content"
4
- android:layout_height="match_parent"
5
- android:layout_gravity="end|center_vertical"
6
- android:paddingEnd="16dp">
7
-
8
- <com.google.android.material.button.MaterialButton
9
- style="@style/Widget.Material3.Button.TextButton"
10
- android:id="@+id/nav_button"
11
- android:layout_width="wrap_content"
12
- android:layout_height="48dp"
13
- android:minWidth="0dip" />
14
- </FrameLayout>
@@ -1,14 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:layout_width="wrap_content"
4
- android:layout_height="match_parent"
5
- android:layout_gravity="end|center_vertical"
6
- android:paddingEnd="16dp">
7
-
8
- <com.google.android.material.button.MaterialButton
9
- style="@style/Widget.Material3.Button.TextButton"
10
- android:id="@+id/nav_button"
11
- android:layout_width="wrap_content"
12
- android:layout_height="48dp"
13
- android:minWidth="0dip" />
14
- </FrameLayout>