repos / neovimcraft

website that makes it easy to find neovim plugins
git clone https://github.com/neurosnap/neovimcraft.git

neovimcraft / static
Kris · 09 Aug 23

client.js

  1function addUrlParam(key, value) {
  2  if (history.pushState) {
  3    const searchParams = new URLSearchParams(window.location.search);
  4    searchParams.set(key, value);
  5    const newurl =
  6      window.location.protocol +
  7      "//" +
  8      window.location.host +
  9      window.location.pathname +
 10      "?" +
 11      searchParams.toString();
 12    window.history.pushState({ path: newurl }, "", newurl);
 13  }
 14}
 15
 16function rmUrlParam(key) {
 17  const url = window.location.href;
 18  const r = new URL(url);
 19  r.searchParams.delete(key);
 20  const newUrl = r.href;
 21  window.history.pushState({ path: newUrl }, "", newUrl);
 22}
 23
 24function getUrlParam(key) {
 25  const url = window.location.href;
 26  const r = new URL(url);
 27  return r.searchParams.get(key);
 28}
 29
 30function createFilter(pluginsEl) {
 31  function filter(value) {
 32    function each(el) {
 33      if (!value) {
 34        el.classList.remove("hidden");
 35        return;
 36      }
 37      const search = value.toLocaleLowerCase();
 38      const username = el.dataset.username;
 39      const repo = el.dataset.repo;
 40      const desc = el.dataset.desc || "";
 41      const tags = el.dataset.tags.split(",");
 42
 43      let hasTags = false;
 44      if (search.includes("tag:")) {
 45        const nextSearch = search.replace("tag:", "");
 46        hasTags = tags.some((tag) => tag === nextSearch);
 47      }
 48
 49      const showEl = username.includes(search)
 50        || repo.includes(search)
 51        || desc.includes(search)
 52        || hasTags;
 53
 54      if (showEl) {
 55        el.classList.remove("hidden");
 56      } else {
 57        el.classList.add("hidden");
 58      }
 59    }
 60
 61    pluginsEl.forEach(each);
 62  }
 63
 64  return filter;
 65}
 66
 67function updateLinks(search) {
 68  const links = must("#sort_links");
 69  for (let link of links.children) {
 70    const r = new URL(link.href);
 71    if (search) {
 72      r.searchParams.set("search", search);
 73    } else {
 74      r.searchParams.delete("search");
 75    }
 76    const newUrl = r.href;
 77    link.href = newUrl;
 78  }
 79}
 80
 81function must(pattern) {
 82  const el = document.querySelector(pattern);
 83  if (!el) {
 84    throw new Error(`${pattern} not found`);
 85  }
 86  return el;
 87}
 88
 89function mustAll(pattern) {
 90  const el = document.querySelectorAll(pattern);
 91  if (!el) {
 92    throw new Error(`${pattern} not found`);
 93  }
 94  return el;
 95}
 96
 97function debounce(fn, delay) {
 98  let timeoutID;
 99  return function(...args) {
100    if (timeoutID) {
101      clearTimeout(timeoutID);
102    }
103    timeoutID = setTimeout(() => {
104      fn(...args);
105    }, delay);
106  };
107}
108
109function init() {
110  const searchEl = must("#search");
111  const clearSearchEl = must("#search_clear");
112  const pluginsEl = mustAll(".plugin");
113  const tagsEl = mustAll(".tag");
114
115  const urlParam = getUrlParam("search");
116  searchEl.value = urlParam;
117  const filter = createFilter(pluginsEl);
118
119  const search = (value) => {
120    if (value) {
121      addUrlParam("search", value);
122      updateLinks(value);
123    } else {
124      rmUrlParam("search");
125      updateLinks("");
126    }
127
128    filter(value);
129  };
130
131  const debouncedSearch = debounce((ev) => {
132    const value = ev.target.value;
133    search(value);
134  }, 150);
135  
136  searchEl.addEventListener("input", debouncedSearch);
137
138
139  clearSearchEl.addEventListener("click", () => {
140    searchEl.value = "";
141    rmUrlParam("search");
142    updateLinks("");
143    filter("");
144  });
145
146  tagsEl.forEach((tagEl) => {
147    tagEl.addEventListener("click", (el) => {
148      const id = el.target.dataset.id;
149      if (!id) return;
150      const value = `tag:${id}`;
151      searchEl.value = value;
152      search(value);
153    });
154  });
155  filter(searchEl.value);
156}
157
158document.addEventListener("DOMContentLoaded", init);