Fix several issues around browser back navigation (#825)

This commit is contained in:
Sascha Ißbrücker 2024-09-15 08:28:49 +02:00 committed by GitHub
parent 74e65bc366
commit db225d5267
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 64 additions and 23 deletions

View file

@ -4,8 +4,9 @@ class BulkEdit extends Behavior {
constructor(element) { constructor(element) {
super(element); super(element);
this.active = false; this.active = element.classList.contains("active");
this.init = this.init.bind(this);
this.onToggleActive = this.onToggleActive.bind(this); this.onToggleActive = this.onToggleActive.bind(this);
this.onToggleAll = this.onToggleAll.bind(this); this.onToggleAll = this.onToggleAll.bind(this);
this.onToggleBookmark = this.onToggleBookmark.bind(this); this.onToggleBookmark = this.onToggleBookmark.bind(this);
@ -13,7 +14,11 @@ class BulkEdit extends Behavior {
this.init(); this.init();
// Reset when bookmarks are refreshed // Reset when bookmarks are refreshed
document.addEventListener("refresh-bookmark-list-done", () => this.init()); document.addEventListener("refresh-bookmark-list-done", this.init);
}
destroy() {
document.removeEventListener("refresh-bookmark-list-done", this.init);
} }
init() { init() {

View file

@ -13,7 +13,10 @@ class ConfirmButtonBehavior extends Behavior {
} }
destroy() { destroy() {
Behavior.interacting = false; this.reset();
this.element.setAttribute("type", this.element.dataset.type);
this.element.setAttribute("name", this.element.dataset.name);
this.element.setAttribute("value", this.element.dataset.value);
} }
onClick(event) { onClick(event) {
@ -70,7 +73,10 @@ class ConfirmButtonBehavior extends Behavior {
reset() { reset() {
setTimeout(() => { setTimeout(() => {
Behavior.interacting = false; Behavior.interacting = false;
this.container.remove(); if (this.container) {
this.container.remove();
this.container = null;
}
this.element.classList.remove("d-none"); this.element.classList.remove("d-none");
}); });
} }

View file

@ -16,6 +16,10 @@ class DropdownBehavior extends Behavior {
}); });
} }
destroy() {
this.close();
}
open() { open() {
this.element.classList.add("active"); this.element.classList.add("active");
document.addEventListener("click", this.onOutsideClick); document.addEventListener("click", this.onOutsideClick);

View file

@ -4,7 +4,12 @@ class GlobalShortcuts extends Behavior {
constructor(element) { constructor(element) {
super(element); super(element);
document.addEventListener("keydown", this.onKeyDown.bind(this)); this.onKeyDown = this.onKeyDown.bind(this);
document.addEventListener("keydown", this.onKeyDown);
}
destroy() {
document.removeEventListener("keydown", this.onKeyDown);
} }
onKeyDown(event) { onKeyDown(event) {

View file

@ -16,7 +16,7 @@ const mutationObserver = new MutationObserver((mutations) => {
}); });
}); });
window.addEventListener("turbo:load", () => { document.addEventListener("turbo:load", () => {
mutationObserver.observe(document.body, { mutationObserver.observe(document.body, {
childList: true, childList: true,
subtree: true, subtree: true,
@ -25,6 +25,10 @@ window.addEventListener("turbo:load", () => {
applyBehaviors(document.body); applyBehaviors(document.body);
}); });
document.addEventListener("turbo:before-cache", () => {
destroyBehaviors(document.body);
});
export class Behavior { export class Behavior {
constructor(element) { constructor(element) {
this.element = element; this.element = element;

View file

@ -5,23 +5,35 @@ import { ApiClient } from "../api";
class TagAutocomplete extends Behavior { class TagAutocomplete extends Behavior {
constructor(element) { constructor(element) {
super(element); super(element);
const wrapper = document.createElement("div"); const input = element.querySelector("input");
if (!input) {
console.warning("TagAutocomplete: input element not found");
return;
}
const container = document.createElement("div");
const apiBaseUrl = document.documentElement.dataset.apiBaseUrl || ""; const apiBaseUrl = document.documentElement.dataset.apiBaseUrl || "";
const apiClient = new ApiClient(apiBaseUrl); const apiClient = new ApiClient(apiBaseUrl);
new TagAutoCompleteComponent({ new TagAutoCompleteComponent({
target: wrapper, target: container,
props: { props: {
id: element.id, id: input.id,
name: element.name, name: input.name,
value: element.value, value: input.value,
placeholder: element.getAttribute("placeholder") || "", placeholder: input.getAttribute("placeholder") || "",
apiClient: apiClient, apiClient: apiClient,
variant: element.getAttribute("variant"), variant: input.getAttribute("variant"),
}, },
}); });
element.replaceWith(wrapper.firstElementChild); this.input = input;
this.autocomplete = container.firstElementChild;
input.replaceWith(this.autocomplete);
}
destroy() {
this.autocomplete.replaceWith(this.input);
} }
} }

View file

@ -23,9 +23,8 @@
<option value="bulk_unshare">Unshare</option> <option value="bulk_unshare">Unshare</option>
{% endif %} {% endif %}
</select> </select>
<div class="tag-autocomplete d-none"> <div class="tag-autocomplete d-none" ld-tag-autocomplete>
<input ld-tag-autocomplete variant="small" <input name="bulk_tag_string" class="form-input input-sm" placeholder="Tag names..." variant="small">
name="bulk_tag_string" class="form-input input-sm" placeholder="Tag names...">
</div> </div>
<button ld-confirm-button type="submit" name="bulk_execute" class="btn btn-link btn-sm"> <button ld-confirm-button type="submit" name="bulk_execute" class="btn btn-link btn-sm">
<span>Execute</span> <span>Execute</span>

View file

@ -19,9 +19,9 @@
The form has been pre-filled with the existing bookmark, and saving the form will update the existing bookmark. The form has been pre-filled with the existing bookmark, and saving the form will update the existing bookmark.
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group" ld-tag-autocomplete>
<label for="{{ form.tag_string.id_for_label }}" class="form-label">Tags</label> <label for="{{ form.tag_string.id_for_label }}" class="form-label">Tags</label>
{{ form.tag_string|add_class:"form-input"|attr:"ld-tag-autocomplete"|attr:"autocomplete:off"|attr:"autocapitalize:off" }} {{ form.tag_string|add_class:"form-input"|attr:"autocomplete:off"|attr:"autocapitalize:off" }}
<div class="form-input-hint"> <div class="form-input-hint">
Enter any number of tags separated by space and <strong>without</strong> the hash (#). Enter any number of tags separated by space and <strong>without</strong> the hash (#).
If a tag does not exist it will be automatically created. If a tag does not exist it will be automatically created.

View file

@ -89,9 +89,9 @@
} }
const apiClient = new linkding.ApiClient('{% url 'bookmarks:api-root' %}') const apiClient = new linkding.ApiClient('{% url 'bookmarks:api-root' %}')
const input = document.querySelector('#search input[name="q"]') const input = document.querySelector('#search input[name="q"]')
const wrapper = document.createElement('div') const container = document.createElement('div')
new linkding.SearchAutoComplete({ new linkding.SearchAutoComplete({
target: wrapper, target: container,
props: { props: {
name: 'q', name: 'q',
placeholder: 'Search for words or #tags', placeholder: 'Search for words or #tags',
@ -103,6 +103,12 @@
search, search,
} }
}) })
input.replaceWith(wrapper.firstElementChild);
const autoComplete = container.firstElementChild;
input.replaceWith(autoComplete);
document.addEventListener("turbo:before-cache", () => {
autoComplete.replaceWith(input);
}, {once: true});
})(); })();
</script> </script>

View file

@ -98,7 +98,7 @@ class BookmarkEditViewTestCase(TestCase, BookmarkFactoryMixin):
tag_string = build_tag_string(bookmark.tag_names, " ") tag_string = build_tag_string(bookmark.tag_names, " ")
self.assertInHTML( self.assertInHTML(
f""" f"""
<input ld-tag-autocomplete type="text" name="tag_string" value="{tag_string}" <input type="text" name="tag_string" value="{tag_string}"
autocomplete="off" autocapitalize="off" class="form-input" id="id_tag_string"> autocomplete="off" autocapitalize="off" class="form-input" id="id_tag_string">
""", """,
html, html,