diff --git a/Gemfile.lock b/Gemfile.lock index c53bc1c..48fb071 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,8 +1,8 @@ PATH remote: . specs: - dev_toolbar (0.1.0) - rails (~> 7.0) + dev_toolbar (2.1.0) + rails (>= 7.0) GEM remote: https://rubygems.org/ @@ -122,6 +122,8 @@ GEM nio4r (2.7.3) nokogiri (1.16.5-x86_64-darwin) racc (~> 1.4) + nokogiri (1.16.5-x86_64-linux) + racc (~> 1.4) psych (5.1.2) stringio racc (1.8.0) @@ -180,6 +182,7 @@ GEM PLATFORMS x86_64-darwin-22 + x86_64-linux DEPENDENCIES bundler (~> 2.0) diff --git a/README.md b/README.md index 559641e..f0bca1a 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,16 @@ if Rails.env.development? end ``` +Include the JavaScript: +```rb +# config/importmap.rb +pin "dev_toolbar", to: "dev_toolbar/index.js" +pin "dev_toolbar/toolbar", to: "dev_toolbar/toolbar.js" + +# app/javascripts/application.js +import "dev_toolbar" +``` + These routes will now appear on every page in your app while in development. ## Updating the gem diff --git a/app/assets/javascripts/dev_toolbar/index.js b/app/assets/javascripts/dev_toolbar/index.js new file mode 100644 index 0000000..b343c20 --- /dev/null +++ b/app/assets/javascripts/dev_toolbar/index.js @@ -0,0 +1,34 @@ +import Toolbar from "dev_toolbar/toolbar" + +function waitForElementToExist(selector) { + return new Promise(resolve => { + if (document.querySelector(selector)) { + return resolve(document.querySelector(selector)); + } + + const observer = new MutationObserver(() => { + if (document.querySelector(selector)) { + resolve(document.querySelector(selector)); + observer.disconnect(); + } + }); + + observer.observe(document.body, { + subtree: true, + childList: true, + }); + }); +} +const loadEvent = self.hasOwnProperty("Turbo") ? "turbo:load" : "DOMContentLoaded"; + +document.addEventListener(loadEvent, function() { + if (!document.getElementById("dev-toolbar")) { + Toolbar.render(); + } + waitForElementToExist("#dev-toolbar-toggle").then( () => { + document.getElementById("dev-toolbar-toggle").addEventListener("click", function() { + var links = document.getElementById("dev-toolbar-links"); + links.classList.toggle("hidden"); + }); + }); +}); diff --git a/app/assets/javascripts/dev_toolbar/toolbar.js b/app/assets/javascripts/dev_toolbar/toolbar.js new file mode 100644 index 0000000..619726b --- /dev/null +++ b/app/assets/javascripts/dev_toolbar/toolbar.js @@ -0,0 +1,24 @@ +export default class Toolbar { + static render() { + const configuration = document.querySelector("meta[name=dev_toolbar_config]") + const defined_links = JSON.parse(configuration.content) + let toolbar_links = `` + for (let index = 0; index < defined_links.length; index++) { + const link = defined_links[index]; + toolbar_links += `${link.name}` + } + const toolbar_html = ` +
+
+ +
+ +
+ ` + document.body.insertAdjacentHTML('beforeend', toolbar_html) + } + +} +export { Toolbar } diff --git a/app/assets/stylesheets/dev_toolbar.css b/app/assets/stylesheets/dev_toolbar.css new file mode 100644 index 0000000..a110d10 --- /dev/null +++ b/app/assets/stylesheets/dev_toolbar.css @@ -0,0 +1,41 @@ +#dev-toolbar { + position: fixed; + right: 0; + top: 50vh; + transform: translateY(-50%); + background-color: #f0f0f0; + border: 1px solid #ccc; + z-index: 1000; + display: flex; + flex-direction: column; + align-items: center; + font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif; + color: #808080; +} + +#dev-toolbar-toggle { + all: unset; + font-size: 2em; + border: none; + cursor: pointer; + line-height: 1.5; + padding: 0 10px; + text-decoration: none; +} + +#dev-toolbar-links { + display: flex; + flex-direction: column; +} + +.dev-toolbar-link { + padding: 5px 10px; + border-bottom: 1px #f0f0f0 solid; + color: #808080; + text-decoration: none; + background-color: white; +} + +#dev-toolbar-links.hidden { + display: none; +} diff --git a/lib/dev_toolbar/app/controllers/dev_toolbar/erd_controller.rb b/app/controllers/dev_toolbar/erd_controller.rb similarity index 75% rename from lib/dev_toolbar/app/controllers/dev_toolbar/erd_controller.rb rename to app/controllers/dev_toolbar/erd_controller.rb index 07a805e..d42f8e7 100644 --- a/lib/dev_toolbar/app/controllers/dev_toolbar/erd_controller.rb +++ b/app/controllers/dev_toolbar/erd_controller.rb @@ -4,7 +4,7 @@ class ErdController < ActionController::Base def show @erd_path = Rails.root.join("erd.png") - render :show + render "dev_toolbar/erd/show", formats: [:html] end end end diff --git a/lib/dev_toolbar/app/views/dev_toolbar/erd/show.html.erb b/app/views/dev_toolbar/erd/show.html.erb similarity index 100% rename from lib/dev_toolbar/app/views/dev_toolbar/erd/show.html.erb rename to app/views/dev_toolbar/erd/show.html.erb diff --git a/lib/dev_toolbar/engine.rb b/lib/dev_toolbar/engine.rb index 046d601..4703ef2 100644 --- a/lib/dev_toolbar/engine.rb +++ b/lib/dev_toolbar/engine.rb @@ -2,9 +2,25 @@ module DevToolbar class Engine < ::Rails::Engine isolate_namespace DevToolbar - config.autoload_paths << File.expand_path("../app/controllers", __FILE__) - config.paths["app/views"] << File.expand_path("../app/views", __FILE__) - + config.assets.paths << root.join("app/assets/stylesheets") + + initializer "dev_toolbar.assets_precompile", group: :all do |app| + # Only configure asset precompilation if Sprockets is available + if defined?(Sprockets) && app.config.respond_to?(:assets) + app.config.assets.precompile += [ + "dev_toolbar/toolbar.js", + "dev_toolbar/index.js", + ] + end + end + + initializer "dev_toolbar.add_static_assets_middleware" do |app| + app.middleware.use ::Rack::Static, + # the url prefix to intercept + urls: ["/dev_toolbar"], + root: "#{root}/app/" + end + initializer "dev_toolbar.add_routes", after: :add_routing_paths do |app| app.routes.append do get "/erd", to: "dev_toolbar/erd#show" diff --git a/lib/dev_toolbar/middleware.rb b/lib/dev_toolbar/middleware.rb index d1ee2d0..ebf6516 100644 --- a/lib/dev_toolbar/middleware.rb +++ b/lib/dev_toolbar/middleware.rb @@ -10,65 +10,10 @@ def call(env) if Rails.env.development? && headers["Content-Type"]&.include?("text/html") response_body = response.body toolbar_html = <<-HTML -
-
- 🛠️ -
- -
- - + HTML - response_body.sub!('', "#{toolbar_html}") + response_body.sub!('', "#{toolbar_html}") headers["Content-Length"] = response_body.bytesize.to_s response = [response_body] @@ -79,15 +24,8 @@ def call(env) private - def toolbar_links - DevToolbar.configuration.links.map do |link| - # if the erd.png file does not exist in /public, don't show the link - if link[:name] == "ERD" && !File.exist?(Rails.root.join("erd.png")) - next - else - "#{link[:name]}" - end - end.compact.join(" ") + def toolbar_links_content + JSON.generate(DevToolbar.configuration.links) end end end