ruby_native 0.6.0 → 0.7.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5456e6607aa5961850b5a774484ca42467c6decfc6d605bef5cb53f302e652f
4
- data.tar.gz: 65cfc4e2c0bce516d97337ce74687591618f4225e05eac63a07c91086ce34c00
3
+ metadata.gz: 8cbf9c1f97b07ee4e4f6bb1f48b529f55928b19ceedc11cd6910ad85da451d36
4
+ data.tar.gz: 6911ed4b628413e7ae1234068bdd6efd3ba682ca1f6e93a15565866c6f19dfea
5
5
  SHA512:
6
- metadata.gz: 4af1c21690a1af9976ba6fe925eaadca6787e75e6b2aab20a05b0f89fa049e854122a854e3b5b937d0f7fb8ef679420a23521c73c66cee9aa8fad06900e1b0ce
7
- data.tar.gz: b2b611b70b24476d9fd933d7631721d1000b94b694a3be31e415181d1e648e54e881662b8056ded977cd87fb5027dcbaa7399dfd1688099257ffe4349522bf56
6
+ metadata.gz: 8936caf8661fc97745fe2282b55650c66a903e2880544247fc16c3668b5ddf0723bc369089cbf89d7ffc88151a7c4464649f33af14d6dca1853833fab1a0803d
7
+ data.tar.gz: a3c94710c642b99f3d5bb48929dad93f5d865dfc815aa4cb2da8337ef22defd140e3367c0159376cb7110f8ffaf1f9b14a0480149e99376f68e972c2933a0d2d
data/README.md CHANGED
@@ -97,71 +97,11 @@ Place helpers in the `<body>`, not the `<head>`.
97
97
  - `native_push_tag` - requests push notification permission.
98
98
  - `native_back_button_tag(text = nil, **options)` - renders a back button for Normal Mode. Hidden by default, shown when the native app sets `body.can-go-back`. Not needed in [Advanced Mode](https://rubynative.com/docs/advanced-mode) where the system provides a native back button.
99
99
 
100
- ### Normal Mode
101
-
102
100
  - `native_form_tag` - marks the page as a form. The app skips form pages when navigating back.
103
-
104
- ### Advanced Mode
105
-
106
- These require the JavaScript setup described in [Advanced Mode setup](#advanced-mode-setup).
107
-
108
- - `native_form_data` - returns the data hash for the native form submit button. Pass to `form_with`'s `data:` option.
109
- - `native_submit_data` - returns the data hash for the native submit target. Pass to `form.submit`'s `data:` option.
110
- - `native_button_tag(title, url, ios_image:, side: :right, **options)` - adds a native navigation bar button.
111
- - `native_menu_tag(title:, side: :right, &block)` - displays a native action sheet menu.
112
- - `native_search_tag` - adds a native search bar.
113
-
114
- ## Advanced Mode setup
115
-
116
- 1. Install the JavaScript dependency:
117
-
118
- ```bash
119
- yarn add @hotwired/hotwire-native-bridge
120
- # or
121
- bin/importmap pin @hotwired/hotwire-native-bridge
122
- ```
123
-
124
- 2. Import the controllers in your JavaScript entrypoint:
125
-
126
- ```js
127
- import "ruby_native/bridge"
128
- ```
129
-
130
- ### `native_form_data` / `native_submit_data`
131
-
132
- ```erb
133
- <%= form_with model: @link, data: native_form_data do |f| %>
134
- <%= f.text_field :url %>
135
- <%= f.submit "Save", data: native_submit_data %>
136
- <% end %>
137
- ```
138
-
139
- ### `native_button_tag`
140
-
141
- ```erb
142
- <%= native_button_tag "Add a link", new_link_path, ios_image: "plus", class: "btn btn-primary" %>
143
- ```
144
-
145
- Options:
146
- - `ios_image:` - SF Symbol icon name (falls back to the title text)
147
- - `side:` - `:left` or `:right` (default). Left supplements the back button.
148
-
149
- ### `native_menu_tag`
150
-
151
- ```erb
152
- <%= native_menu_tag(title: "Actions") do |menu| %>
153
- <%= menu.item "Edit", edit_link_path(@link) %>
154
- <%= menu.item "Delete", link_path(@link), method: :delete, destructive: true %>
155
- <% end %>
156
- ```
157
-
158
- Options on `native_menu_tag`:
159
- - `title:` - action sheet title
160
- - `side:` - `:left` or `:right` (default)
161
-
162
- Options on `menu.item`:
163
- - `method:` - Turbo method (e.g., `:delete`)
164
- - `destructive: true` - red styling
101
+ - `native_navbar_tag(title, &block)` - native navigation bar with title, buttons, menus, and submit actions.
102
+ - `native_badge_tag(count, home:, tab:)` - sets the app icon and tab bar badge counts.
103
+ - `native_haptic_data(feedback = :success)` - returns a data hash that fires a haptic feedback on click.
104
+ - `native_overscroll_tag(top:, bottom:)` - per-page overscroll colors.
165
105
 
166
106
  ## Stylesheet
167
107
 
@@ -23,14 +23,3 @@
23
23
  body.can-go-back .native-back-button {
24
24
  display: inline;
25
25
  }
26
-
27
- [data-bridge-components~="form"]
28
- [data-controller~="bridge--form"]
29
- [type="submit"] {
30
- display: none;
31
- }
32
-
33
- [data-bridge-components~="button"]
34
- [data-controller~="bridge--button"] {
35
- display: none;
36
- }
data/config/importmap.rb CHANGED
@@ -1,3 +1 @@
1
- pin "ruby_native/bridge", to: "ruby_native/bridge/index.js"
2
- pin_all_from RubyNative::Engine.root.join("app/javascript/ruby_native/bridge"), under: "ruby_native/bridge"
3
1
  pin "ruby_native/back", to: "ruby_native/back.js"
@@ -46,11 +46,6 @@ module RubyNative
46
46
  say " 4. Preview on your device:"
47
47
  say " bundle exec ruby_native preview"
48
48
  say ""
49
- say " For Advanced Mode (native buttons, menus, search):"
50
- say " bin/importmap pin @hotwired/hotwire-native-bridge"
51
- say " Then add to your JavaScript entrypoint:"
52
- say " import \"ruby_native/bridge\""
53
- say ""
54
49
  if File.directory?(File.join(destination_root, ".claude"))
55
50
  say " Tip: .claude/ruby_native.md was added with setup instructions."
56
51
  say " Open Claude Code and ask \"what do I need to do next?\" for guided help."
@@ -1,29 +1,16 @@
1
1
  module RubyNative
2
2
  module Helper
3
3
  def native_tabs_tag(enabled: true)
4
- safe_join([
5
- (tag.div(data: { native_tabs: true }, hidden: true) if enabled),
6
- tag.div(data: { controller: "bridge--tabs", bridge__tabs_enabled_value: enabled })
7
- ].compact)
4
+ return "".html_safe unless enabled
5
+ tag.div(data: { native_tabs: true }, hidden: true)
8
6
  end
9
7
 
10
8
  def native_form_tag
11
9
  tag.div(data: { native_form: true }, hidden: true)
12
10
  end
13
11
 
14
- def native_form_data(**data)
15
- merge_controller(data, "bridge--form")
16
- end
17
-
18
- def native_submit_data
19
- { bridge__form_target: "submit" }
20
- end
21
-
22
12
  def native_push_tag
23
- safe_join([
24
- tag.div(data: { native_push: true }, hidden: true),
25
- tag.div(data: { controller: "bridge--push" })
26
- ])
13
+ tag.div(data: { native_push: true }, hidden: true)
27
14
  end
28
15
 
29
16
  def native_back_button_tag(text = nil, **options)
@@ -35,53 +22,22 @@ module RubyNative
35
22
  tag.button(text || default_content, onclick: "RubyNative.postMessage({action: 'back'})", **options)
36
23
  end
37
24
 
38
- def native_search_tag
39
- tag.div(data: { controller: "bridge--search" })
40
- end
41
-
42
- def native_button_tag(title, url, ios_image: nil, side: :right, **options)
43
- data = options.delete(:data) || {}
44
- data[:controller] = "bridge--button"
45
- data[:bridge_side] = side.to_s
46
- data[:bridge_ios_image] = ios_image if ios_image
47
-
48
- link_to title, url, **options, data: data
49
- end
50
-
51
- def native_menu_tag(title:, side: :right, &block)
52
- builder = MenuBuilder.new(self)
53
- capture(builder, &block)
54
-
55
- tag.div(style: "display:none", data: {
56
- controller: "bridge--menu",
57
- bridge__menu_title_value: title,
58
- bridge__menu_side_value: side.to_s
59
- }) { builder.to_html }
60
- end
61
-
62
25
  def native_badge_tag(count = nil, home: nil, tab: nil)
63
26
  home = count if count && home.nil?
64
27
  tab = count if count && tab.nil?
65
28
 
66
- signal_data = { native_badge: "" }
67
- signal_data[:native_badge_home] = home unless home.nil?
68
- signal_data[:native_badge_tab] = tab unless tab.nil?
29
+ data = { native_badge: "" }
30
+ data[:native_badge_home] = home unless home.nil?
31
+ data[:native_badge_tab] = tab unless tab.nil?
69
32
 
70
- bridge_data = { controller: "bridge--badge" }
71
- bridge_data[:bridge__badge_home_value] = home unless home.nil?
72
- bridge_data[:bridge__badge_tab_value] = tab unless tab.nil?
73
-
74
- safe_join([
75
- tag.div(data: signal_data, hidden: true),
76
- tag.div(data: bridge_data)
77
- ])
33
+ tag.div(data: data, hidden: true)
78
34
  end
79
35
 
80
- def native_navbar_tag(title, &block)
36
+ def native_navbar_tag(title = nil, &block)
81
37
  builder = NavbarBuilder.new(self)
82
38
  capture(builder, &block) if block
83
39
 
84
- tag.div(data: { native_navbar: title }, hidden: true) { builder.to_html }
40
+ tag.div(data: { native_navbar: title.to_s }, hidden: true) { builder.to_html }
85
41
  end
86
42
 
87
43
  def native_overscroll_tag(top:, bottom: nil)
@@ -91,49 +47,20 @@ module RubyNative
91
47
  def native_haptic_data(feedback = :success, **data)
92
48
  feedback = feedback.to_s
93
49
  feedback = "success" if feedback.empty?
94
-
95
50
  data[:native_haptic] = feedback
96
- data[:bridge__haptic_feedback_value] = feedback
97
- merge_controller(data, "bridge--haptic")
98
- end
99
-
100
- private
101
-
102
- def merge_controller(data, controller)
103
- data[:controller] = [data[:controller], controller].compact.join(" ")
104
51
  data
105
52
  end
106
53
 
107
- class MenuBuilder
108
- def initialize(context)
109
- @context = context
110
- @items = []
111
- end
112
-
113
- def item(title, url, method: nil, destructive: false, **options)
114
- data = options.delete(:data) || {}
115
- data[:bridge__menu_target] = "item"
116
- data[:turbo_method] = method if method
117
- data[:destructive] = "" if destructive
118
-
119
- @items << @context.link_to(title, url, **options, data: data, hidden: true)
120
- end
121
-
122
- def to_html
123
- @context.safe_join(@items)
124
- end
125
- end
126
-
127
54
  class NavbarBuilder
128
55
  def initialize(context)
129
56
  @context = context
130
57
  @items = []
131
58
  end
132
59
 
133
- def button(icon: nil, title: nil, href: nil, click: nil, position: :trailing, selected: false, &block)
60
+ def button(title = nil, icon: nil, href: nil, click: nil, position: :trailing, selected: false, &block)
134
61
  data = { native_button: "" }
135
- data[:native_icon] = icon if icon
136
62
  data[:native_title] = title if title
63
+ data[:native_icon] = icon if icon
137
64
  data[:native_href] = href if href
138
65
  data[:native_click] = click if click
139
66
  data[:native_position] = position.to_s
@@ -1,3 +1,3 @@
1
1
  module RubyNative
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_native
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Masilotti
@@ -72,15 +72,6 @@ files:
72
72
  - app/controllers/ruby_native/push/devices_controller.rb
73
73
  - app/controllers/ruby_native/webhooks/apple_controller.rb
74
74
  - app/javascript/ruby_native/back.js
75
- - app/javascript/ruby_native/bridge/badge_controller.js
76
- - app/javascript/ruby_native/bridge/button_controller.js
77
- - app/javascript/ruby_native/bridge/form_controller.js
78
- - app/javascript/ruby_native/bridge/haptic_controller.js
79
- - app/javascript/ruby_native/bridge/index.js
80
- - app/javascript/ruby_native/bridge/menu_controller.js
81
- - app/javascript/ruby_native/bridge/push_controller.js
82
- - app/javascript/ruby_native/bridge/search_controller.js
83
- - app/javascript/ruby_native/bridge/tabs_controller.js
84
75
  - app/models/ruby_native/iap/purchase_intent.rb
85
76
  - app/views/ruby_native/auth/start/show.html.erb
86
77
  - config/importmap.rb
@@ -1,21 +0,0 @@
1
- import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
-
3
- export default class extends BridgeComponent {
4
- static component = "badge"
5
- static values = { home: Number, tab: Number }
6
-
7
- connect() {
8
- super.connect()
9
- this.#update()
10
- }
11
-
12
- homeValueChanged() { this.#update() }
13
- tabValueChanged() { this.#update() }
14
-
15
- #update() {
16
- const data = {}
17
- if (this.hasHomeValue) data.home = this.homeValue
18
- if (this.hasTabValue) data.tab = this.tabValue
19
- this.send("connect", data)
20
- }
21
- }
@@ -1,30 +0,0 @@
1
- import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
-
3
- export default class extends BridgeComponent {
4
- static component = "button"
5
-
6
- connect() {
7
- super.connect()
8
- this.#addButton()
9
- }
10
-
11
- disconnect() {
12
- super.disconnect()
13
- this.#removeButton()
14
- }
15
-
16
- #addButton() {
17
- const element = this.bridgeElement
18
- const side = element.bridgeAttribute("side") || "right"
19
- const image = element.bridgeAttribute("ios-image")
20
- const data = { title: element.title, image }
21
-
22
- this.send(side, data, () => {
23
- this.element.click()
24
- })
25
- }
26
-
27
- #removeButton() {
28
- this.send("disconnect")
29
- }
30
- }
@@ -1,27 +0,0 @@
1
- import { BridgeComponent, BridgeElement } from "@hotwired/hotwire-native-bridge"
2
-
3
- export default class extends BridgeComponent {
4
- static component = "form"
5
- static targets = ["submit"]
6
-
7
- connect() {
8
- super.connect()
9
-
10
- const title = new BridgeElement(this.submitTarget).title
11
- this.send("connect", { submitTitle: title.trim() }, () => {
12
- this.submitTarget.click()
13
- })
14
-
15
- this.element.addEventListener("turbo:submit-start", this.submitStarted)
16
- this.element.addEventListener("turbo:submit-end", this.submitEnded)
17
- }
18
-
19
- disconnect() {
20
- super.disconnect()
21
- this.element.removeEventListener("turbo:submit-start", this.submitStarted)
22
- this.element.removeEventListener("turbo:submit-end", this.submitEnded)
23
- }
24
-
25
- submitStarted = () => this.send("submitDisabled")
26
- submitEnded = () => this.send("submitEnabled")
27
- }
@@ -1,11 +0,0 @@
1
- import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
-
3
- export default class extends BridgeComponent {
4
- static component = "haptic"
5
- static values = { feedback: { type: String, default: "success" } }
6
-
7
- vibrate() {
8
- const feedback = this.feedbackValue || "success"
9
- this.send("vibrate", { feedback })
10
- }
11
- }
@@ -1,18 +0,0 @@
1
- import { application } from "controllers/application"
2
- import TabsController from "ruby_native/bridge/tabs_controller"
3
- import FormController from "ruby_native/bridge/form_controller"
4
- import PushController from "ruby_native/bridge/push_controller"
5
- import MenuController from "ruby_native/bridge/menu_controller"
6
- import SearchController from "ruby_native/bridge/search_controller"
7
- import ButtonController from "ruby_native/bridge/button_controller"
8
- import HapticController from "ruby_native/bridge/haptic_controller"
9
- import BadgeController from "ruby_native/bridge/badge_controller"
10
-
11
- application.register("bridge--tabs", TabsController)
12
- application.register("bridge--form", FormController)
13
- application.register("bridge--push", PushController)
14
- application.register("bridge--menu", MenuController)
15
- application.register("bridge--search", SearchController)
16
- application.register("bridge--button", ButtonController)
17
- application.register("bridge--haptic", HapticController)
18
- application.register("bridge--badge", BadgeController)
@@ -1,22 +0,0 @@
1
- import { BridgeComponent, BridgeElement } from "@hotwired/hotwire-native-bridge"
2
-
3
- export default class extends BridgeComponent {
4
- static component = "menu"
5
- static targets = ["item"]
6
- static values = { title: String, side: { type: String, default: "right" } }
7
-
8
- connect() {
9
- super.connect()
10
-
11
- const items = this.itemTargets.map((el, index) => ({
12
- title: new BridgeElement(el).title,
13
- index,
14
- destructive: el.hasAttribute("data-destructive")
15
- }))
16
-
17
- this.send("connect", { title: this.titleValue, items, side: this.sideValue }, (message) => {
18
- const { selectedIndex } = message.data
19
- this.itemTargets[selectedIndex]?.click()
20
- })
21
- }
22
- }
@@ -1,10 +0,0 @@
1
- import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
-
3
- export default class extends BridgeComponent {
4
- static component = "push"
5
-
6
- connect() {
7
- super.connect()
8
- this.send("connect")
9
- }
10
- }
@@ -1,16 +0,0 @@
1
- import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
-
3
- export default class extends BridgeComponent {
4
- static component = "search"
5
-
6
- connect() {
7
- super.connect()
8
-
9
- this.send("connect", {}, (message) => {
10
- const query = message.data.query
11
- const detail = {query}
12
-
13
- this.dispatch("queried", {detail})
14
- })
15
- }
16
- }
@@ -1,11 +0,0 @@
1
- import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
-
3
- export default class extends BridgeComponent {
4
- static component = "tabs"
5
- static values = { enabled: Boolean }
6
-
7
- connect() {
8
- super.connect()
9
- this.send("connect", { enabled: this.enabledValue })
10
- }
11
- }