소스 검색

deploy: fbf235106a89690f503a0bc3db7ff08b3e9dcbe1

jefftriplett 2 일 전
부모
커밋
2d863a89b9
4개의 변경된 파일316개의 추가작업 그리고 2개의 파일을 삭제
  1. 2 0
      404.html
  2. 2 0
      index.html
  3. 189 0
      javascripts/directory.js
  4. 123 2
      stylesheets/django.css

+ 2 - 0
404.html

@@ -370,6 +370,8 @@
       
         <script src="/javascripts/anchor-compat.js"></script>
       
+        <script src="/javascripts/directory.js"></script>
+      
     
   </body>
 </html>

+ 2 - 0
index.html

@@ -1735,6 +1735,8 @@ or buying <a rel="sponsored" href="https://django.threadless.com/">official merc
       
         <script src="./javascripts/anchor-compat.js"></script>
       
+        <script src="./javascripts/directory.js"></script>
+      
     
   </body>
 </html>

+ 189 - 0
javascripts/directory.js

@@ -0,0 +1,189 @@
+/* Enhance the shared README without changing its GitHub presentation. */
+(function () {
+  function enhanceDirectory() {
+    var article = document.querySelector(".md-content__inner");
+    if (!article || article.dataset.directoryReady) return;
+    var contents = article.querySelector("#contents");
+    if (!contents) return;
+    article.dataset.directoryReady = "true";
+
+    var toc = contents.nextElementSibling;
+    var firstHeading = toc && toc.nextElementSibling;
+    while (firstHeading && firstHeading.tagName !== "H2") {
+      firstHeading = firstHeading.nextElementSibling;
+    }
+    if (!firstHeading) return;
+
+    var controls = document.createElement("div");
+    controls.className = "awesome-django-controls";
+    controls.id = "contents";
+    controls.innerHTML =
+      '<div class="awesome-django-filter"><label for="directory-filter">Filter packages and resources</label>' +
+    '<div class="awesome-django-filter__input"><input id="directory-filter" type="search" placeholder="Search names and descriptions…" aria-controls="directory-results">' +
+    '<button type="button" id="directory-clear">Clear</button></div></div>' +
+    '<div class="awesome-django-jump"><label for="directory-category">Jump to category</label>' +
+    '<select id="directory-category"><option value="">Choose a category…</option></select></div>' +
+    '<p class="awesome-django-result-count" role="status" aria-live="polite" aria-atomic="true"></p>';
+    contents.replaceWith(controls);
+    if (toc && toc.tagName === "UL") toc.remove();
+    new ResizeObserver(function () {
+      article.style.setProperty("--directory-controls-height", controls.getBoundingClientRect().height + "px");
+    }).observe(controls);
+
+    var contribution = document.createElement("p");
+    contribution.className = "awesome-django-contribute";
+    var link = document.createElement("a");
+    link.href = "https://github.com/wsvincent/awesome-django/blob/main/.github/pull_request_template.md";
+    link.textContent = "Suggest a project";
+    contribution.append(link, " — read the submission criteria before opening a pull request.");
+    article.querySelector("blockquote").after(contribution);
+    article.querySelectorAll(":scope > p").forEach(function (p) {
+      if (!p.textContent.trim() && p.querySelector("br")) p.classList.add("awesome-django-spacer");
+    });
+
+    var results = document.createElement("div");
+    results.id = "directory-results";
+    firstHeading.before(results);
+    var group;
+    var section;
+    var node = firstHeading;
+    while (node) {
+      var next = node.nextSibling;
+      if (node.nodeType === 1 && node.tagName === "H2") {
+        group = document.createElement("section");
+        group.className = "awesome-django-group";
+        group.setAttribute("aria-labelledby", node.id);
+        results.append(group);
+        section = group;
+      } else if (node.nodeType === 1 && node.tagName === "H3") {
+        section = document.createElement("section");
+        section.className = "awesome-django-category";
+        section.setAttribute("aria-labelledby", node.id);
+        group.append(section);
+      }
+      section.append(node);
+      node = next;
+    }
+
+    var entries = Array.from(results.querySelectorAll("li"));
+    var categories = Array.from(results.querySelectorAll("section"));
+    var select = controls.querySelector("select");
+    var counts = {};
+    results.querySelectorAll("h2, h3").forEach(function (heading) {
+      var label = heading.cloneNode(true);
+      label.querySelectorAll(".headerlink").forEach(function (a) { a.remove(); });
+      var count = heading.parentElement.querySelectorAll("li").length;
+      counts[heading.id] = count;
+      var option = document.createElement("option");
+      option.value = heading.id;
+      option.textContent = (heading.tagName === "H3" ? " " : "") + label.textContent.trim() + " (" + count + ")";
+      select.append(option);
+    });
+
+    var input = controls.querySelector("input");
+    var status = controls.querySelector("[role=status]");
+    var empty = document.createElement("p");
+    empty.textContent = "No matching entries. Try another term or clear the filter.";
+    empty.hidden = true;
+    results.after(empty);
+    function filter() {
+      var terms = input.value.trim().toLowerCase().split(/\s+/).filter(Boolean);
+      var visible = 0;
+      entries.forEach(function (entry) {
+        var text = entry.textContent.toLowerCase();
+        entry.hidden = !terms.every(function (term) { return text.includes(term); });
+        if (!entry.hidden) visible++;
+      });
+      categories.forEach(function (category) {
+        category.hidden = !Array.from(category.querySelectorAll("li")).some(function (entry) { return !entry.hidden; });
+      });
+      status.textContent = terms.length ? visible + " of " + entries.length + " entries match" : entries.length + " packages and resources";
+      empty.hidden = visible !== 0;
+    }
+    input.addEventListener("input", filter);
+    controls.querySelector("button").addEventListener("click", function () {
+      input.value = "";
+      filter();
+      input.focus();
+    });
+    select.addEventListener("change", function () {
+      if (!select.value) return;
+      input.value = "";
+      filter();
+      var target = document.getElementById(select.value);
+      window.location.hash = select.value;
+      target.setAttribute("tabindex", "-1");
+      target.focus({ preventScroll: true });
+      target.scrollIntoView();
+    });
+
+    var disclosures = [];
+    document.querySelectorAll('[data-md-component="toc"] a.md-nav__link').forEach(function (a) {
+      var id = a.hash.slice(1);
+      if (id === "contents") {
+        a.closest("li").remove();
+        return;
+      }
+      if (counts[id] === undefined) return;
+      var badge = document.createElement("span");
+      badge.className = "awesome-django-nav-count";
+      badge.textContent = counts[id];
+      badge.setAttribute("aria-label", counts[id] + " entries");
+      a.append(badge);
+      var nav = a.parentElement.querySelector(":scope > nav");
+      if (!nav) return;
+      var button = document.createElement("button");
+      button.type = "button";
+      button.className = "awesome-django-nav-toggle";
+      button.textContent = "⌄";
+      button.setAttribute("aria-label", "Toggle " + nav.getAttribute("aria-label") + " categories");
+      nav.id = "directory-nav-" + id;
+      button.setAttribute("aria-controls", nav.id);
+      function setExpanded(expanded) {
+        nav.hidden = !expanded;
+        button.setAttribute("aria-expanded", String(expanded));
+      }
+      setExpanded(id === "third-party-packages");
+      button.addEventListener("click", function () { setExpanded(nav.hidden); });
+      a.after(button);
+      a.parentElement.classList.add("awesome-django-nav-group");
+      disclosures.push({ nav: nav, expand: function () { setExpanded(true); } });
+    });
+    function revealAnchor() {
+      var id;
+      try { id = decodeURIComponent(window.location.hash.slice(1)); } catch (_) { return; }
+      var target = document.getElementById(id);
+      if (target && results.contains(target) && input.value) {
+        input.value = "";
+        filter();
+      }
+      disclosures.forEach(function (item) {
+        if (Array.from(item.nav.querySelectorAll("a")).some(function (a) { return a.hash === "#" + id; })) item.expand();
+      });
+    }
+    window.addEventListener("hashchange", revealAnchor);
+    filter();
+    revealAnchor();
+
+    var picture = article.querySelector("picture");
+    if (picture) {
+      var logo = picture.querySelector("img");
+      var source = picture.querySelector("source");
+      var light = logo.getAttribute("src");
+      var dark = source.getAttribute("srcset");
+      source.remove();
+      logo.alt = "Django";
+      function syncLogo() {
+        var scheme = document.querySelector("[data-md-color-scheme]");
+        logo.src = scheme && scheme.getAttribute("data-md-color-scheme") === "slate" ? dark : light;
+      }
+      syncLogo();
+      new MutationObserver(syncLogo).observe(document.documentElement, {
+        attributes: true, subtree: true, attributeFilter: ["data-md-color-scheme"],
+      });
+    }
+  }
+  if (typeof document$ !== "undefined") document$.subscribe(enhanceDirectory);
+  else if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", enhanceDirectory);
+  else enhanceDirectory();
+})();

+ 123 - 2
stylesheets/django.css

@@ -24,7 +24,7 @@
   --md-accent-fg-color: var(--django-green-mid);
   --md-accent-fg-color--transparent: rgba(32, 170, 118, 0.1);
 
-  --md-typeset-a-color: var(--django-green-mid);
+  --md-typeset-a-color: var(--django-green-dark);
   --md-code-bg-color: var(--django-cream);
 }
 
@@ -59,7 +59,7 @@
 
 /* The Django logo is a wide SVG. Keep it from overflowing on small screens. */
 .md-typeset picture img {
-  max-width: 320px;
+  max-width: min(160px, 100%);
   height: auto;
 }
 
@@ -135,3 +135,124 @@
   margin: 0 auto;
   max-width: 100%;
 }
+
+/* Directory controls enhance the website; the README keeps its full TOC. */
+.awesome-django-spacer,
+.awesome-django-group[hidden],
+.awesome-django-category[hidden],
+#directory-results [hidden],
+.awesome-django-nav-group > nav[hidden] {
+  display: none !important;
+}
+
+.awesome-django-controls {
+  display: grid;
+  grid-template-columns: 3fr 2fr;
+  gap: 0.5rem 1rem;
+  padding: 0.8rem;
+  margin: 1rem 0;
+  border: 1px solid var(--md-default-fg-color--lighter);
+  border-radius: 0.3rem;
+  background: var(--md-default-bg-color);
+}
+
+.awesome-django-controls label {
+  display: block;
+  margin-bottom: 0.3rem;
+  font-size: 0.7rem;
+  font-weight: 700;
+}
+
+.awesome-django-filter,
+.awesome-django-jump {
+  min-width: 0;
+}
+
+.awesome-django-filter__input {
+  display: flex;
+  gap: 0.4rem;
+}
+
+.awesome-django-controls input,
+.awesome-django-controls select,
+.awesome-django-controls button {
+  min-height: 2.2rem;
+  border: 1px solid var(--md-default-fg-color--light);
+  border-radius: 0.2rem;
+  padding: 0.4rem;
+  font: inherit;
+  font-size: 0.8rem;
+  color: var(--md-default-fg-color);
+  background: var(--md-default-bg-color);
+}
+
+.awesome-django-controls input,
+.awesome-django-controls select {
+  width: 100%;
+  min-width: 0;
+}
+
+.awesome-django-controls button,
+.awesome-django-nav-toggle {
+  cursor: pointer;
+}
+
+.awesome-django-controls :focus-visible,
+.awesome-django-nav-toggle:focus-visible {
+  outline: 2px solid var(--md-typeset-a-color);
+  outline-offset: 2px;
+}
+
+.md-typeset .awesome-django-result-count {
+  grid-column: 1 / -1;
+  margin: 0;
+  font-size: 0.7rem;
+}
+
+.awesome-django-nav-count {
+  margin-left: auto;
+  padding-left: 0.4rem;
+  font-size: 0.6rem;
+  font-variant-numeric: tabular-nums;
+  color: var(--md-default-fg-color--light);
+}
+
+.awesome-django-nav-group {
+  position: relative;
+}
+
+.awesome-django-nav-group > a {
+  padding-right: 1.6rem;
+}
+
+.awesome-django-nav-toggle {
+  position: absolute;
+  top: 0;
+  right: 0;
+  width: 1.6rem;
+  height: 1.6rem;
+  color: var(--md-default-fg-color);
+}
+
+.awesome-django-nav-toggle[aria-expanded="false"] {
+  transform: rotate(-90deg);
+}
+
+#directory-results h2,
+#directory-results h3 {
+  scroll-margin-top: 7rem;
+}
+
+@media (max-width: 76.234375em) {
+  .awesome-django-controls {
+    position: sticky;
+    top: 2.8rem;
+    z-index: 2;
+    grid-template-columns: 1fr;
+  }
+
+  #directory-results h2,
+  #directory-results h3 {
+    scroll-margin-top: calc(var(--directory-controls-height, 14rem) + 4rem);
+  }
+}