PageSourceSearch

https://catalog.kuzcolighting.com/assets/controllers/org_settings_…085c1bb292787ce08159e855370102a069ea23b8.js

js kuzcolighting.com collected 2026-09-30 15:24:04 UTC 3,879 bytes, 131 lines download raw bytes

1import { Controller } from "@hotwired/stimulus"
2
3// Company Settings: one panel visible; active section is reflected in the URL as #fragment.
4// Optional ?tab=panel_id is accepted for deep links and normalized to #panel_id (query param removed).
5// Tab bar uses flex-wrap so pills flow to multiple rows instead of horizontal scroll.
6export default class extends Controller {
7  static targets = ["tab", "panel"]
8  static values = {
9    defaultPanel: String,
10  }
11
12  connect() {
13    this._onHashChange = this._onHashChange.bind(this)
14    this._onPopState = this._onPopState.bind(this)
15    window.addEventListener("hashchange", this._onHashChange)
16    window.addEventListener("popstate", this._onPopState)
17
18    const fromQuery = this._panelIdFromQuery()
19    const fromHash = this._panelIdFromHash()
20
21    let panelId = null
22    if (fromQuery && this._validTab(fromQuery)) {
23      panelId = fromQuery
24      this._replaceUrlHash(panelId)
25    } else if (fromHash && this._validTab(fromHash)) {
26      panelId = fromHash
27    } else if (fromHash || fromQuery) {
28      panelId = this._fallbackPanelId()
29      this._replaceUrlHash(panelId)
30    } else {
31      panelId = this._fallbackPanelId()
32    }
33
34    this.activate(panelId, { syncUrl: false })
35  }
36
37  disconnect() {
38    window.removeEventListener("hashchange", this._onHashChange)
39    window.removeEventListener("popstate", this._onPopState)
40  }
41
42  show(event) {
43    const panelId = event.currentTarget.dataset.panel
44    if (!panelId) return
45    this.activate(panelId, { syncUrl: true })
46  }
47
48  /** @param {{ syncUrl?: boolean }} opts */
49  activate(panelId, opts = {}) {
50    const { syncUrl = true } = opts
51    if (!this._validTab(panelId)) return
52
53    this.panelTargets.forEach((panel) => {
54      panel.classList.toggle("hidden", panel.dataset.panel !== panelId)
55    })
56
57    this.tabTargets.forEach((tab) => {
58      const selected = tab.dataset.panel === panelId
59      tab.setAttribute("aria-selected", String(selected))
60      tab.classList.toggle("border-sc-crimson", selected)
61      tab.classList.toggle("bg-white", selected)
62      tab.classList.toggle("text-sc-crimson", selected)
63      tab.classList.toggle("shadow-sm", selected)
64      tab.classList.toggle("border-transparent", !selected)
65      tab.classList.toggle("text-sc-text-2", !selected)
66    })
67
68    if (syncUrl) this._replaceUrlHash(panelId)
69  }
70
71  _onHashChange() {
72    const id = this._panelIdFromHash()
73    if (this._validTab(id)) this.activate(id, { syncUrl: false })
74  }
75
76  _onPopState() {
77    const id = this._panelIdFromHash()
78    if (this._validTab(id)) this.activate(id, { syncUrl: false })
79    else if (id) {
80      const fallback = this._fallbackPanelId()
81      this.activate(fallback, { syncUrl: true })
82    } else {
83      this.activate(this._fallbackPanelId(), { syncUrl: false })
84    }
85  }
86
87  _validTab(panelId) {
88    if (!panelId) return false
89    return this.tabTargets.some((t) => t.dataset.panel === panelId)
90  }
91
92  _fallbackPanelId() {
93    if (this.defaultPanelValue && this._validTab(this.defaultPanelValue)) {
94      return this.defaultPanelValue
95    }
96    return this.tabTargets[0]?.dataset.panel || ""
97  }
98
99  _panelIdFromHash() {
100    const raw = (window.location.hash || "").replace(/^#/, "")
101    if (!raw) return ""
102    try {
103      return decodeURIComponent(raw)
104    } catch {
105      return raw
106    }
107  }
108
109  _panelIdFromQuery() {
110    try {
111      return new URL(window.location.href).searchParams.get("tab") || ""
112    } catch {
113      return ""
114    }
115  }
116
117  /** Sets #fragment, drops ?tab=, preserves other query params */
118  _replaceUrlHash(panelId) {
119    try {
120      const url = new URL(window.location.href)
121      url.searchParams.delete("tab")
122      url.hash = panelId
123      const next = url.toString()
124      if (window.location.href === next) return
125      history.replaceState(null, document.title, next)
126    } catch {
127      /* ignore */
128    }
129  }
130}
131;

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.