aboutsummaryrefslogtreecommitdiff
path: root/html/item_list.js
diff options
context:
space:
mode:
Diffstat (limited to 'html/item_list.js')
-rw-r--r--html/item_list.js189
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