Skip to content

Commit

Permalink
Move hydration mechanics to the lower level commands lib (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
hopsoft authored Jan 3, 2023
1 parent b2df30d commit 624919f
Show file tree
Hide file tree
Showing 14 changed files with 238 additions and 253 deletions.
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
</h1>
<p align="center">
<a href="http://blog.codinghorror.com/the-best-code-is-no-code-at-all/">
<img alt="Lines of Code" src="https://img.shields.io/badge/loc-1131-47d299.svg" />
<img alt="Lines of Code" src="https://img.shields.io/badge/loc-1114-47d299.svg" />
</a>
<a href="https://codeclimate.com/github/hopsoft/turbo_boost-elements/maintainability">
<img src="https://api.codeclimate.com/v1/badges/7aac6daed3e4032e292e/maintainability" />
Expand Down Expand Up @@ -181,21 +181,18 @@ This example will re-render the `post` partial and toggle the `form` section.
If a named keyword argument is shared by both the `trigger` and `target`,
the trigger value will take precendence because multiple triggers might control the same target.

#### DevTools

#### DevTools Helpers
To make your development easier, Reflex Behaviors comes with a browser DevTools helper.
You can enable the helper from your javascript console or your application's javascript file
TurboBoost ships with client/browser based devtools designed to improve the developer experience.
You can enable the devtools with JavaScript like so.

```js
ReflexBehaviors.devtools.start()
TurboBoost.devtools.start()
```


## Introductory Video
[![Watch the introduction on YouTube](https://img.youtube.com/vi/WERDPzOz1sA/maxresdefault.jpg)](https://youtu.be/WERDPzOz1sA "Watch the introduction on YouTube")



## Releasing

1. Run `yarn` and `bundle` to pick up the latest
Expand Down
20 changes: 10 additions & 10 deletions app/assets/builds/@turbo-boost/elements.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions app/assets/builds/@turbo-boost/elements.js.map

Large diffs are not rendered by default.

30 changes: 0 additions & 30 deletions app/commands/turbo_boost/elements/application_command.rb
Original file line number Diff line number Diff line change
@@ -1,34 +1,4 @@
# frozen_string_literal: true

class TurboBoost::Elements::ApplicationCommand < TurboBoost::Commands::Command
protected

def render_payload
return {} if element.renders.blank?
@render_payload ||= {partial: idomatic_partial_path(element.renders)}.tap do |payload|
if element.assigns.present?
payload[:assigns] = JSON.parse(element.assigns)
payload[:assigns].each { |key, value| payload[:assigns][key] = hydrate_value(value) }
end
if element.locals.present?
payload[:locals] = JSON.parse(element.locals)
payload[:locals].each { |key, value| payload[:locals][key] = hydrate_value(value) }
end
end.deep_symbolize_keys
end

private

def hydrate_value(value)
hydrated = begin
GlobalID::Locator.locate_signed(value)
rescue
value
end
hydrated.blank? ? nil : hydrated
end

def idomatic_partial_path(partial_path)
partial_path.to_s.gsub("/_", "/").split(".").first
end
end
36 changes: 33 additions & 3 deletions app/commands/turbo_boost/elements/toggle_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,51 @@ class TurboBoost::Elements::ToggleCommand < TurboBoost::Elements::ApplicationCom
prevent_controller_action

def show
if element.remember == "true"
validate_element!

if element.remember?
state[element.aria.controls] = true
else
state.now[element.aria.controls] = true
end

morph "##{element.morphs}", render(render_payload)
morph id: element.morphs, html: render(element.render_options)
end

def hide
validate_element!
state[element.aria.controls] = false
morph "##{element.morphs}", render(render_payload)
morph id: element.morphs, html: render(element.render_options)
end

def toggle
validate_element!
element.aria.expanded? ? hide : show
end

private

def validate_element!
validate_element_attributes! && validate_element_aria_attributes!
end

def validate_element_attributes!
case element
in {renders: _, morphs: _} then return true
in {renders: _} then raise TurboBoost::Commands::InvalidElementError, "The trigger element is missing the `morphs` attribute!"
in {morphs: _} then raise TurboBoost::Commands::InvalidElementError, "The trigger element is missing the `renders` attribute!"
else raise TurboBoost::Commands::InvalidCommandError, "The trigger element is missing the `renders` and `moprhs` attributes!"
end
false
end

def validate_element_aria_attributes!
case element.aria
in {controls: _, expanded: _} then return true
in {controls: _} then raise TurboBoost::Commands::InvalidElementError, "The trigger element is missing the `aria-expanded` attribute!"
in {expanded: _} then raise TurboBoost::Commands::InvalidElementError, "The trigger element is missing the `aria-controls` attribute!"
else raise TurboBoost::Commands::InvalidElementError, "The trigger element is missing the `aria-controls` and `aria-expanded` attributes!"
end
false
end
end
13 changes: 3 additions & 10 deletions app/helpers/turbo_boost/elements/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,10 @@
require_relative "../../../../lib/turbo_boost/elements/tag_builders"

module TurboBoost::Elements::ApplicationHelper
# Returns an idiomatic path for the currently rendering template
# i.e. How you'd pass the path to a `render partial: ...` call
def current_partial_path
path = nil
prefix = "app/views/"
start = 1
while path.nil? && start < 100
location = caller_locations(start, 1).first
path = location.path if location.path.include?(prefix)
start += 1
end
return "unknown" if path.nil?
path[(path.index(prefix) + prefix.length), path.rindex("/")]
@virtual_path.to_s.gsub("/_", "/")
end

def method_missing(name, ...)
Expand Down
1 change: 0 additions & 1 deletion app/javascript/devtools/toggle.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,6 @@ export default class ToggleDevtool {

tooltip.drag = new PlainDraggable(tooltip)
tooltip.drag.onMove = () => {
console.log('nate', tooltip.line)
tooltip.line.position()
if (tooltip.lineToTarget) tooltip.lineToTarget.position()
if (tooltip.lineToRendering) tooltip.lineToRendering.position()
Expand Down
2 changes: 1 addition & 1 deletion bin/standardize
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

bundle exec magic_frozen_string_literal
bundle exec standardrb --fix
yarn run prettier-standard "app/javascript/**/*.js"
yarn run prettier-standard package.json app/javascript/**/*.js
5 changes: 3 additions & 2 deletions lib/turbo_boost/elements/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ def self.config
class Engine < ::Rails::Engine
config.turbo_boost_elements = ActiveSupport::OrderedOptions.new

ActiveSupport.on_load(:action_controller) do
try :helper, TurboBoost::Elements::ApplicationHelper
ActiveSupport.on_load(:action_controller_base) do
# `self` is ActionController::Base
helper TurboBoost::Elements::ApplicationHelper
end
end
end
15 changes: 0 additions & 15 deletions lib/turbo_boost/elements/tag_builders/base_tag_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,4 @@ def view_stack
memo << location.path[(location.path.index(prefix) + prefix.length)..]
end
end

protected

def dehydrate_value(value)
return value.to_s unless value.respond_to?(:to_sgid_param)
value.try(:persisted?) ? value.to_sgid_param : nil
end

def dehydrate_hash(hash)
hash
.with_indifferent_access
.each_with_object({}.with_indifferent_access) do |(key, val), memo|
memo[key] = dehydrate_value(val)
end
end
end
12 changes: 5 additions & 7 deletions lib/turbo_boost/elements/tag_builders/toggle_tags_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ def trigger_tag(
renders:, # REQUIRED, the partial path to render
morphs:, # REQUIRED, `dom_id` of the partial's outermost containing element
controls:, # REQUIRED, `dom_id` of the toggle target
assigns: {}, # `assigns` required to render the partial (i.e. instance variables)
locals: {}, # `local_assigns` required to render the parital
collapse_selector: nil, # CSS selector for other matching targets to collapse when the target is expanded
focus_selector: nil, # CSS selector for the element to focus when the target is expanded
method: :toggle, # method to inovke (:show, :hide, :toggle)
Expand All @@ -24,18 +22,16 @@ def trigger_tag(
kwargs[:data] ||= {}
kwargs[:data][:turbo_command] = "TurboBoost::Elements::ToggleCommand##{method}" unless disabled

# target / aria
# aria
kwargs[:aria] ||= {}
kwargs[:aria][:controls] = controls
kwargs[:aria][:controls] = controls # toggle target
kwargs[:aria][:expanded] = target_expanded?(controls)
kwargs[:aria][:atomic] ||= true
kwargs[:aria][:relevant] ||= "all"

# rendering
kwargs[:renders] = renders
kwargs[:morphs] = morphs
kwargs[:assigns] = dehydrate_hash(assigns).compact.to_json if assigns.present?
kwargs[:locals] = dehydrate_hash(locals).compact.to_json if locals.present?
kwargs[:view_stack] = view_stack.to_json if Rails.env.development?

# misc
Expand All @@ -44,7 +40,9 @@ def trigger_tag(
kwargs[:remember] = !!remember

args = kwargs.select { |_, value| value.present? }
content_tag("turbo-boost-toggle-trigger", nil, args.transform_keys(&:dasherize), &block)
args = args.transform_keys(&:dasherize)

content_tag("turbo-boost-toggle-trigger", nil, args, &block)
end

def target_tag(
Expand Down
18 changes: 15 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@
"name": "@turbo-boost/elements",
"version": "0.0.1",
"description": "Pre-built easy to use reactive TurboBoost elements for Rails/Hotwire apps.",
"main": "app/javascript/index.js",
"keywords": [
"hotwire",
"hotwired",
"rails",
"turbo",
"turbo-boost",
"web-components"
],
"type": "module",
"main": "app/assets/builds/@turbo-boost/elements.js",
"files": [
"app/assets/builds"
],
"repository": "https://github.com/hopsoft/turbo_boost-elements",
"author": "Nate Hopkins (hopsoft) <natehop@gmail.com>",
"license": "MIT",
"dependencies": {
"@turbo-boost/commands": ">= 0.0.1"
"@turbo-boost/commands": ">= 0.0.4"
},
"peerDependencies": {
"@hotwired/turbo-rails": ">= 7.2"
Expand All @@ -18,6 +30,6 @@
"prettier-standard": "^16.4.1"
},
"scripts": {
"build": "esbuild app/javascript/index.js --bundle --minify --sourcemap --format=esm --outfile=app/assets/builds/@turbo-boost/elements.js"
"build": "esbuild app/javascript/index.js --bundle --minify --sourcemap --format=esm --target=es2020,chrome58,firefox57,safari11 --analyze --outfile=app/assets/builds/@turbo-boost/elements.js"
}
}
2 changes: 1 addition & 1 deletion turbo_boost-elements.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Gem::Specification.new do |s|

s.add_dependency "rails", ">= 6.1"
s.add_dependency "turbo-rails", ">= 1.1"
s.add_dependency "turbo_boost-commands", ">= 0.0.2"
s.add_dependency "turbo_boost-commands", ">= 0.0.4"

s.add_development_dependency "magic_frozen_string_literal"
s.add_development_dependency "minitest-reporters"
Expand Down
Loading

0 comments on commit 624919f

Please sign in to comment.