mirror of
https://github.com/sissbruecker/linkding
synced 2024-11-22 11:23:02 +00:00
Fix several issues around browser back navigation (#825)
This commit is contained in:
parent
74e65bc366
commit
db225d5267
10 changed files with 64 additions and 23 deletions
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
|
if (this.container) {
|
||||||
this.container.remove();
|
this.container.remove();
|
||||||
|
this.container = null;
|
||||||
|
}
|
||||||
this.element.classList.remove("d-none");
|
this.element.classList.remove("d-none");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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>
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue