diff options
Diffstat (limited to 'html/item_list.js')
-rw-r--r-- | html/item_list.js | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/html/item_list.js b/html/item_list.js new file mode 100644 index 0000000..f6b9bd3 --- /dev/null +++ b/html/item_list.js @@ -0,0 +1,189 @@ +/** + * This file is part of Haketilo. + * + * Function: Showing a list of resources/mappings in a browser. + * + * Copyright (C) 2022 Wojtek Kosior + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * As additional permission under GNU GPL version 3 section 7, you + * may distribute forms of that code without the copy of the GNU + * GPL normally required by section 4, provided you include this + * license notice and, in case of non-source distribution, a URL + * through which recipients can access the Corresponding Source. + * If you modify file(s) with this exception, you may extend this + * exception to your version of the file(s), but you are not + * obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * As a special exception to the GPL, any HTML file which merely + * makes function calls to this code, and for that purpose + * includes it by reference shall be deemed a separate work for + * copyright law purposes. If you modify this code, you may extend + * this exception to your version of the code, but you are not + * obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * I, Wojtek Kosior, thereby promise not to sue for violation of this file's + * license. Although I request that you do not make use of this code in a + * proprietary program, I am not going to enforce this in court. + */ + +#IMPORT common/indexeddb.js AS haketilodb +#IMPORT html/dialog.js + +#FROM html/item_preview.js IMPORT resource_preview, mapping_preview +#FROM html/DOM_helpers.js IMPORT clone_template + +function preview_item(list_ctx, item, ignore_dialog=false) +{ + if (list_ctx.dialog_ctx.shown && !ignore_dialog) + return; + + list_ctx.preview_ctx = + list_ctx.preview_cb(item.definition, list_ctx.preview_ctx); + list_ctx.preview_container + .prepend(list_ctx.preview_ctx.main_div); + + if (list_ctx.previewed_item !== null) + list_ctx.previewed_item.li.classList.remove("item_li_highlight"); + + list_ctx.previewed_item = item; + item.li.classList.add("item_li_highlight"); + + list_ctx.preview_container.classList.remove("hide"); +} + +function insert_item(list_ctx, definition, idx) +{ + const li = document.createElement("li"); + li.innerText = definition.long_name; + if (idx) + list_ctx.items[idx - 1].li.after(li); + else + list_ctx.ul.prepend(li); + + const item = {definition, li}; + list_ctx.items.splice(idx, 0, item); + list_ctx.by_identifier.set(definition.identifier, item); + + li.addEventListener("click", () => preview_item(list_ctx, item)); + + return item; +} + +const coll = new Intl.Collator(); + +function item_cmp(def1, def2) +{ + return coll.compare(def1.long_name, def2.long_name) || + coll.compare(def1.identifier, def2.identifier); +} + +function find_item_idx(definition) +{ + /* Perform a binary search of item's (new or not) index in sorted array. */ + let left = 0, right = list_ctx.items.length; + + while (left < right) { + const mid = (left + right) >> 1; + if (item_cmp(definition, list_ctx.items[mid].definition) > 0) + left = mid + 1; + else /* <= 0 */ + right = mid; + } + + return left; +} + +function item_changed(list_ctx, change) +{ + + /* Remove item. */ + const old_item = list_ctx.by_identifier.get(change.key); + if (old_item !== undefined) { + list_ctx.items.splice(find_item_idx(old_item.definition), 1); + list_ctx.by_identifier.delete(change.key); + old_item.li.remove(); + + if (list_ctx.previewed_item === old_item) { + list_ctx.preview_container.classList.add("hide"); + list_ctx.previewed_item = null; + } + } + + if (change.new_val === undefined) + return; + + const new_item = insert_item(list_ctx, change.new_val, + find_item_idx(change.new_val)); + if (list_ctx.previewed_item === old_item) + preview_item(list_ctx, new_item, true); +} + +async function item_list(preview_cb, track_cb) +{ + const list_ctx = clone_template("item_list"); + + const [tracking, definitions] = + await track_cb(ch => item_changed(list_ctx, ch)); + + definitions.sort(item_cmp); + + Object.assign(list_ctx, { + items: [], + by_identifier: new Map(), + tracking, + previewed_item: null, + preview_cb, + dialog_ctx: dialog.make(() => on_dialog_show(list_ctx), + () => on_dialog_hide(list_ctx)) + }); + list_ctx.dialog_container.append(list_ctx.dialog_ctx.main_div); + + for (const def of definitions) + insert_item(list_ctx, def, list_ctx.items.length); + + return list_ctx; +} + +function on_dialog_show(list_ctx) +{ + list_ctx.ul; + list_ctx.preview_container.classList.add("hide"); + list_ctx.dialog_container.classList.remove("hide"); +} + +function on_dialog_hide(list_ctx) +{ + list_ctx.ul; + list_ctx.preview_container.classList.remove("hide"); + list_ctx.dialog_container.classList.add("hide"); +} + +const resource_list = + () => item_list(resource_preview, haketilodb.track.resources); +#EXPORT resource_list + +const mapping_list = + () => item_list(mapping_preview, haketilodb.track.mappings); +#EXPORT mapping_list + +function destroy_list(list_ctx) +{ + haketilodb.untrack(list_ctx.tracking); + list_ctx.main_div.remove(); +} +#EXPORT destroy_list |