Cleanup modals

This commit is contained in:
Sascha Ißbrücker
2025-12-31 17:30:59 +01:00
parent 9013a8dfc2
commit b97b0493e0
4 changed files with 38 additions and 61 deletions

View File

@@ -22,16 +22,20 @@ customElements.define("ld-filter-drawer-trigger", FilterDrawerTrigger);
class FilterDrawer extends Modal {
connectedCallback() {
this.classList.add("modal", "drawer", "filter-drawer");
this.classList.add("modal", "drawer");
// Render modal structure
render(
html`
<div class="modal-overlay"></div>
<div class="modal-overlay" data-close-modal></div>
<div class="modal-container" role="dialog" aria-modal="true">
<div class="modal-header">
<h2>Filters</h2>
<button class="btn btn-noborder close" aria-label="Close dialog">
<button
class="btn btn-noborder close"
aria-label="Close dialog"
data-close-modal
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
@@ -49,9 +53,7 @@ class FilterDrawer extends Modal {
</svg>
</button>
</div>
<div class="modal-body">
<div class="content"></div>
</div>
<div class="modal-body"></div>
</div>
`,
this,
@@ -61,6 +63,8 @@ class FilterDrawer extends Modal {
// Force close on turbo cache to restore content
this.doClose = this.doClose.bind(this);
document.addEventListener("turbo:before-cache", this.doClose);
// Force reflow to make transform transition work
this.getBoundingClientRect();
// Add active class to start slide-in animation
requestAnimationFrame(() => this.classList.add("active"));
// Call super after rendering to ensure elements are available
@@ -70,7 +74,7 @@ class FilterDrawer extends Modal {
disconnectedCallback() {
super.disconnectedCallback();
this.teleportBack();
document.addEventListener("turbo:before-cache", this.doClose);
document.removeEventListener("turbo:before-cache", this.doClose);
}
mapHeading(container, from, to) {
@@ -83,7 +87,7 @@ class FilterDrawer extends Modal {
}
teleport() {
const content = this.querySelector(".content");
const content = this.querySelector(".modal-body");
const sidePanel = document.querySelector(".side-panel");
content.append(...sidePanel.children);
this.mapHeading(content, "h2", "h3");
@@ -91,7 +95,7 @@ class FilterDrawer extends Modal {
teleportBack() {
const sidePanel = document.querySelector(".side-panel");
const content = this.querySelector(".content");
const content = this.querySelector(".modal-body");
sidePanel.append(...content.children);
this.mapHeading(sidePanel, "h3", "h2");
}

View File

@@ -5,11 +5,10 @@ export class Modal extends HTMLElement {
requestAnimationFrame(() => {
this.onClose = this.onClose.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.overlay = this.querySelector(".modal-overlay");
this.closeButton = this.querySelector(".modal-header .close");
this.overlay.addEventListener("click", this.onClose);
this.closeButton.addEventListener("click", this.onClose);
this.querySelectorAll("[data-close-modal]").forEach((btn) => {
btn.addEventListener("click", this.onClose);
});
document.addEventListener("keydown", this.onKeyDown);
this.setupScrollLock();
@@ -20,8 +19,9 @@ export class Modal extends HTMLElement {
}
disconnectedCallback() {
this.overlay.removeEventListener("click", this.onClose);
this.closeButton.removeEventListener("click", this.onClose);
this.querySelectorAll("[data-close-modal]").forEach((btn) => {
btn.removeEventListener("click", this.onClose);
});
document.removeEventListener("keydown", this.onKeyDown);
this.removeScrollLock();
@@ -74,8 +74,9 @@ export class Modal extends HTMLElement {
// Navigate to close URL
const closeUrl = this.dataset.closeUrl;
const frame = this.dataset.turboFrame;
const action = this.dataset.turboAction || "replace";
if (closeUrl) {
Turbo.visit(closeUrl, { action: "replace", frame: frame });
Turbo.visit(closeUrl, { action, frame: frame });
}
}
}

View File

@@ -1,25 +1,12 @@
<ld-details-modal class="modal active bookmark-details"
data-bookmark-id="{{ details.bookmark.id }}" data-close-url="{{ details.close_url }}"
data-turbo-frame="details-modal">
<div class="modal-overlay"></div>
<div class="modal-overlay" data-close-modal></div>
<div class="modal-container" role="dialog" aria-modal="true">
<div class="modal-header">
<h2 class="title">{{ details.bookmark.resolved_title }}</h2>
<button class="btn btn-noborder close" aria-label="Close dialog">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M18 6l-12 12"></path>
<path d="M6 6l12 12"></path>
</svg>
</button>
</div>
{% include 'shared/modal_header.html' with title=details.bookmark.resolved_title %}
<div class="modal-body">
<div class="content">
{% include 'bookmarks/details/form.html' %}
</div>
{% include 'bookmarks/details/form.html' %}
</div>
{% if details.is_editable %}
<div class="modal-footer">
<div class="actions">

View File

@@ -3,40 +3,25 @@
data-turbo-frame="api-section">
{% csrf_token %}
<ld-modal class="modal active" data-close-url="{% url 'linkding:settings.integrations' %}"
data-turbo-frame="api-modal">
<div class="modal-overlay"></div>
data-turbo-frame="api-modal">
<div class="modal-overlay" data-close-modal></div>
<div class="modal-container" role="dialog" aria-modal="true">
<div class="modal-header">
<h2 class="title">Create API Token</h2>
<button type="button" class="btn btn-noborder close" aria-label="Close dialog">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" stroke-width="2"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M18 6l-12 12"></path>
<path d="M6 6l12 12"></path>
</svg>
</button>
</div>
{% include 'shared/modal_header.html' with title="Create API Token" %}
<div class="modal-body">
<div class="content">
<div class="form-group">
<label class="form-label" for="token-name">Token name</label>
<input type="text"
class="form-input"
id="token-name"
name="name"
placeholder="e.g., Browser Extension, Mobile App"
value="API Token"
maxlength="128">
<p class="form-input-hint">A descriptive name to identify the purpose of the token</p>
</div>
<div class="form-group">
<label class="form-label" for="token-name">Token name</label>
<input type="text"
class="form-input"
id="token-name"
name="name"
placeholder="e.g., Browser Extension, Mobile App"
value="API Token"
maxlength="128">
<p class="form-input-hint">A descriptive name to identify the purpose of the token</p>
</div>
</div>
<div class="modal-footer d-flex justify-between">
<a class="btn btn-wide"
href="{% url 'linkding:settings.integrations' %}"
data-turbo-frame="api-modal">Cancel</a>
<a class="btn btn-wide" data-close-modal>Cancel</a>
<button type="submit" class="btn btn-primary">Create Token</button>
</div>
</div>