built custom dropdown field to select a titelbild from the database
This commit is contained in:
parent
edb19bde80
commit
bcf8fa5d66
@ -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 {
|
||||||
|
|||||||
20
the_works/static/js/imagepreview.js
Normal file
20
the_works/static/js/imagepreview.js
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
53
the_works/static/js/imageselect.js
Normal file
53
the_works/static/js/imageselect.js
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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 %}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user