PageSourceSearch

https://reach.wellfound.com/assets/controllers/marketing/pricing_t…98ab887599707b46fd125986851dafdf092c9048.js

js wellfound.com collected 2026-09-24 08:31:27 UTC 2,934 bytes, 86 lines download raw bytes

1import { Controller } from "@hotwired/stimulus";
2
3// Quick-jump tab nav for the marketing /pricing page.
4//
5// All panels stay visible at all times. Tabs act as a scroll-to-section
6// nav (Stripe-style sub-nav). As the user scrolls, the tab corresponding
7// to the most-visible panel becomes active.
8//
9// Markup contract:
10//   data-controller="marketing--pricing-tabs"
11//
12//   tab buttons:     data-marketing--pricing-tabs-target="tab"
13//                    data-product="<product-key>"
14//                    data-action="click->marketing--pricing-tabs#select"
15//   panels:          data-marketing--pricing-tabs-target="panel"
16//                    data-product="<product-key>"
17//                    id="<product-key>"   (so #hash deep-links work)
18export default class extends Controller {
19  static targets = ["tab", "panel"];
20  static values = {
21    navOffset: { type: Number, default: 90 },
22  };
23
24  connect() {
25    this.#setupIntersectionObserver();
26
27    const hashKey = window.location.hash.slice(1);
28    const hashPanel = this.panelTargets.find((p) => p.dataset.product === hashKey);
29
30    if (hashPanel) {
31      // Defer one frame so the layout has settled before measuring.
32      requestAnimationFrame(() => this.#scrollToPanel(hashPanel, false));
33    } else if (this.tabTargets.length > 0) {
34      this.#setActive(this.tabTargets[0].dataset.product);
35    }
36  }
37
38  disconnect() {
39    if (this.observer) this.observer.disconnect();
40  }
41
42  select(event) {
43    event.preventDefault();
44    const product = event.currentTarget.dataset.product;
45    if (!product) return;
46    const panel = this.panelTargets.find((p) => p.dataset.product === product);
47    if (!panel) return;
48    this.#scrollToPanel(panel, true);
49    history.replaceState(null, "", `#${product}`);
50  }
51
52  #setupIntersectionObserver() {
53    // Pick the panel whose top is closest to (but past) the offset line.
54    this.observer = new IntersectionObserver(
55      (entries) => {
56        entries.forEach((entry) => {
57          if (entry.isIntersecting) {
58            this.#setActive(entry.target.dataset.product);
59          }
60        });
61      },
62      {
63        // Activate as the panel crosses the line just under the sticky nav,
64        // and deactivate before it fully leaves through the bottom.
65        rootMargin: `-${this.navOffsetValue + 20}px 0px -55% 0px`,
66        threshold: 0,
67      },
68    );
69    this.panelTargets.forEach((p) => this.observer.observe(p));
70  }
71
72  #setActive(product) {
73    if (!product) return;
74    this.tabTargets.forEach((t) => {
75      const on = t.dataset.product === product;
76      t.classList.toggle("on", on);
77      t.setAttribute("aria-selected", on ? "true" : "false");
78      t.setAttribute("tabindex", on ? "0" : "-1");
79    });
80  }
81
82  #scrollToPanel(panel, smooth) {
83    const top = panel.getBoundingClientRect().top + window.scrollY - this.navOffsetValue;
84    window.scrollTo({ top, behavior: smooth ? "smooth" : "auto" });
85  }
86};

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.