mirror of
https://github.com/Findus23/RPGnotes.git
synced 2024-09-19 15:43:45 +02:00
autocomplete
This commit is contained in:
parent
24d6e6dd24
commit
fb3878ad94
11 changed files with 156 additions and 9 deletions
|
@ -30,6 +30,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{{ super() }}
|
||||
{% if "Safari/" not in request.META.get('HTTP_USER_AGENT', '') %}
|
||||
<script src="{{ static("libs/easymde.min.js") }}"></script>
|
||||
<script src="{{ static("libs/fontawesome-solid.min.js") }}"></script>
|
||||
|
|
11
package-lock.json
generated
11
package-lock.json
generated
|
@ -7,6 +7,7 @@
|
|||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"@sentry/browser": "^6.12.0",
|
||||
"@trevoreyre/autocomplete-js": "^2.2.0",
|
||||
"bootstrap": "^5.1.0",
|
||||
"easymde": "^2.15.0",
|
||||
"luminous-lightbox": "^2.3.5"
|
||||
|
@ -106,6 +107,11 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@trevoreyre/autocomplete-js": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@trevoreyre/autocomplete-js/-/autocomplete-js-2.2.0.tgz",
|
||||
"integrity": "sha512-emHJWZBPWdB5iDW9MrLSfq3lopyDlIhYXa8ttnCX9kQp1g+G0Lmfu/v6fW2aggjAfsZX8ksuZSG65o+EdwoN0g=="
|
||||
},
|
||||
"node_modules/@types/codemirror": {
|
||||
"version": "5.60.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz",
|
||||
|
@ -265,6 +271,11 @@
|
|||
"tslib": "^1.9.3"
|
||||
}
|
||||
},
|
||||
"@trevoreyre/autocomplete-js": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@trevoreyre/autocomplete-js/-/autocomplete-js-2.2.0.tgz",
|
||||
"integrity": "sha512-emHJWZBPWdB5iDW9MrLSfq3lopyDlIhYXa8ttnCX9kQp1g+G0Lmfu/v6fW2aggjAfsZX8ksuZSG65o+EdwoN0g=="
|
||||
},
|
||||
"@types/codemirror": {
|
||||
"version": "5.60.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.5.tgz",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.4",
|
||||
"@sentry/browser": "^6.12.0",
|
||||
"@trevoreyre/autocomplete-js": "^2.2.0",
|
||||
"bootstrap": "^5.1.0",
|
||||
"easymde": "^2.15.0",
|
||||
"luminous-lightbox": "^2.3.5"
|
||||
|
|
|
@ -4,5 +4,6 @@ from search import views
|
|||
|
||||
urlpatterns = [
|
||||
path("", views.SearchResultsView.as_view(), name="search"),
|
||||
path("autocomplete/", views.autocomplete, name="autocomplete"),
|
||||
|
||||
]
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# Create your views here.
|
||||
from itertools import chain
|
||||
|
||||
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank, SearchHeadline, TrigramWordSimilarity
|
||||
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank, SearchHeadline, TrigramWordDistance
|
||||
from django.http import JsonResponse
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from campaigns.models import Campaign
|
||||
|
@ -12,6 +13,8 @@ from locations.models import Location
|
|||
from loot.models import Loot
|
||||
from notes.models import Note
|
||||
|
||||
search_models = [Location, Character, Faction, IngameDay, Note, Loot]
|
||||
|
||||
|
||||
class SearchResultsView(TemplateView):
|
||||
template_name = "search/search_results.jinja"
|
||||
|
@ -27,16 +30,15 @@ class SearchResultsView(TemplateView):
|
|||
name_vector = SearchVector('name', weight="A", config=config)
|
||||
description_vector = SearchVector("description_html", weight="B", config=config)
|
||||
query = SearchQuery(query_string, search_type='websearch', config=config)
|
||||
models = [Location, Character, Faction, IngameDay, Note, Loot]
|
||||
all_results = []
|
||||
all_similar = []
|
||||
for m in models:
|
||||
for m in search_models:
|
||||
if m == IngameDay:
|
||||
vector = description_vector
|
||||
else:
|
||||
vector = description_vector + name_vector
|
||||
similar = m.objects.annotate(
|
||||
distance=TrigramWordSimilarity(query_string, "name")
|
||||
distance=TrigramWordDistance(query_string, "name")
|
||||
# distance=TrigramDistance("name", query_string)
|
||||
).filter(name__trigram_word_similar=query_string).order_by('distance')
|
||||
all_similar.extend(list(similar))
|
||||
|
@ -58,3 +60,29 @@ class SearchResultsView(TemplateView):
|
|||
context["similars"] = all_similar
|
||||
context["query"] = query_string
|
||||
return context
|
||||
|
||||
|
||||
def autocomplete(request):
|
||||
if "q" not in request.GET:
|
||||
return ""
|
||||
query_string = request.GET['q']
|
||||
all_similar = []
|
||||
|
||||
for m in search_models:
|
||||
if m == IngameDay:
|
||||
continue
|
||||
similar = m.objects.annotate(
|
||||
distance=TrigramWordDistance(query_string, "name")
|
||||
# distance=TrigramDistance("name", query_string)
|
||||
).order_by('distance')
|
||||
similar = [s for s in similar if s.distance < 0.5]
|
||||
all_similar.extend(list(similar))
|
||||
all_similar.sort(key=lambda s: s.distance)
|
||||
data = []
|
||||
for s in all_similar:
|
||||
data.append({
|
||||
"url": s.get_absolute_url(),
|
||||
"name": s.name,
|
||||
"distance": s.distance
|
||||
})
|
||||
return JsonResponse(data, safe=False)
|
||||
|
|
25
static/js/autocomplete.js
Normal file
25
static/js/autocomplete.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
new Autocomplete('#autocomplete', {
|
||||
search: input => {
|
||||
const url = `/search/autocomplete/?q=${encodeURI(input)}`
|
||||
|
||||
return new Promise(resolve => {
|
||||
if (input.length === 0) {
|
||||
return resolve([])
|
||||
}
|
||||
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
resolve(data)
|
||||
})
|
||||
})
|
||||
},
|
||||
getResultValue: result => result.name,
|
||||
onSubmit: result => {
|
||||
if (!result) {
|
||||
return
|
||||
}
|
||||
location.href = result.url
|
||||
}
|
||||
|
||||
})
|
1
static/libs/autocomplete.min.js
vendored
Symbolic link
1
static/libs/autocomplete.min.js
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../node_modules/@trevoreyre/autocomplete-js/dist/autocomplete.min.js
|
71
static/scss/_autocomple.scss
Normal file
71
static/scss/_autocomple.scss
Normal file
|
@ -0,0 +1,71 @@
|
|||
// based on https://github.com/trevoreyre/autocomplete/blob/4caf5f8107365c268a0543c652f6ad44a91fe488/packages/style.css
|
||||
|
||||
[data-position="below"] .autocomplete-input[aria-expanded="true"] {
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
|
||||
[data-position="above"] .autocomplete-input[aria-expanded="true"] {
|
||||
border-top-color: transparent;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Loading spinner */
|
||||
.autocomplete[data-loading="true"]::after {
|
||||
content: "";
|
||||
border: 3px solid rgba(0, 0, 0, 0.12);
|
||||
border-right: 3px solid rgba(0, 0, 0, 0.48);
|
||||
border-radius: 100%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
animation: rotate 1s infinite linear;
|
||||
}
|
||||
|
||||
.autocomplete-result-list {
|
||||
margin: 0;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
max-height: 296px;
|
||||
overflow-y: auto;
|
||||
background: #fff;
|
||||
list-style: none;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.16);
|
||||
}
|
||||
|
||||
[data-position="below"] .autocomplete-result-list {
|
||||
margin-top: -1px;
|
||||
border-top-color: transparent;
|
||||
border-radius: 0 0 8px 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
[data-position="above"] .autocomplete-result-list {
|
||||
margin-bottom: -1px;
|
||||
border-bottom-color: transparent;
|
||||
border-radius: 8px 8px 0 0;
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
/* Single result item */
|
||||
.autocomplete-result {
|
||||
cursor: default;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.autocomplete-result:hover,
|
||||
.autocomplete-result[aria-selected="true"] {
|
||||
background-color: rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: translateY(-50%) rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: translateY(-50%) rotate(359deg);
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
@import "node_modules/bootstrap/scss/bootstrap";
|
||||
@import "node_modules/easymde/dist/easymde.min";
|
||||
@import "node_modules/luminous-lightbox/dist/luminous-basic";
|
||||
@import "autocomple";
|
||||
|
||||
@import "misc";
|
||||
@import "avatar";
|
||||
|
|
|
@ -48,7 +48,6 @@
|
|||
<script nonce="{{ request.csp_nonce }}">
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const galleries = document.querySelectorAll("a.image-viewer");
|
||||
console.info(galleries)
|
||||
new LuminousGallery(galleries)
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -45,9 +45,12 @@
|
|||
</ul>
|
||||
<form class="d-flex" action="{{ url("search") }}">
|
||||
<div class="input-group">
|
||||
<input class="form-control" name="q" type="search"
|
||||
<div id="autocomplete" class="autocomplete">
|
||||
<input class="form-control autocomplete-input" name="q" type="search"
|
||||
placeholder="{% trans %}Search{% endtrans %}"
|
||||
aria-label="{% trans %}Search{% endtrans %}">
|
||||
<ul class="autocomplete-result-list"></ul>
|
||||
</div>
|
||||
<button class="btn btn-outline-secondary" type="submit">
|
||||
{% trans %}Go!{% endtrans %}
|
||||
</button>
|
||||
|
@ -83,3 +86,8 @@
|
|||
{% block content %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="/static/libs/autocomplete.min.js"></script>
|
||||
<script src="/static/js/autocomplete.js"></script>
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue