~netlandish/links

7a103963bafc6670f027bc3d5435ec67f5c06ce6 — Yader Velasquez 9 months ago 3168558
Add new advancedsearch js
Support tag autcompletion for two inputs

References: https://todo.code.netlandish.com/~netlandish/links/47
M core/routes.go => core/routes.go +1 -0
@@ 1357,6 1357,7 @@ func (s *Service) OrgLinksList(c echo.Context) error {
	pd.Data["include_tags"] = lt.Translate("Include Tags")
	pd.Data["exclude_tags"] = lt.Translate("Exclude Tags")
	pd.Data["apply"] = lt.Translate("Apply")
	pd.Data["clear"] = lt.Translate("Clear")
	orgLinks := result.OrgLinks.Result
	gmap := gobwebs.Map{
		"pd":               pd,

M static/css/style.css => static/css/style.css +10 -0
@@ 427,6 427,16 @@ a.bullet-link:before {
    border-radius: 4px;
}

.autocomplete-tags {
    padding: 10px;
    z-index: 1000;
    position: absolute;
    background-color: #fff;
    width: 250px;
    border: 1px solid var(--color-lightGrey);
    border-radius: 4px;
}

.tag-suggested {
    margin-bottom: 5px;
    cursor: pointer;

A static/js/advancedsearch.js => static/js/advancedsearch.js +57 -0
@@ 0,0 1,57 @@
var url = document.querySelector('body').getAttribute('data-autocomplete');
var tagSelectors = document.querySelectorAll(".tag-selector");

function autocomplete() {
    tags = this.value.split(",")
    tag = tags[tags.length -1].trim()

    autocompleteTags = undefined;
    if (this.nextElementSibling && this.nextElementSibling.classList.contains("autocomplete-tags")) {
        autocompleteTags = this.nextElementSibling;
    }
    if (tag !== "") {
        fetch(url + "?q=" + tag)
            .then(function (response) {
                if (!response.ok) {
                    console.log("Network response was not ok");
                }
                return response.json();
            })
            .then(function (data) {
                if (data.length > 0) {
                    var t = "";
                    for (var i=0; i < data.length; i++) {
                        t += '<p class="tag-suggested">' + data[i].Name + '</p>';
                    }
                    if (autocompleteTags !== undefined) {
                        autocompleteTags.innerHTML = t;
                        autocompleteTags.classList.remove("d-none");
                    }
                }
            })
            .catch(function (error) {
                console.log("Fetch error:", error);
            });
    } else {
        autocompleteTags.classList.add("d-none");
    }

    tagSelector = this;
    autocompleteTags.addEventListener("click", function(event) {
        if (event.target.classList.contains("tag-suggested")) {
            newTag = event.target.textContent + ", ";
            if (tags.length > 1) {
                newTag = " " + newTag;
            }
            tags[tags.length - 1] = newTag;
            tagSelector.value = tags.join(",");
            autocompleteTags.classList.add("d-none");
            tagSelector.focus();
        }
    });
}

tagSelectors.forEach(function(tagSelector) {
    tagSelector.addEventListener('keyup', autocomplete);
});


M templates/base.html => templates/base.html +4 -1
@@ 12,7 12,7 @@
    <link rel="icon" type="image/png" sizes="16x16" href="{{ staticURL "img/favicon-16x16.png" }}?{{.serverVersion}}" />
    <link rel="apple-touch-icon" type="image/png" href="{{ staticURL "img/apple-touch-icon.png" }}?{{.serverVersion}}" />
  </head>
  <body class="container"{{if .useTagAutocomplete}} data-autocomplete="{{reverse "core:tag_autocomplete"}}"{{end}}>
  <body class="container"{{if or .useTagAutocomplete .advancedSearch}} data-autocomplete="{{reverse "core:tag_autocomplete"}}"{{end}}>
    <aside>
      <div>
        <h1>{{.base_pd.Title}}</h1>


@@ 217,5 217,8 @@
  {{if .useTagAutocomplete}}
    <script src="{{staticURL "js/autocomplete.js"}}?{{.serverVersion}}"></script>
  {{end}}
  {{if .advancedSearch}}
    <script src="{{staticURL "js/advancedsearch.js"}}?{{.serverVersion}}"></script>
  {{end}}
</html>
{{end}}

M templates/link_list.html => templates/link_list.html +7 -4
@@ 2,7 2,7 @@
<section class="app-header app-header-advanced-search">
  <h1 class="app-header__title">{{ if .isOrgLink }}{{ .org.Name }} {{.pd.Title}}{{else if not .isPopular}}{{.pd.Data.recent}}{{else}}{{.pd.Data.popular}}{{end}}</h1>
  <div>
      <form class="app-header__search app-header__search--stack" method="GET"
      <form class="app-header__search app-header__search--stack" method="GET" id="advanced-search"
        action="{{if .isPopular}}{{reverse "core:popular_link_list"}}{{else if .isOrgLink}}{{reverse "core:home_link_list"}}{{else}}{{reverse "core:recent_link_list"}}{{end}}">
          <div class="app-header__search app-header__search--inline">
              <input type="search" name="q" value="{{.search}}"/>


@@ 17,14 17,17 @@
                  <p><a href="#">{{.pd.Data.advanced_search}}</a></p>
                  <div>
                      <label>{{.pd.Data.include_tags}}</label>
                      <input type="text" name="tag" value="{{.tagFilter}}"/>
                      <input type="text" name="tag" value="{{.tagFilter}}" class="tag-selector" autocomplete="off"/>
                      <div class="d-none autocomplete-tags">Include</div>
                  </div>
                  <div>
                      <label>{{.pd.Data.exclude_tags}}</label>
                      <input type="text" name="exclude" value="{{.excludeTagFilter}}"/>
                      <input type="text" name="exclude" value="{{.excludeTagFilter}}" class="tag-selector" autocomplete="off"/>
                      <div class="d-none autocomplete-tags">Exclude</div>
                  </div>
                  <p class="mt-1 pull-right">
                    <button type="submit" class="button primary small is-small">{{.pd.Data.apply}}</button>
                    <a href="#" class="button primary is-small">{{.pd.Data.clear}}</a>
                    <button type="submit" class="button primary is-small">{{.pd.Data.apply}}</button>
                  </p>
              </div>
          {{end}}