PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.5
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.5
4.9.5 4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 All 203 releases
betterdocs / react-src / utils / categoryGrid.js

categoryGrid.js in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.5, at react-src/utils/categoryGrid.js

203 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 export default function ( $scope, $ ) {
2 let masonryGrids = $(".betterdocs-category-grid-inner-wrapper.masonry", $scope);
3
4 if ( masonryGrids?.length == 0 ) {
5 let masonryItems = $('.betterdocs-single-category-wrapper', $scope);
6 (masonryItems || []).each((_, item) => {
7 item.removeAttribute("style");
8 });
9
10 return;
11 }
12
13 var mobileDevice = window.matchMedia("(max-width: 767px)");
14 var tabDevice = window.matchMedia("(max-width: 1024px)");
15
16 masonryGrids.each((_, masonryGrid) => {
17 let columnPerGrid = 0;
18 let column_space = 0;
19
20 switch (true) {
21 case mobileDevice.matches:
22 columnPerGrid = masonryGrid.getAttribute("data-column_mobile");
23 column_space = masonryGrid.getAttribute("data-column_space_mobile");
24 break;
25 case tabDevice.matches:
26 columnPerGrid = masonryGrid.getAttribute("data-column_tab");
27 column_space = masonryGrid.getAttribute("data-column_space_tab");
28 break;
29 default:
30 columnPerGrid = masonryGrid.getAttribute("data-column_desktop");
31 column_space = masonryGrid.getAttribute("data-column_space_desktop") ?? 15;
32 break;
33 }
34
35 column_space = parseInt(column_space);
36 columnPerGrid = parseInt(columnPerGrid);
37
38 let masonryItems = masonryGrid.querySelectorAll(".betterdocs-single-category-wrapper");
39 let total_margin = (columnPerGrid - 1) * column_space;
40
41 if (masonryGrid) {
42 masonryItems.forEach((item) => {
43 item.style.width = `calc((100% - ${total_margin}px) / ${columnPerGrid})`;
44 });
45
46 new Masonry(masonryGrid, {
47 itemSelector: ".betterdocs-single-category-wrapper",
48 percentPosition: true,
49 gutter: column_space,
50 });
51 }
52 });
53 }
54
55
56 export function toggleList() {
57 var $ = jQuery;
58
59 let currentActiveCat = $(".betterdocs-sidebar-content .betterdocs-category-grid-wrapper .betterdocs-single-category-wrapper.active");
60 let catTitleList = $(".betterdocs-sidebar-content .betterdocs-category-grid-wrapper .betterdocs-single-category-wrapper .betterdocs-category-header");
61
62 // Nested-category interactions are delegated off document because the
63 // markup is injected later via AJAX (lazy-load) and any handler bound
64 // directly on init would miss those nodes.
65 $(document).on("click", ".betterdocs-nested-category-title a", function (e) {
66 e.preventDefault();
67 });
68
69 currentActiveCat
70 .addClass("show")
71 .find(".betterdocs-body")
72 .css("display", "block");
73 currentActiveCat
74 .siblings()
75 .find(".betterdocs-body")
76 .css("display", "none");
77
78 // Inject a skeleton (animated placeholder rows) into a lazy body and
79 // fetch its real content. The skeleton stays visible for at least
80 // SKELETON_MIN_MS so the user perceives a clear loading state even if
81 // the AJAX returns almost instantly. Returns a promise that resolves
82 // after both the fetch and the minimum-delay timer have completed.
83 var SKELETON_MIN_MS = 1000;
84 var SKELETON_HTML = (
85 '<ul class="betterdocs-skeleton" aria-hidden="true">' +
86 '<li><span class="betterdocs-skeleton-bar"></span></li>' +
87 '<li><span class="betterdocs-skeleton-bar"></span></li>' +
88 '<li><span class="betterdocs-skeleton-bar"></span></li>' +
89 '<li><span class="betterdocs-skeleton-bar"></span></li>' +
90 '<li><span class="betterdocs-skeleton-bar"></span></li>' +
91 '</ul>'
92 );
93
94 function loadLazyBody($body) {
95 if ($body.attr("data-bd-loaded") === "1" || $body.attr("data-bd-loading") === "1") {
96 return $.Deferred().resolve().promise();
97 }
98 var cfg = window.betterdocsCategoryGridConfig || {};
99 if (!cfg.ajax_url) {
100 return $.Deferred().reject().promise();
101 }
102
103 $body.html(SKELETON_HTML);
104 $body.attr("data-bd-loading", "1");
105
106 // Sleek sidebar (layout-7) uses folder icons on nested-category-title.
107 // The initial PHP render gets this via sidebar-7.php's
108 // category_icon=folder shortcode attr; the AJAX path needs to be told
109 // explicitly so its rendered HTML matches.
110 var category_icon = $body.closest(".betterdocs-sidebar-layout-7").length ? "folder" : "";
111
112 var ajax = $.post(cfg.ajax_url, {
113 action: cfg.lazy_load_action || "betterdocs_lazy_category_body",
114 term_id: $body.attr("data-bd-term-id"),
115 kb_slug: cfg.kb_slug || "",
116 multiple_kb: cfg.multiple_kb ? 1 : 0,
117 category_icon: category_icon
118 });
119
120 var minDelay = $.Deferred();
121 setTimeout(function () { minDelay.resolve(); }, SKELETON_MIN_MS);
122
123 return $.when(ajax, minDelay.promise())
124 .done(function (ajaxArgs) {
125 // $.when wraps single-promise resolve values in arrays.
126 var resp = $.isArray(ajaxArgs) ? ajaxArgs[0] : ajaxArgs;
127 if (resp && resp.success && resp.data && typeof resp.data.html === "string") {
128 $body.html(resp.data.html);
129 $body.attr("data-bd-loaded", "1");
130 $body.removeAttr("data-bd-lazy");
131 }
132 })
133 .always(function () {
134 $body.removeAttr("data-bd-loading");
135 });
136 }
137
138 catTitleList.on("click", function (e) {
139 e.preventDefault();
140 var $parentCat = jQuery(e.target).closest(".betterdocs-single-category-wrapper");
141 var $body = $parentCat.children(".betterdocs-single-category-inner").children(".betterdocs-body");
142 if ($body.length === 0) {
143 $body = $parentCat.find(".betterdocs-body").first();
144 }
145 var isLazy = $body.attr("data-bd-lazy") === "1";
146 var isLoaded = $body.attr("data-bd-loaded") === "1";
147
148 // Kick off the lazy fetch synchronously so the skeleton paints
149 // immediately on the slide-down (loadLazyBody injects the skeleton
150 // into $body before returning the AJAX promise).
151 if (isLazy && !isLoaded) {
152 loadLazyBody($body);
153 }
154
155 $body.slideToggle();
156 $parentCat.toggleClass("show");
157 $parentCat.siblings()
158 .removeClass("active show")
159 .find(".betterdocs-body").slideUp();
160 $parentCat.addClass("active");
161 });
162
163 $(document).on("click", ".betterdocs-nested-category-title", function (e) {
164 e.preventDefault();
165 var $self = $(this);
166 // Sleek Layout (.betterdocs-sidebar-layout-7) uses fixed folder icons
167 // pinned by !important CSS -- jQuery's .toggle() ends up hiding both
168 // arrow SVGs because the inline display can't beat the !important
169 // arrow-down rule. Skip the arrow swap there and let CSS keep its
170 // intended static visual.
171 if (!$self.closest(".betterdocs-sidebar-layout-7").length) {
172 $self.children(".toggle-arrow").toggle();
173 }
174 var $list = $self.next(".betterdocs-nested-category-list");
175 if ($list.hasClass("active")) {
176 $list.slideUp("fast", "swing", function () { $(this).toggleClass("active"); });
177 } else {
178 $list.slideDown("fast", "swing", function () { $(this).toggleClass("active"); });
179 }
180 });
181
182 // Memphis layout (sidebar-2): IntersectionObserver triggers AJAX as each
183 // .betterdocs-body scrolls into view, since the layout has no click-to-toggle
184 // category header.
185 var memphis = document.querySelector(".betterdocs-sidebar-layout-2");
186 if (memphis && "IntersectionObserver" in window) {
187 var pending = memphis.querySelectorAll('.betterdocs-body[data-bd-lazy="1"]:not([data-bd-loaded="1"])');
188 if (pending.length) {
189 var observer = new IntersectionObserver(function (entries) {
190 entries.forEach(function (entry) {
191 if (!entry.isIntersecting) return;
192 observer.unobserve(entry.target);
193 var $b = $(entry.target);
194 loadLazyBody($b).done(function () {
195 $b.css("display", "block");
196 });
197 });
198 }, { rootMargin: "200px 0px" });
199 pending.forEach(function (el) { observer.observe(el); });
200 }
201 }
202 }
203