/* !include https://cdn.jsdelivr.net/npm/tom-select@2.6.1/dist/js/tom-select.popular.min.js */ function sqlpage_select_dropdown() { for (const s of document.querySelectorAll( "[data-pre-init=select-dropdown]", )) { try { sqlpage_select_dropdown_individual(s); } catch (e) { console.error(e); } } } /** * Initialize a select dropdown for a single element * @param {HTMLSelectElement} s - The select element to initialize */ function sqlpage_select_dropdown_individual(s) { s.removeAttribute("data-pre-init"); // See: https://github.com/orchidjs/tom-select/issues/716 // By default, TomSelect will not retain the focus if s is already focused // This is a workaround to fix that const is_focused = s === document.activeElement; const tom = new TomSelect(s, { load: sqlpage_load_options_source(s.dataset.options_source), valueField: "value", labelField: "label", searchField: "label", create: s.dataset.create_new, maxOptions: null, onItemAdd: function () { this.setTextboxValue(""); this.refreshOptions(); }, }); if (is_focused) tom.focus(); s.form?.addEventListener("reset", async () => { // The reset event is fired before the form is reset, so we need to wait for the next event loop await new Promise((resolve) => setTimeout(resolve, 0)); // Sync the options with the new reset value tom.sync(); // Wait for the options to be updated await new Promise((resolve) => setTimeout(resolve, 0)); // "sync" also focuses the input, so we need to blur it to remove the focus tom.blur(); tom.close(); }); } function sqlpage_load_options_source(options_source) { if (!options_source) return; return async (query, callback) => { const err = (label) => callback([{ label, value: "" }]); const resp = await fetch( `${options_source}?search=${encodeURIComponent(query)}`, ); if (!resp.ok) { return err( `Error loading options from "${options_source}": ${resp.status} ${resp.statusText}`, ); } const resp_type = resp.headers.get("content-type"); if (resp_type !== "application/json") { return err( `Invalid response type: ${resp_type} from "${options_source}". Make sure to use the 'json' component in the SQL file that generates the options.`, ); } const results = await resp.json(); if (!Array.isArray(results)) { return err( `Invalid response from "${options_source}". The response must be an array of objects with a 'label' and a 'value' property.`, ); } if (results.length === 1 && results[0].error) { return err(results[0].error); } if (results.length > 0) { const keys = Object.keys(results[0]); if ( keys.length !== 2 || !keys.includes("label") || !keys.includes("value") ) { return err( `Invalid response from "${options_source}". The response must be an array of objects with a 'label' and a 'value' property. Got: ${JSON.stringify(results[0])} in the first object instead.`, ); } } callback(results); }; } add_init_fn(sqlpage_select_dropdown);