PluginProbe
TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables / 2.2.14
TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables v2.2.14
2.2.14 2.2.13 2.2.12 2.2.11 2.2.10 2.2.9 2.2.8 2.2.7 2.2.6 2.2.5 2.2.4 2.2.3 trunk 1.0.0 1.0.1 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.2.0 2.2.1 2.2.2
table-builder-block / assets / js / elementor-editor-block-picker.js

elementor-editor-block-picker.js in TableKit – WordPress Table Builder for Data Tables, WooCommerce Product Tables & Post Tables 2.2.14, at assets/js/elementor-editor-block-picker.js

345 lines 9.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Elementor editor integration for the TableKit widget's block picker.
3 *
4 * @file Watches the TableKit Elementor widget's "table_id" control and,
5 * whenever it changes, fetches that table's block list from the
6 * tablekit/v1/table-blocks REST route and repopulates the widget's
7 * "block_index" control so the user can target a specific table
8 * block instead of rendering every block in the table.
9 * @since Unknown
10 */
11
12 (function () {
13 'use strict';
14
15 /**
16 * Base REST URL for the table-blocks endpoint, localized from PHP.
17 *
18 * @since Unknown
19 *
20 * @type {string}
21 */
22 var REST_URL = window.tablekitEditorData.restUrl;
23
24 /**
25 * REST nonce used to authenticate the table-blocks fetch, localized from PHP.
26 *
27 * @since Unknown
28 *
29 * @type {string}
30 */
31 var NONCE = window.tablekitEditorData.nonce;
32
33 /**
34 * Label used for the "render every block" option, localized from PHP.
35 *
36 * @since Unknown
37 *
38 * @type {string}
39 */
40 var ALL_LABEL = window.tablekitEditorData.allLabel;
41
42 /**
43 * Label shown in the picker while a table's blocks are being fetched, localized from PHP.
44 *
45 * @since Unknown
46 *
47 * @type {string}
48 */
49 var LOADING = window.tablekitEditorData.loading;
50
51 /**
52 * The Elementor widget name this script attaches its panel behavior to.
53 *
54 * @since Unknown
55 *
56 * @type {string}
57 */
58 var WIDGET = 'tablekit_table';
59
60 /**
61 * In-memory cache of fetched block lists, keyed by table ID.
62 *
63 * @since Unknown
64 *
65 * @type {Object}
66 */
67 var cache = {};
68
69 /**
70 * How long a cached block list stays valid, in milliseconds.
71 *
72 * @since Unknown
73 *
74 * @type {number}
75 */
76 var CACHE_TTL = 60000;
77
78 /**
79 * Fetches (and caches) the list of table blocks for a given table ID.
80 *
81 * @since Unknown
82 *
83 * @param {string} tableId Post ID of the table to fetch blocks for.
84 * @return {Promise<Array>} Resolves to an array of {index, label, block_name} descriptors,
85 * or an empty array on cache miss failure.
86 */
87 function fetchBlocks(tableId) {
88 var entry = cache[tableId];
89 if (entry && (Date.now() - entry.ts) < CACHE_TTL) {
90 return Promise.resolve(entry.data);
91 }
92 return fetch(REST_URL + '?table_id=' + encodeURIComponent(tableId), {
93 headers: { 'X-WP-Nonce': NONCE }
94 })
95 .then(function (res) { return res.json(); })
96 .then(function (data) {
97 var blocks = Array.isArray(data) ? data : [];
98 cache[tableId] = { data: blocks, ts: Date.now() };
99 return blocks;
100 })
101 .catch(function () { return []; });
102 }
103
104 /**
105 * Gets the currently open Elementor panel element.
106 *
107 * @since Unknown
108 *
109 * @return {Element|null} The panel element, or null if not open.
110 */
111 function getPanel() { return document.querySelector('.elementor-panel'); }
112
113 /**
114 * Gets the "block_index" control's select element within the open panel.
115 *
116 * @since Unknown
117 *
118 * @return {Element|null} The select element, or null if not found.
119 */
120 function getSelect() {
121 var p = getPanel();
122 return p ? p.querySelector('select[data-setting="block_index"]') : null;
123 }
124
125 /**
126 * Replaces the "block_index" select's options and dispatches a change
127 * event, guarding the model-sync listeners against reacting to it.
128 *
129 * @since Unknown
130 *
131 * @param {Object} options Option values keyed by block index (or "" for "All").
132 * @param {string} selectedValue Value to mark as selected.
133 * @return {void}
134 */
135 function populateSelect(options, selectedValue) {
136 var select = getSelect();
137 if (!select) { return; }
138 select.innerHTML = '';
139 Object.keys(options).forEach(function (val) {
140 var opt = document.createElement('option');
141 opt.value = val;
142 opt.textContent = options[val];
143 if (String(val) === String(selectedValue)) { opt.selected = true; }
144 select.appendChild(opt);
145 });
146 _settingInternally = true;
147 select.dispatchEvent(new Event('change', { bubbles: true }));
148 _settingInternally = false;
149 }
150
151 /**
152 * Hides the "block_index" control's row, after resetting it to "All".
153 *
154 * @since Unknown
155 *
156 * @return {void}
157 */
158 function hidePickerRow() {
159 var select = getSelect();
160 if (select) {
161 populateSelect({ '': ALL_LABEL }, '');
162 var row = select.closest('.elementor-control');
163 if (row) { row.style.display = 'none'; }
164 }
165 }
166
167 /**
168 * Shows the "block_index" control's row.
169 *
170 * @since Unknown
171 *
172 * @return {void}
173 */
174 function showPickerRow() {
175 var select = getSelect();
176 if (select) {
177 var row = select.closest('.elementor-control');
178 if (row) { row.style.display = ''; }
179 }
180 }
181
182 /**
183 * Whether the "block_index" control is currently being updated by this
184 * script (rather than by direct user interaction), used to prevent the
185 * change-event listeners below from reacting to their own updates.
186 *
187 * @since Unknown
188 *
189 * @type {boolean}
190 */
191 var _settingInternally = false;
192
193 /**
194 * Refreshes the "block_index" picker for a given table: resets the
195 * setting, shows/hides the row, and repopulates its options from the
196 * table's fetched block list.
197 *
198 * @since Unknown
199 *
200 * @param {Object} model Elementor widget settings model.
201 * @param {string} tableId Post ID of the selected table, or an empty string if none.
202 * @param {string} savedBlockIndex Previously saved "block_index" value to restore, if any.
203 * @return {void}
204 */
205 function updateBlockPicker(model, tableId, savedBlockIndex) {
206 _settingInternally = true;
207 model.setSetting('block_index', '');
208 _settingInternally = false;
209
210 if (!tableId) {
211 hidePickerRow();
212 return;
213 }
214
215 showPickerRow();
216 populateSelect({ '': LOADING }, '');
217
218 fetchBlocks(tableId).then(function (blocks) {
219 if (blocks.length < 2) {
220 hidePickerRow();
221 return;
222 }
223
224 var options = { '': ALL_LABEL };
225 blocks.forEach(function (block) {
226 options[String(block.index)] = block.label;
227 });
228
229 populateSelect(options, savedBlockIndex || '');
230 showPickerRow();
231 });
232 }
233
234 /**
235 * Waits for the "block_index" select to exist in the DOM (Elementor
236 * renders panel controls asynchronously) before invoking the callback.
237 * Gives up after 3 seconds.
238 *
239 * @since Unknown
240 *
241 * @param {Function} callback Called once the select is found, or after the timeout.
242 * @return {void}
243 */
244 function waitForSelect(callback) {
245 var panel = getPanel();
246 if (!panel) { callback(); return; }
247 if (getSelect()) { callback(); return; }
248
249 var observer = new MutationObserver(function () {
250 if (getSelect()) {
251 observer.disconnect();
252 callback();
253 }
254 });
255 observer.observe(panel, { childList: true, subtree: true });
256 setTimeout(function () { observer.disconnect(); }, 3000);
257 }
258
259 /**
260 * Sets up the block picker for the TableKit widget's panel: syncs it to
261 * the currently selected table on open, and wires up listeners that keep
262 * it in sync as "table_id"/"block_index" change afterward.
263 *
264 * @since Unknown
265 *
266 * @listens Elementor#panel/open_editor/widget/tablekit_table
267 *
268 * @param {Object} panel The opened widget panel view (unused; kept for hook signature).
269 * @param {Object} model Elementor widget settings model.
270 * @return {void}
271 */
272 window.elementor.hooks.addAction('panel/open_editor/widget/' + WIDGET, function (panel, model) {
273
274 waitForSelect(function () {
275 var initialId = model.getSetting('table_id');
276 if (Array.isArray(initialId)) { initialId = initialId[0] || ''; }
277 var savedBlock = model.getSetting('block_index') || '';
278 updateBlockPicker(model, String(initialId || ''), savedBlock);
279 });
280
281 model.off('change:settings', onSettingsChange);
282 model.on('change:settings', onSettingsChange);
283
284 jQuery(document).off('change.tablekit-table-picker');
285 jQuery(document).on(
286 'change.tablekit-table-picker',
287 '.elementor-panel select[data-setting="table_id"]',
288 function () {
289 var newId = jQuery(this).val() || '';
290 if (Array.isArray(newId)) { newId = newId[0] || ''; }
291 updateBlockPicker(model, String(newId), '');
292 }
293 );
294
295 jQuery(document).off('change.tablekit-block-picker');
296 jQuery(document).on(
297 'change.tablekit-block-picker',
298 '.elementor-panel select[data-setting="block_index"]',
299 function () {
300 if (_settingInternally) { return; }
301 var val = jQuery(this).val() || '';
302 _settingInternally = true;
303 model.setSetting('block_index', val);
304 _settingInternally = false;
305 }
306 );
307
308 /**
309 * Model "change:settings" handler: refreshes the block picker whenever
310 * the widget's "table_id" setting changes via a means other than the
311 * direct select-element listener above (e.g. dynamic tags, undo/redo).
312 *
313 * @since Unknown
314 *
315 * @listens Backbone.Model#change:settings
316 *
317 * @param {Object} changedModel The settings model that changed.
318 * @return {void}
319 */
320 function onSettingsChange(changedModel) {
321 if (_settingInternally) { return; }
322 var changed = changedModel.changed || {};
323 if (!Object.prototype.hasOwnProperty.call(changed, 'table_id')) { return; }
324 var newId = changed.table_id;
325 if (Array.isArray(newId)) { newId = newId[0] || ''; }
326 updateBlockPicker(model, String(newId || ''), '');
327 }
328 });
329
330 /**
331 * Tears down the table/block picker's jQuery change listeners when the
332 * Elementor panel closes, so they don't leak across widget panels.
333 *
334 * @since Unknown
335 *
336 * @listens Elementor#panel/close_editor
337 *
338 * @return {void}
339 */
340 window.elementor.hooks.addAction('panel/close_editor', function () {
341 jQuery(document).off('change.tablekit-table-picker');
342 jQuery(document).off('change.tablekit-block-picker');
343 });
344
345 })();