Last active
May 2, 2022 11:21
-
-
Save zelaznik/86bc43cec8da28c4866ecc8d35cf1658 to your computer and use it in GitHub Desktop.
Making an "active" link in Rails / Stimulus
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Controller } from "@hotwired/stimulus" | |
// Connects to data-controller="active-link" | |
export default class extends Controller { | |
static values = { | |
activeClass: { type: String, default: 'active' } | |
} | |
connect() { | |
this._syncActive(document.location) | |
let oldHref = document.location.href | |
this._observer = new MutationObserver(() => { | |
if (oldHref != document.location.href) { | |
this._syncActive(document.location) | |
oldHref = document.location.href | |
} | |
}) | |
this._observer.observe(document, { attributes: true, subtree: true }) | |
} | |
disconnect() { | |
this._observer.disconnect() | |
} | |
_syncActive(location) { | |
const href = this.element.getAttribute('href') | |
const isActive = | |
location.pathname == href || | |
location.pathname.startsWith(`${href}/`) || | |
location.pathname.startsWith(`${href}?`) | |
if (isActive) { | |
this.element.classList.add(this.activeClassValue) | |
} else { | |
this.element.classList.remove(this.activeClassValue) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module ApplicationHelper | |
def active_link_to(*args, **params, &block) | |
href = block_given? ? args.first : args[1] | |
active_class_name = params.delete(:active_class_name) || 'active' | |
if route_is_active?(href) | |
# Add in the active class name in ruby, just in case javascript is disabled | |
existing_classes = params[:class] || '' | |
params[:class] = appended_name(existing_classes, active_class_name) | |
end | |
# Add in the 'active-link' controller, but don't erase any other simulus controllers either | |
params[:data] = params[:data] || {} | |
existing_controllers = params[:data][:controller] || '' | |
params[:data][:controller] = appended_name(existing_controllers, 'active-link') | |
# Pass the optional "active_class_name" value to the active-link controller | |
params[:data][:active_link_active_class_value] = active_class_name | |
link_to(*args, **params, &block) | |
end | |
def route_is_active?(href) | |
request.path == href || | |
request.path.starts_with?("#{href}/") || | |
request.path.starts_with?("#{href}?") | |
end | |
def appended_name(original, name) | |
class_names = original.strip.split(/\s+/).to_set | |
class_names << name | |
class_names.join(' ') | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- The reason for this link in the first place was that links were not highlighting | |
When it targeted a turbo-frame and not the entire page, hence the data-tubo- attributes --> | |
<!-- Nevetheless, this pattern also makes a link "future-proof" against any turbo-frames, | |
because it moves the logic from Ruby to Javascript to figure out whether a link is active --> | |
<%= active_link_to image_tag(item.bait.image, alt: item.bait.name), | |
tackle_box_item_path(item), | |
active_class_name: 'selected', # This attribute is optional and defaults to the value 'active' | |
data: { | |
turbo_frame: "selected_tackle_box_item", | |
turbo_action: "replace" | |
} %> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment