turbo-native-initializer 0.0.11 → 0.0.13

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