hotwire_native_rails 0.1.0 → 0.2.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: 5cd95ffaf194bb91e5f601aa7dde53a93d3e47bc7e859b69348abc6d86aba97a
4
- data.tar.gz: 7575cd5a82c3499610e41db9ea37d1a58aa8850e5fda8d3f516e31752e6d7357
3
+ metadata.gz: 4a4224784bdab848b3b193b9902b70940618a3d37aa48b9502f5e98756fe4d31
4
+ data.tar.gz: cf49ce2b8982ae85ac45032bf39f64b20971f6521e83e9080a417fb0269fd9da
5
5
  SHA512:
6
- metadata.gz: 3755dcc7413bf37957901a0a877168bee454b65f29e6942d81d18b0492b6f4ea5fabdd8167591811d0ac2766e0377d6ff249c04d5fb3e867623e205b9eb1b1e6
7
- data.tar.gz: aa9862acb940543a56d8cec09f0461af28bc21038cc15b77569f54bd91350613fe06b7f2a86fe6acc5b5304b4ab1698728e85190da89e420197cea492f17c60e
6
+ metadata.gz: 92d384f1c486c93494a14e2635739a335e68a599201fcf3642ce1f19986d1d2b951fd7fbfb3a0e85bec69d0bddc45f6b34e0df5c23a7e6aae5c9f5f635c43755
7
+ data.tar.gz: 2bf89e359cd0e1518367f32ae7edd349e6aee123ccdd09b4cbb73335d8c113d6b230838f56bee15aee092b4de791615a113eaf90622214807b68a01c83138863
data/README.md CHANGED
@@ -1,43 +1,39 @@
1
- # HotwireNativeRails
1
+ # Hotwire Native Rails generator
2
2
 
3
- TODO: Delete this and the text below, and describe your gem
4
-
5
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/hotwire_native_rails`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Power pack to make your Rails app [Hotwire Native](https://native.hotwired.dev)
6
4
 
7
5
  ## Installation
8
6
 
9
- TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
10
-
11
- Install the gem and add to the application's Gemfile by executing:
7
+ Install the gem:
12
8
 
13
- ```bash
14
- bundle add UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
9
+ ```sh
10
+ bundle add hotwire_native_rails
15
11
  ```
16
12
 
17
- If bundler is not being used to manage dependencies, install the gem by executing:
13
+ Run the generator:
18
14
 
19
- ```bash
20
- gem install UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG
15
+ ```sh
16
+ rails g hotwire_native_rails
21
17
  ```
22
18
 
23
19
  ## Usage
24
20
 
25
- TODO: Write usage instructions here
26
-
27
- ## Development
28
-
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
-
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
-
33
- ## Contributing
34
-
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/hotwire_native_rails. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/hotwire_native_rails/blob/master/CODE_OF_CONDUCT.md).
21
+ #### Helpers
22
+ - `viewport_meta_tag` - forbid zooming on mobile/native
23
+ - use `data: { turbo_action: replace_if_native }` to submit authentication forms & forms in modals
24
+ - `:mobile` request variant. `index.html+mobile.erb`
25
+ - override link_to to not open internal links in in-app browser on native app
36
26
 
37
- ## License
27
+ #### CSS
28
+ - `turbo-native:` css variant (works with CSS and Tailwind)
38
29
 
39
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
30
+ #### Bridge Components
31
+ - install Hotwire Native Bridge (works with Importmaps and Node)
32
+ - add default bridge components (`Form`, `Menu`, `Button`)
33
+ - `bridge_form_with` - easily apply Bridge Form component
40
34
 
41
- ## Code of Conduct
35
+ #### Path Configuration
36
+ - `path_configuration_controller` for `ios` and `android`
42
37
 
43
- Everyone interacting in the HotwireNativeRails project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/hotwire_native_rails/blob/master/CODE_OF_CONDUCT.md).
38
+ gem build hotwire_native_rails.gemspec
39
+ gem push hotwire_native_rails-0.1.0.gem
@@ -1,17 +1,98 @@
1
1
  class HotwireNativeGenerator < Rails::Generators::Base
2
2
  source_root File.expand_path("templates", __dir__)
3
3
 
4
+ def add_gems
5
+ gem "browser"
6
+ end
7
+
4
8
  def copy_files
9
+ # helpers
5
10
  copy_file "helpers/hotwire_native_helper.rb", "app/helpers/hotwire_native_helper.rb"
6
11
  copy_file "test_unit/hotwire_native_helper_test.rb", "test/helpers/hotwire_native_helper_test.rb"
7
12
 
8
- # copy conent of https://raw.githubusercontent.com/hotwired/hotwire-native-demo/refs/heads/main/public/javascript/controllers/bridge/form_controller.js to app/javascript/controllers/bridge/form_controller.js
9
- # copy_file "bridge/form_controller.js", "app/javascript/controllers/bridge/form_controller.js"
13
+ # routes
14
+ copy_file "routes/hotwire_native.rb", "config/routes/hotwire_native.rb"
15
+ copy_file "controllers/hotwire_native/v1/android/path_configuration_controller.rb", "app/controllers/hotwire_native/v1/android/path_configuration_controller.rb"
16
+ copy_file "controllers/hotwire_native/v1/ios/path_configuration_controller.rb", "app/controllers/hotwire_native/v1/ios/path_configuration_controller.rb"
17
+
18
+ # :native request variant
19
+ copy_file "controllers/concerns/device_format.rb", "app/controllers/concerns/device_format.rb"
20
+ end
21
+
22
+ def add_detect_device_to_application_controller
23
+ inject_into_class "app/controllers/application_controller.rb", ApplicationController, " include DetectDevice\n"
24
+ end
25
+
26
+ def add_routes
27
+ route "draw(:hotwire_native)"
28
+ end
29
+
30
+ # https://native.hotwired.dev/reference/bridge-installation
31
+ def install_javascript
32
+ copy_file "javascript/controllers/bridge/button_controller.js", "app/javascript/controllers/bridge/button_controller.js"
33
+ copy_file "javascript/controllers/bridge/menu_controller.js", "app/javascript/controllers/bridge/menu_controller.js"
34
+ copy_file "javascript/controllers/bridge/form_controller.js", "app/javascript/controllers/bridge/form_controller.js"
35
+ copy_file "javascript/controllers/bridge/overflow_menu_controller.js", "app/javascript/controllers/bridge/overflow_menu_controller.js"
36
+
37
+ run "bin/importmap pin @hotwired/stimulus @hotwired/hotwire-native-bridge" if importmaps?
38
+ run "yarn add @hotwired/stimulus @hotwired/hotwire-native-bridge" if node?
39
+ end
40
+
41
+ def add_viewport_meta_tag
42
+ gsub_file "app/views/layouts/application.html.erb", "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">", "<%= viewport_meta_tag %>"
43
+ end
44
+
45
+ def add_css_variants
46
+ return add_tailwind_css_variants if tailwind?
47
+
48
+ add_turbo_native_css
49
+ end
50
+
51
+ def add_platform_identifier
52
+ gsub_file "app/views/layouts/application.html.erb", "<html>", "<html <%= platform_identifier %>>"
53
+ end
54
+
55
+ private
56
+
57
+ def importmaps?
58
+ Rails.root.join("config/importmap.rb").exist?
59
+ end
60
+
61
+ def node?
62
+ Rails.root.join("package.json").exist?
63
+ end
64
+
65
+ def tailwind?
66
+ Rails.root.join("config/tailwind.config.js").exist?
67
+ end
68
+
69
+ # class="turbo-native:hidden"
70
+ # class="non-turbo-native:hidden"
71
+ def add_tailwind_css_variants
72
+ prepend_to_file "config/tailwind.config.js", "const plugin = require('tailwindcss/plugin')\n"
73
+
74
+ inject_into_file "config/tailwind.config.js", after: "plugins: [" do
75
+ <<-JS
76
+
77
+ plugin(function({ addVariant }) {
78
+ addVariant("turbo-native", "html[data-turbo-native] &"),
79
+ addVariant("non-turbo-native", "html:not([data-turbo-native]) &")
80
+ }),
81
+ JS
82
+ end
83
+ end
10
84
 
11
- # path_configuration file
85
+ # class="turbo-native:hidden"
86
+ def add_turbo_native_css
87
+ gsub_file "app/views/layouts/application.html.erb", "<body>", "<body class=\"<%= \"turbo-native\" if turbo_native_app? %>\">"
12
88
 
13
- # tailwind
89
+ append_to_file "app/assets/stylesheets/application.css" do
90
+ <<-CSS
14
91
 
15
- # devise variant
92
+ body.turbo-native .turbo-native:hidden {
93
+ display: none;
94
+ }
95
+ CSS
96
+ end
16
97
  end
17
98
  end
@@ -0,0 +1,13 @@
1
+ module DeviceFormat
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_action :set_variant
6
+ end
7
+
8
+ private
9
+
10
+ def set_variant
11
+ return request.variant = :mobile if turbo_native_app? || browser.device.mobile?
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class HotwireNative::V1::Android::PathConfigurationsController < ActionController::Base
2
+ def show
3
+ render json: {}
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class HotwireNative::V1::Ios::PathConfigurationsController < ActionController::Base
2
+ def show
3
+ render json: {}
4
+ end
5
+ end
@@ -1,27 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HotwireNativeHelper
4
+ # forbid zooming on mobile devices
4
5
  def viewport_meta_tag
5
6
  content = ['width=device-width,initial-scale=1']
6
7
  content << 'maximum-scale=1, user-scalable=0' if turbo_native_app? || browser.device.mobile?
7
8
  tag.meta(name: 'viewport', content: content.join(','))
8
9
  end
9
10
 
11
+ # set on <html> tag
10
12
  def platform_identifier
11
13
  'data-turbo-native' if turbo_native_app?
12
14
  end
13
15
 
16
+ # link_to 'Next', next_path, data: { turbo_action: replace_if_native }
17
+ # https://turbo.hotwired.dev/handbook/drive#application-visits
14
18
  def replace_if_native
15
19
  return 'replace' if turbo_native_app?
16
20
 
17
21
  'advance'
18
22
  end
19
23
 
24
+ # override link_to to not open internal links in in-app browser on native app
20
25
  def link_to(name = nil, options = nil, html_options = {}, &block)
21
26
  html_options[:target] = '' if turbo_native_app? && internal_url?(url_for(options))
22
27
  super(name, options, html_options, &block)
23
28
  end
24
29
 
30
+ # https://github.com/joemasilotti/daily-log/blob/main/rails/app/helpers/form_helper.rb
25
31
  class BridgeFormBuilder < ActionView::Helpers::FormBuilder
26
32
  def submit(value = nil, options = {})
27
33
  options[:data] ||= {}
@@ -0,0 +1,20 @@
1
+ import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
+ // Source:
3
+ // https://native.hotwired.dev/ios/bridge-components
4
+ // Docs:
5
+ // https://blog.corsego.com/hotwire-native-bridge-button
6
+ export default class extends BridgeComponent {
7
+ static component = "button"
8
+
9
+ connect() {
10
+ super.connect()
11
+
12
+ const element = this.bridgeElement
13
+ const title = element.bridgeAttribute("title")
14
+ const image = element.bridgeAttribute("ios-image")
15
+ const side = element.bridgeAttribute("side") || "right"
16
+ this.send("connect", {title, image, side}, () => {
17
+ this.element.click()
18
+ })
19
+ }
20
+ }
@@ -0,0 +1,34 @@
1
+ import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
+ import { BridgeElement } from "@hotwired/hotwire-native-bridge"
3
+ // Source:
4
+ // https://github.com/hotwired/hotwire-native-demo/blob/main/public/javascript/controllers/bridge/form_controller.js
5
+ // Docs:
6
+ // https://blog.corsego.com/hotwire-native-form-component
7
+ export default class extends BridgeComponent {
8
+ static component = "form"
9
+ static targets = [ "submit" ]
10
+
11
+ connect() {
12
+ super.connect()
13
+ this.notifyBridgeOfConnect()
14
+ }
15
+
16
+ notifyBridgeOfConnect() {
17
+ const submitButton = new BridgeElement(this.submitTarget)
18
+ const submitTitle = submitButton.title
19
+
20
+ this.send("connect", { submitTitle }, () => {
21
+ this.submitTarget.click()
22
+ })
23
+ }
24
+
25
+ submitStart(event) {
26
+ this.submitTarget.disabled = true
27
+ this.send("submitDisabled")
28
+ }
29
+
30
+ submitEnd(event) {
31
+ this.submitTarget.disabled = false
32
+ this.send("submitEnabled")
33
+ }
34
+ }
@@ -0,0 +1,47 @@
1
+ import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
+ import { BridgeElement } from "@hotwired/hotwire-native-bridge"
3
+ // Source:
4
+ // https://github.com/hotwired/hotwire-native-demo/blob/main/public/javascript/controllers/bridge/menu_controller.js
5
+ // Docs:
6
+ // https://blog.corsego.com/hotwire-native-bridge-menu-component
7
+ export default class extends BridgeComponent {
8
+ static component = "menu"
9
+ static targets = [ "title", "item" ]
10
+
11
+ show(event) {
12
+ if (this.enabled) {
13
+ event.stopImmediatePropagation()
14
+ this.notifyBridgeToDisplayMenu(event)
15
+ }
16
+ }
17
+
18
+ notifyBridgeToDisplayMenu(event) {
19
+ const title = new BridgeElement(this.titleTarget).title
20
+ const items = this.makeMenuItems(this.itemTargets)
21
+
22
+ this.send("display", { title, items }, message => {
23
+ const selectedIndex = message.data.selectedIndex
24
+ const selectedItem = new BridgeElement(this.itemTargets[selectedIndex])
25
+
26
+ selectedItem.click()
27
+ })
28
+ }
29
+
30
+ makeMenuItems(elements) {
31
+ const items = elements.map((element, index) => this.menuItem(element, index))
32
+ const enabledItems = items.filter(item => item)
33
+
34
+ return enabledItems
35
+ }
36
+
37
+ menuItem(element, index) {
38
+ const bridgeElement = new BridgeElement(element)
39
+
40
+ if (bridgeElement.disabled) return null
41
+
42
+ return {
43
+ title: bridgeElement.title,
44
+ index: index
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,22 @@
1
+ import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
+ // Source:
3
+ // https://github.com/hotwired/hotwire-native-demo/blob/main/public/javascript/controllers/bridge/overflow_menu_controller.js
4
+ // Docs:
5
+ // https://blog.corsego.com/hotwire-native-bridge-menu-component
6
+
7
+ export default class extends BridgeComponent {
8
+ static component = "overflow-menu"
9
+
10
+ connect() {
11
+ super.connect()
12
+ this.notifyBridgeOfConnect()
13
+ }
14
+
15
+ notifyBridgeOfConnect() {
16
+ const label = this.bridgeElement.title
17
+
18
+ this.send("connect", { label }, () => {
19
+ this.bridgeElement.click()
20
+ })
21
+ }
22
+ }
@@ -0,0 +1,11 @@
1
+ import { BridgeComponent } from "@hotwired/hotwire-native-bridge"
2
+ // Docs:
3
+ // https://blog.corsego.com/hotwire-native-leave-a-review-bridge-component
4
+ export default class extends BridgeComponent {
5
+ static component = "review-prompt"
6
+
7
+ connect() {
8
+ super.connect()
9
+ this.send("connect")
10
+ }
11
+ }
@@ -0,0 +1,10 @@
1
+ namespace :hotwire_native do
2
+ namespace :v1 do
3
+ namespace :android do
4
+ resource :path_configuration, only: :show
5
+ end
6
+ namespace :ios do
7
+ resource :path_configuration, only: :show
8
+ end
9
+ end
10
+ end
@@ -1,6 +1,7 @@
1
1
  require 'test_helper'
2
2
 
3
3
  class HotwireNativeHelperTest < ActionView::TestCase
4
+ # add more tests
4
5
  setup do
5
6
  def root_url
6
7
  "http://test.host/"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HotwireNativeRails
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotwire_native_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yaro Shm
@@ -28,7 +28,16 @@ files:
28
28
  - Rakefile
29
29
  - hotwire_native_rails.gemspec
30
30
  - lib/generators/hotwire_native/hotwire_native_generator.rb
31
+ - lib/generators/hotwire_native/templates/controllers/concerns/device_format.rb
32
+ - lib/generators/hotwire_native/templates/controllers/hotwire_native/v1/android/path_configuration_controller.rb
33
+ - lib/generators/hotwire_native/templates/controllers/hotwire_native/v1/ios/path_configuration_controller.rb
31
34
  - lib/generators/hotwire_native/templates/helpers/hotwire_native_helper.rb
35
+ - lib/generators/hotwire_native/templates/javascript/controllers/bridge/button_controller.js
36
+ - lib/generators/hotwire_native/templates/javascript/controllers/bridge/form_controller.js
37
+ - lib/generators/hotwire_native/templates/javascript/controllers/bridge/menu_controller.js
38
+ - lib/generators/hotwire_native/templates/javascript/controllers/bridge/overflow_menu_controller.js
39
+ - lib/generators/hotwire_native/templates/javascript/controllers/bridge/review_prompt_controller.js
40
+ - lib/generators/hotwire_native/templates/routes/hotwire_native.rb
32
41
  - lib/generators/hotwire_native/templates/test_unit/hotwire_native_helper_test.rb
33
42
  - lib/hotwire_native_rails.rb
34
43
  - lib/hotwire_native_rails/version.rb