PageSourceSearch

https://www.hotelmanagement-network.com/wp-content/themes/goodlife…/assets/js/modules/GDMFiltersAccordion.js

js hotelmanagement-network.com collected 2026-10-02 04:28:50 UTC 6,099 bytes, 187 lines download raw bytes

1export default function GDMFiltersAccordion(
2  containerSelector = ".insights_filters",
3) {
4  const container = document.querySelector(containerSelector);
5  if (!container) return;
6
7  const headers = Array.from(container.querySelectorAll(".fancytree-header"));
8
9  // When the "Apply filters" button is clicked, close the filters panel by
10  // removing the classes that open it (mobile drawer behaviour): "open" (used
11  // by the accordion/filter-groups)and "insights-filters-open" (used by the
12  // filters-toggle modal drawer on mobile).
13  const applyFiltersButton = container.querySelector(".apply-filters");
14
15  if (applyFiltersButton) {
16    applyFiltersButton.addEventListener("click", () => {
17      container.classList.remove("open", "insights-filters-open");
18      document.body.classList.remove("freeze-element");
19    });
20  }
21
22  if (headers.length === 0) return;
23
24  const getPaneForHeader = (header) => {
25    const toggleId = header.getAttribute("data-toggle");
26
27    if (toggleId) {
28      const normalizedId = toggleId.replace(/^#/, "");
29      const paneById = document.getElementById(normalizedId);
30      if (paneById && paneById.classList.contains("dropdown-pane")) {
31        return paneById;
32      }
33    }
34
35    const siblingPane = header.nextElementSibling;
36    if (siblingPane && siblingPane.classList.contains("dropdown-pane")) {
37      return siblingPane;
38    }
39
40    return null;
41  };
42
43  const getOrCreateSummary = (pane) => {
44    let summary = pane.nextElementSibling;
45
46    if (!summary || !summary.classList.contains("gdm-filters-summary")) {
47      summary = document.createElement("div");
48      summary.className = "gdm-filters-summary";
49      pane.insertAdjacentElement("afterend", summary);
50    }
51
52    return summary;
53  };
54
55  // Render the collapsed group's summary. When filters are checked we show the
56  // checked items exactly as they appear when the group is open (via the
57  // `ul.selected-filters.show` styling applied by the theme CSS: checkbox +
58  // checked background + check icon), and each item can be clicked to uncheck
59  // the filter without opening the group. With nothing selected we show the
60  // "No filters selected" text.
61  const renderSummary = (pane, summary) => {
62    summary.innerHTML = "";
63
64    const selectedLis = Array.from(
65      pane.querySelectorAll('li[aria-selected="true"]'),
66    ).filter((li) => li.getAttribute("data-os-facet-data") !== "any");
67
68    if (selectedLis.length === 0) {
69      summary.innerHTML = "";
70      return;
71    }
72
73    // "show" is required so the theme's ul.selected-filters.show rules style
74    // these checkboxes exactly like the open tree (checked box + check icon).
75    const list = document.createElement("ul");
76    list.className = "selected-filters show gdm-selected-filters-summary";
77
78    selectedLis.forEach((li) => {
79      const liClone = li.cloneNode(true);
80      liClone.setAttribute("aria-selected", "true");
81
82      liClone.querySelectorAll("ul").forEach((list) => list.remove());
83
84      const node = liClone.querySelector(".fancytree-node");
85      if (node) {
86        node.classList.add("fancytree-selected");
87      }
88
89      const checkbox = liClone.querySelector(".fancytree-checkbox");
90      if (checkbox) {
91        checkbox.setAttribute("aria-checked", "true");
92      }
93
94      // Clicking anywhere on the inserted item unchecks the filter. We dispatch
95      // a bubbling click on the ORIGINAL tree item so the plugin's own delegated
96      // handler runs (it unchecks, re-sorts, refreshes counts and re-applies the
97      // filters). `MouseEvent` defaults to non-bubbling, which would never reach
98      // the container-bound handler, so bubbles must be set explicitly.
99      const uncheck = (event) => {
100        event.preventDefault();
101        event.stopPropagation();
102        li.dispatchEvent(
103          new MouseEvent("click", { bubbles: true, cancelable: true }),
104        );
105      };
106
107      // One handler on the row handles clicks on the checkbox OR the label —
108      // clicks on the checkbox bubble up to the li and are not stopped, so the
109      // checkbox itself is clickable to uncheck.
110      liClone.addEventListener("click", uncheck);
111
112      list.appendChild(liClone);
113    });
114
115    summary.appendChild(list);
116  };
117
118  const syncFiltersSummary = (pane) => {
119    const summary = getOrCreateSummary(pane);
120    const precedingPane = summary.previousElementSibling;
121    const precedingPaneIsOpen =
122      precedingPane &&
123      precedingPane.classList.contains("dropdown-pane") &&
124      precedingPane.classList.contains("open");
125
126    if (precedingPaneIsOpen) {
127      // Group is open: the real tree is visible and interactive, hide the summary.
128      summary.style.display = "none";
129      return;
130    }
131
132    renderSummary(pane, summary);
133  };
134
135  const canOpenPane = (pane) => {
136    if (!pane) return false;
137    return pane.style.display !== "none";
138  };
139
140  headers.forEach((header) => {
141    const pane = getPaneForHeader(header);
142    if (pane) syncFiltersSummary(pane);
143  });
144
145  // Re-sync the summaries whenever a selection changes or the tree is
146  // re-rendered (AJAX filter updates rebuild the tree markup).
147  headers.forEach((header) => {
148    const pane = getPaneForHeader(header);
149    if (!pane) return;
150
151    const observer = new MutationObserver(() => syncFiltersSummary(pane));
152    observer.observe(pane, {
153      childList: true,
154      subtree: true,
155      attributes: true,
156      attributeFilter: ["aria-selected", "class"],
157    });
158  });
159
160  headers.forEach((header) => {
161    header.addEventListener("click", () => {
162      const pane = getPaneForHeader(header);
163
164      if (!pane) return;
165
166      if (!canOpenPane(pane)) {
167        header.classList.remove("open");
168        pane.classList.remove("open");
169        syncFiltersSummary(pane);
170        return;
171      }
172
173      const isOpen = pane.classList.contains("open");
174
175      if (isOpen) {
176        header.classList.remove("open");
177        pane.classList.remove("open");
178        syncFiltersSummary(pane);
179        return;
180      }
181
182      header.classList.add("open");
183      pane.classList.add("open");
184      syncFiltersSummary(pane);
185    });
186  });
187}

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.