built custom dropdown field to select a titelbild from the database

This commit is contained in:
eclipse 2025-05-28 20:58:47 +02:00
parent edb19bde80
commit bcf8fa5d66
6 changed files with 208 additions and 47 deletions

View File

@ -171,8 +171,8 @@ form:not([novalidate]) input:user-valid[type="search"] {
} }
/* style for Klappentext tooltips */ /* style for Klappentext tooltips */
[data-tooltip][data-placement="bottom"]::after, [data-tooltip][data-placement="bottom"]:after,
[data-tooltip][data-placement="bottom"]::before { [data-tooltip][data-placement="bottom"]:before {
white-space: pre-line; white-space: pre-line;
max-width: 400px; max-width: 400px;
} }
@ -212,6 +212,68 @@ form:not([novalidate]) input:user-valid[type="search"] {
display: none; display: none;
} }
/* imageselect styles */
label:has([type="radio"]) {
width: 100%;
}
.imageselect-entry,
.imageselect-entry-summary {
display: inline grid;
grid-template-columns: 2rem 2rem auto;
grid-column-gap: var(--pico-form-element-spacing-horizontal);
align-content: center;
height: calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2);
padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);
.imageselect-div {
display: flex;
align-items: center;
justify-content: center;
border: var(--pico-border-width) solid var(--pico-table-border-color);
cursor: zoom-in;
max-height: 100%;
text-align: center;
width: calc(1rem * var(--pico-line-height));
/* display: block;
padding: 0px;
margin: auto;*/
}
.imageselect-img,
.imageselect-svg {
object-fit: contain;
height: calc(1rem * var(--pico-line-height));
}
}
details.imageselect > summary.imageselect-summary {
padding: 0px;
}
#imagepreview-modal {
max-height: 95vh;
.imagepreview-div {
display: flex;
align-items: center;
justify-content: center;
}
}
/* .imageselect-overlay {
position: absolute;
top: 50px;
left: 50px;
display: none;
}
.imageselect-div:hover .imageselect-overlay {
display: block;
} */
[data-display-werksform]::before { [data-display-werksform]::before {

View File

@ -0,0 +1,20 @@
/*
* IMAGEPREVIEW FUNCTIONS
*/
function initImagepreview(modal_id) {
// event handlers for all the thumbnails that raise the preview modal on click
document.querySelectorAll(".imageselect-div").forEach(el => el.addEventListener("click", handleImagepreview, true));
// enable closing the modal
document.getElementById(modal_id).addEventListener("click", event => event.target.close());
document.querySelector("dialog button.modal-close").addEventListener("click", event => event.target.closest("dialog").close());
}
function handleImagepreview(event) {
let modal = document.getElementById("imagepreview-modal");
modal.querySelector("img").setAttribute("src", event.target.closest(".imageselect-entry").dataset["bild"]);
modal.querySelector(".imagepreview-details").innerText = event.target.closest(".imageselect-entry").querySelector(".imageselect-label").innerText;
modal.showModal();
if ( event.target.closest(".imageselect-entry").localName.toLowerCase() == "div" ) {
event.stopPropagation();
}
}

View File

@ -0,0 +1,53 @@
/*
* IMAGESELECT FUNCTIONS
*/
// initialize imege select with given id
function initImageselect(id) {
console.log("this is initImageselect!");
let is = document.getElementById(id);
// add event listener to radio buttons
let summary = is.querySelector("summary");
let input_name = is.querySelector("input[type='radio']").getAttribute("name");
document.querySelectorAll(`[name='${input_name}']`).forEach( el => {
el.addEventListener("change", event => updateImageselect(event.target, summary));
if ( el.checked ) {
el.dispatchEvent(new Event("change"));
}
});
}
// event handler for imageselect radio buttons; displays the selected entry in the dropdown's summary element
function updateImageselect(that, summary) {
// clear summary contents
summary.innerHTML = "";
// create new element which will contain a copy of the entry in the dropdown menu
let div = document.createElement("div");
div.classList.add("imageselect-entry");
div.dataset["bild"] = that.closest(".imageselect-entry").dataset["bild"];
// remove radio button's attributes id (no duplicates allowed) and name (so it won't count towards the form)
let n = that.closest(".imageselect-entry").querySelector(".imageselect-input").cloneNode(true);
n.firstElementChild.removeAttribute("name");
n.firstElementChild.removeAttribute("id");
n.firstElementChild.setAttribute("checked", "");
div.appendChild(n);
// clone image part
n = that.closest(".imageselect-entry").querySelector(".imageselect-div").cloneNode(true);
n.addEventListener("click", handleImagepreview, true);
div.appendChild(n);
// remove label element (but keep the contents) so it doesn't interfere with the dropdown functionality
n = that.closest(".imageselect-entry").querySelector(".imageselect-title").cloneNode(true);
let innerText = n.firstElementChild.innerText;
n.classList.add("imageselect-label");
n.firstElementChild.remove();
n.innerText = innerText;
div.appendChild(n);
// add div to summary
summary.appendChild(div);
}

View File

@ -9,7 +9,6 @@ function initModal(modal_id, input_ids, headings, form_actions) {
document.getElementById("create-button").addEventListener("click", () => showDialog(modal_id, input_ids, headings[0], form_actions[0], new Array(input_ids.length).fill(""), 0)); document.getElementById("create-button").addEventListener("click", () => showDialog(modal_id, input_ids, headings[0], form_actions[0], new Array(input_ids.length).fill(""), 0));
// add event listeners to "update" elements // add event listeners to "update" elements
for (const el of document.querySelectorAll('.action-update')) { for (const el of document.querySelectorAll('.action-update')) {
console.log(`input_ids is ${input_ids}`);
el.addEventListener("click", event => showDialog( el.addEventListener("click", event => showDialog(
modal_id, modal_id,
input_ids, input_ids,
@ -19,6 +18,8 @@ console.log(`input_ids is ${input_ids}`);
event.currentTarget.dataset.id event.currentTarget.dataset.id
)); ));
} }
// add event listener to close button
document.querySelector("dialog button.modal-close").addEventListener("click", event => event.target.closest("dialog").close());
} }

View File

@ -4,7 +4,7 @@
// initializes all dropdown selects on the page // initializes all dropdown selects on the page
function initAllMultiselects() { function initAllMultiselects() {
// initialize each dropdown individually // initialize each dropdown individually
let dropdowns = document.querySelectorAll("form details.dropdown"); let dropdowns = document.querySelectorAll("form details.multiselect");
dropdowns.forEach(d => initMultiselect(d)); dropdowns.forEach(d => initMultiselect(d));
// now it's time to set the z-indices // now it's time to set the z-indices
// get the_works style sheet // get the_works style sheet
@ -24,7 +24,7 @@ function initAllMultiselects() {
} }
} }
// add event handler to summary elements preventing default behavior (toggling the details element) if the click actually targeted a badge // add event handler to summary elements preventing default behavior (toggling the details element) if the click actually targeted a badge
document.querySelectorAll("form details.dropdown summary").forEach(summary => summary.addEventListener("click", event => summaryClick(event, summary), true)); document.querySelectorAll("form details.multiselect summary").forEach(summary => summary.addEventListener("click", event => summaryClick(event, summary), true));
} }

View File

@ -45,17 +45,19 @@ Werk bearbeiten
{% for wf in werksformen %}<option value="{{ wf.ID }}"{% if wf.ID == werk['Werksform'] %} selected{% endif %}>{{ wf.Werksform }}</option>{% endfor %} {% for wf in werksformen %}<option value="{{ wf.ID }}"{% if wf.ID == werk['Werksform'] %} selected{% endif %}>{{ wf.Werksform }}</option>{% endfor %}
</select> </select>
</label> </label>
<label> <div class="grid">
Reihe <label>
<select id="form_Reihe" name="form_Reihe" aria-label="Der Text gehört zur Reihe …"> Reihe
<option value="" >keine Reihe</option> <select id="form_Reihe" name="form_Reihe" aria-label="Der Text gehört zur Reihe …">
{% for r in reihen %}<option value="{{ r.ID }}"{% if r.ID == werk['Reihe'] %} selected{% endif %}>{{ r.Titel }}</option>{% endfor %} <option value="" >keine Reihe</option>
</select> {% for r in reihen %}<option value="{{ r.ID }}"{% if r.ID == werk['Reihe'] %} selected{% endif %}>{{ r.Titel }}</option>{% endfor %}
</label> </select>
<label> </label>
Reihennummer <label>
<input id="form_Reihennummer" name="form_Reihennummer" aria-label="Reihennummer" placeholder="keine Reihennummer" value="{{ werk['Reihennummer'] or '' }}" /> Reihennummer
</label> <input id="form_Reihennummer" name="form_Reihennummer" aria-label="Reihennummer" placeholder="keine Reihennummer" value="{{ werk['Reihennummer'] or '' }}" />
</label>
</div>
<label> <label>
Verlag Verlag
<select id="form_Verlag" name="form_Verlag" aria-label="Verlag"> <select id="form_Verlag" name="form_Verlag" aria-label="Verlag">
@ -67,6 +69,41 @@ Werk bearbeiten
Preis Preis
<input id="form_Preis" name="form_Preis" aria-label="Preis" placeholder="kein Preis" value="{{ werk['Preis'] or '' }}" /> <input id="form_Preis" name="form_Preis" aria-label="Preis" placeholder="kein Preis" value="{{ werk['Preis'] or '' }}" />
</label> </label>
<label>
Titelbild
<details id="form_Titelbild_dropdown" class="dropdown imageselect">
<summary id="form_Titelbild_summary" class="imageselect-summary">
</summary>
<ul>
<li class="imageselect-entry" data-bild="">
<div class="imageselect-input">
<input id="imageselect-radio-0" type="radio" name="form_Titelbild" value="" {% if not werk['Titelbild'] %}checked{% endif %}/>
</div>
<div class="imageselect-div">
<svg viewbox="0 0 90 128">
<use class="imageselect-svg" href="#placeholder" />
</svg>
</div>
<div class="imageselect-title">
<label class="imageselect-label" for="imageselect-radio-0">kein Titelbild</label>
</div>
</li>
{% for t in titelbilder %}
<li class="imageselect-entry" data-bild="{{ t.Bild }}">
<div class="imageselect-input">
<input id="imageselect-radio-{{ t.ID }}" type="radio" name="form_Titelbild" value="{{ t.ID }}" {% if werk['Titelbild'] == t.ID %}checked{% endif %}/>
</div>
<div class="imageselect-div">
<img class="imageselect-img" src="{{ t.Thumbnail }}" />
</div>
<div class="imageselect-title">
<label class="imageselect-label" for="imageselect-radio-{{ t.ID }}">{{ t.Dateiname }} ({{ t.Breite }} x {{ t.Hoehe }}, {{ t.Dateigroesse }} Bytes)</label>
</div>
</li>
{% endfor %}
</ul>
</details>
</label>
</div> </div>
<div> <div>
<label> <label>
@ -91,7 +128,7 @@ Werk bearbeiten
</label> </label>
<label> <label>
Genre(s) Genre(s)
<details id="form_Genre_dropdown" class="dropdown"> <details id="form_Genre_dropdown" class="dropdown multiselect">
<summary id="form_Genre_summary" data-default="kein Genre ausgewählt"></summary> <summary id="form_Genre_summary" data-default="kein Genre ausgewählt"></summary>
<ul> <ul>
<li> <li>
@ -110,7 +147,7 @@ Werk bearbeiten
</label> </label>
<label> <label>
Herausgeber:in(nen) Herausgeber:in(nen)
<details id="form_Herausgeber_dropdown" class="dropdown"> <details id="form_Herausgeber_dropdown" class="dropdown multiselect">
<summary id="form_Herausgeber_summary" data-default="kein(e) Herausgeber:in(nen) ausgewählt"></summary> <summary id="form_Herausgeber_summary" data-default="kein(e) Herausgeber:in(nen) ausgewählt"></summary>
<ul> <ul>
<li> <li>
@ -141,35 +178,6 @@ Werk bearbeiten
{{ werk['Anmerkungen'] or '' }}</textarea> {{ werk['Anmerkungen'] or '' }}</textarea>
</label> </label>
</section> </section>
<hr />
<section>
<label>
Titelbild
<div id="titelbild-wrapper">
<div id="titelbild-pic">
<a id="titelbild-link" href="{{ titelbild['Bild'] | default('') }}" title="zum Originalbild"{{ ' class="display-none"' | safe if not titelbild }}>
<img id="titelbild-thumbnail" alt="Thumbnail" src="{{ titelbild['Thumbnail'] | default('') }}" />
</a>
<div id="titelbild-placeholder"{{ ' class="display-none"' | safe if titelbild }}>
<svg viewbox="0 0 90 128">
<use href="#placeholder" />
</svg>
</div>
</div>
<div id="titelbild-controls">
<button id="titelbild-delete" class="contrast" type="button"{{ ' disabled' | safe if not titelbild }}>Bild löschen</button>
<input type="file" id="titelbild-upload" name="form_Titelbild" aria-label="Titelbild" placeholder="kein Titelbild" accept="image/*"/>
<input type="hidden" id="titelbild-haschanged" name="form_Titelbild_haschanged" value="" />
</div>
<div id="titelbild-info"{{ ' class="display-none"' | safe if not titelbild }}>
<small>
Dateigröße: <span id="titelbild-dateigroesse">{{ titelbild["Dateigroesse"] | default('') }}</span> Bytes<br />
Abmessungen: <span id="titelbild-breite">{{ titelbild["Breite"] | default('') }}</span>x<span id="titelbild-hoehe">{{ titelbild["Hoehe"] | default('') }}</span> px
</small>
</div>
</div>
</label>
</section>
<footer class="grid"> <footer class="grid">
<button id="form_submit" type="submit" onclick="return validate_date()" formmethod="post" formaction="{% if create_mode %}{{ url_for('werk.create') }}{% else %}{{ url_for('werk.update', id=werk['ID']) }}{% endif %}"> <button id="form_submit" type="submit" onclick="return validate_date()" formmethod="post" formaction="{% if create_mode %}{{ url_for('werk.create') }}{% else %}{{ url_for('werk.update', id=werk['ID']) }}{% endif %}">
@ -179,14 +187,31 @@ Werk bearbeiten
<a role="button" class="contrast" href="{{ url_for('werk.all') }}">Abbrechen (nicht speichern)</a> <a role="button" class="contrast" href="{{ url_for('werk.all') }}">Abbrechen (nicht speichern)</a>
</footer> </footer>
</form> </form>
<dialog id="imagepreview-modal">
<article>
<header>
<button class="modal-close" aria-label="close" rel="prev"></button>
<div class="imagepreview-details"></div>
</header>
<div class="imagepreview-div">
<img src="#" alt="#" />
</div>
</article>
</dialog>
{% endblock content %} {% endblock content %}
{% block script %} {% block script %}
<script src="{{ url_for('static', filename='js/multiselect.js') }}"></script> <script src="{{ url_for('static', filename='js/multiselect.js') }}"></script>
<script src="{{ url_for('static', filename='js/imageselect.js') }}"></script>
<script src="{{ url_for('static', filename='js/imagepreview.js') }}"></script>
<script src="{{ url_for('static', filename='js/validate_date.js') }}"></script> <script src="{{ url_for('static', filename='js/validate_date.js') }}"></script>
<script> <script>
window.onload = () => { window.onload = () => {
initAllMultiselects(); initAllMultiselects();
initImageselect("form_Titelbild_dropdown");
initImagepreview("imagepreview-modal");
} }
</script> </script>
{% endblock script %} {% endblock script %}