PageSourceSearch

https://internationaldayofzerowaste.org/assets/admin_controllers/nav_rail_controller-7d1944bc.js

js internationaldayofzerowaste.org collected 2026-09-29 11:08:49 UTC 2,236 bytes, 67 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3// Slides a single accent indicator along an expanded nav group's sub-item rail.
4// It rests on the active sub-item and follows the pointer to whichever sub-item
5// is hovered, sliding back to the active one when the pointer leaves the group.
6export default class extends Controller {
7  static targets = ["indicator", "item"]
8
9  // Vertical padding so the bar sits shorter than the item (matches the old
10  // top-1.5 / bottom-1.5 inset).
11  INSET = 6
12
13  connect() {
14    this.currentItem = this.activeItem
15    this.place(this.currentItem, { animate: false })
16
17    this.itemTargets.forEach((item) => item.addEventListener("mouseenter", this.onEnter))
18    this.element.addEventListener("mouseleave", this.onLeave)
19
20    this.resizeObserver = new ResizeObserver(() => this.place(this.currentItem, { animate: false }))
21    this.resizeObserver.observe(this.element)
22  }
23
24  disconnect() {
25    this.itemTargets.forEach((item) => item.removeEventListener("mouseenter", this.onEnter))
26    this.element.removeEventListener("mouseleave", this.onLeave)
27    this.resizeObserver?.disconnect()
28  }
29
30  onEnter = (event) => {
31    this.currentItem = event.currentTarget
32    this.place(this.currentItem, { animate: true })
33  }
34
35  onLeave = () => {
36    this.currentItem = this.activeItem
37    this.place(this.currentItem, { animate: true })
38  }
39
40  place(item, { animate }) {
41    if (!this.hasIndicatorTarget) return
42    const el = this.indicatorTarget
43
44    if (!item) {
45      el.style.opacity = "0"
46      return
47    }
48
49    const top = item.offsetTop + this.INSET
50    const height = Math.max(0, item.offsetHeight - this.INSET * 2)
51    const active = item.dataset.navRailActive === "true"
52
53    if (!animate) el.style.transition = "none"
54    el.style.transform = `translateY(${top}px)`
55    el.style.height = `${height}px`
56    el.style.backgroundColor = active ? "var(--zw-blue-700)" : "var(--zw-blue-500)"
57    el.style.opacity = "1"
58    if (!animate) {
59      el.offsetHeight // force reflow so the resumed transition doesn't animate the initial placement
60      el.style.transition = ""
61    }
62  }
63
64  get activeItem() {
65    return this.itemTargets.find((item) => item.dataset.navRailActive === "true") || null
66  }
67}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.