UI: fixes based on Actionable Feedback from devtalk and frontend changes #40

Merged
Pablo Vazquez merged 15 commits from martonlente/extensions-website:ui-alpha-launch-actionable-feedback into main 2024-02-27 16:05:54 +01:00
7 changed files with 213 additions and 39 deletions

View File

@ -127,6 +127,7 @@
a
overflow: hidden
// TODO: consider changing text-decoration underlines to border-bottoms for improved, more spatious vertical spacings
text-decoration: underline
text-overflow: ellipsis
white-space: nowrap
@ -339,6 +340,7 @@
width: var(--preview-thumbnail-max-size)
.previews-list-item-thumbnail-img
background-color: var(--background-color)
background-position: center
background-size: cover
border-radius: var(--border-radius)
@ -368,6 +370,11 @@
pointer-events: none
user-select: none
.form-control
&[type="file"]
font-size: var(--font-size-extra-small)
max-width: 50%
.ext-version-history
summary
display: flex

View File

@ -1,3 +1,5 @@
// TODO: improve and refactor variable namings
const formsetContainer = document.getElementById('add-image-container');
const form = document.getElementById('update-extension-form');
const btnAddImage = document.getElementById('btn-add-image');
@ -5,35 +7,173 @@ const formsetPrefix = 'form';
const inputTotalForms = document.getElementById(`id_${formsetPrefix}-TOTAL_FORMS`);
const tagInput = document.getElementById('id_tags');
// Create function addImgUploadFormClasses
function addImgUploadFormClasses() {
// Function intializes img upload widgets' custom classes for js manage
const inputImgHelper = document.querySelector('.js-input-img-helper');
const inputImgCaptionHelper = document.querySelector('.js-input-img-caption-helper');
inputImgHelper.querySelector('input')
.classList.add('js-input-img');
inputImgHelper.querySelector('label')
.classList.add('d-none');
inputImgCaptionHelper.querySelector('input')
.classList.add('js-input-img-caption');
}
function appendImageUploadForm() {
const i = inputTotalForms.value;
const formRow = document.createElement('div');
const newFormHTML = `
<div class="col">
<input type="file" name="${formsetPrefix}-${i}-source"
accept="image/*" class="form-control"
id="id_${formsetPrefix}-${i}-source">
<div class="invalid-feedback"></div>
</div>
<div class="col">
<input type="text" name="${formsetPrefix}-${i}-caption"
maxlength="255" placeholder="Describe the preview"
class="form-control" id="id_${formsetPrefix}-${i}-caption">
<div class="invalid-feedback"></div>
<div class="previews-list-item">
<div class="align-items-center d-flex previews-list-item-thumbnail pl-3">
<div class="js-input-img-thumbnail previews-list-item-thumbnail-img" title="Preview">
<div class="align-items-center d-flex js-input-img-thumbnail-icon justify-content-center">
<i class="i-image"></i>
</div>
</div>
</div>
<div class="details flex-grow-1">
<div class="mb-2">
<label for="${formsetPrefix}-${i}-caption">Caption</label>
<input class="js-input-img-caption form-control" id="${formsetPrefix}-${i}-caption" type="text" maxlength="255" name="${formsetPrefix}-${i}-caption" placeholder="Describe the preview">
</div>
<div class="align-items-center d-flex justify-content-between">
<input accept="image/*" class="form-control js-input-img" id="id_${formsetPrefix}-${i}-source" type="file" name="${formsetPrefix}-${i}-source">
<ul class="pt-0">
<li>
<button class="btn btn-link btn-sm js-btn-reset-img-upload-form pl-2 pr-0"><i class="i-refresh"></i> Reset</button>
</li>
<li>
<button class="btn btn-link btn-sm js-btn-remove-img-upload-form pl-2 pr-0"><i class="i-trash"></i> Delete</button>
</li>
</ul>
</div>
</div>
</div>
`;
formRow.classList.add('row', 'ext-edit-field-row');
formRow.classList.add('ext-edit-field-row', 'fade', 'js-ext-edit-field-row');
formRow.innerHTML = newFormHTML;
formsetContainer.appendChild(formRow);
inputTotalForms.value = parseInt(inputTotalForms.value) + 1;
setTimeout(function() {
// TODO: fix jump coming from grid gap on parent
formRow.classList.add('show');
}, 20);
}
btnAddImage.addEventListener('click', function(ev) {
ev.preventDefault();
appendImageUploadForm();
// Init function removeImgUploadForm
removeImgUploadForm();
// Init function resetImgUploadForm
resetImgUploadForm();
// Init function setImgUploadFormThumbnail
setImgUploadFormThumbnail();
return false;
});
// Create function removeImgUploadForm
function removeImgUploadForm() {
const btnRemoveImgUploadForm = document.querySelectorAll('.js-btn-remove-img-upload-form');
btnRemoveImgUploadForm.forEach(function(item) {
item.addEventListener('click', function(e) {
e.preventDefault();
// Find the row parent
const rowParent = this.closest('.js-ext-edit-field-row');
// Remove the row
rowParent.remove();
});
});
}
// Create function resetImgUploadForm
function resetImgUploadForm() {
const btnResetImgUploadForm = document.querySelectorAll('.js-btn-reset-img-upload-form');
btnResetImgUploadForm.forEach(function(item) {
item.addEventListener('click', function(e) {
e.preventDefault();
// Find the row parent
const rowParent = this.closest('.js-ext-edit-field-row');
// Find the input image
const inputImg = rowParent.querySelector('.js-input-img');
// Find the input image caption
const inputImgCaption = rowParent.querySelector('.js-input-img-caption');
// Find the input image thumbnail
const inputImgThumbnail = rowParent.querySelector('.js-input-img-thumbnail');
// Find the input image thumbnail icon
const inputImgThumbnailIcon = rowParent.querySelector('.js-input-img-thumbnail-icon');
// Reset the selected image (if any)
inputImg.value = ''; // Clear the input value
// Reset the selected image caption
inputImgCaption.value='';
// Reset the selected image thumbnail
inputImgThumbnail.removeAttribute('style');
// Hide the selected image thumbnail icon
inputImgThumbnailIcon.classList.add('d-flex');
inputImgThumbnailIcon.classList.remove('d-none');
});
});
}
// Create function setImgUploadFormThumbnail
function setImgUploadFormThumbnail() {
const inputImg = document.querySelectorAll('.js-input-img');
inputImg.forEach(function(item) {
item.addEventListener('change', function(e) {
// Init file image
const file = event.target.files[0];
// Find the row parent
const rowParent = this.closest('.js-ext-edit-field-row');
// Find the thumbnail image
const inputImgThumbnail = rowParent.querySelector('.js-input-img-thumbnail');
// Find the input image thumbnail icon
const inputImgThumbnailIcon = rowParent.querySelector('.js-input-img-thumbnail-icon');
// Set thumbnail img
if (file) {
inputImgThumbnail.style.backgroundImage = `url('${URL.createObjectURL(file)}')`;
}
// Hide the selected image thumbnail icon
inputImgThumbnailIcon.classList.add('d-none');
inputImgThumbnailIcon.classList.remove('d-flex');
});
});
}
// Create function init
function init() {
addImgUploadFormClasses();
resetImgUploadForm();
setImgUploadFormThumbnail();
}
// Configure tag selection, if present on the page
if (tagInput) {
const taggerOptions = {
@ -51,3 +191,5 @@ if (tagInput) {
tagger(tagInput, taggerOptions);
// FIXME: changes to the tags input don't trigger form change
};
init();

View File

@ -307,9 +307,11 @@
{% endif %}
{% for rating in extension.ratings.listed_texts|slice:":3" %}
{% include "ratings/components/review.html" %}
{% empty %}
<a href="{{ extension.get_rate_url }}">{% trans "Be the first to review." %}</a>
{% endfor %}
{% if not extension.ratings.listed.count and not my_rating %}
<a href="{{ extension.get_rate_url }}">{% trans "Be the first to review." %}</a>
{% endif %}
</div>
<div class="col-md-4">
{# Rating #}

View File

@ -1,22 +1,40 @@
{% load common %}
{# Upload new preview images #}
{% load i18n %}
<div id="add-image-container">
{{ add_preview_formset.management_form }}
{{ add_preview_formset.non_form_errors }}
{% for newform in add_preview_formset %}
{% with inlineform=newform|add_form_classes %}
<div class="row">
<div class="col">
{% include "common/components/field.html" with field=inlineform.source label='File' %}
<div class="previews-upload">
<div id="add-image-container" class="previews-list">
{{ add_preview_formset.management_form }}
{{ add_preview_formset.non_form_errors }}
{% for newform in add_preview_formset %}
{% with inlineform=newform|add_form_classes %}
<div class="ext-edit-field-row js-ext-edit-field-row">
<div class="previews-list-item">
<div class="align-items-center d-flex previews-list-item-thumbnail pl-3">
<div class="js-input-img-thumbnail previews-list-item-thumbnail-img" title="Preview">
<div class="align-items-center d-flex js-input-img-thumbnail-icon justify-content-center">
<i class="i-image"></i>
</div>
</div>
</div>
<div class="details flex-grow-1">
<div class="js-input-img-caption-helper mb-2">
{% include "common/components/field.html" with field=inlineform.caption label='Caption' %}
</div>
<div class="align-items-center d-flex js-input-img-helper justify-content-between">
{% include "common/components/field.html" with field=inlineform.source label='File' %}
<ul class="pt-0">
<li>
<button class="btn btn-link btn-sm js-btn-reset-img-upload-form pl-2 pr-0"><i class="i-refresh"></i> Reset</button>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="col">
{% include "common/components/field.html" with field=inlineform.caption label='Caption' %}
</div>
</div>
{{ inlineform.non_form_errors }}
{% endwith %}
{% endfor %}
{{ inlineform.non_form_errors }}
{% endwith %}
{% endfor %}
</div>
</div>
<div class="row">
<div class="col text-right mt-3">

View File

@ -126,13 +126,15 @@
</div>
</div>
<div class="btn-col justify-content-end">
{# TODO: Make deletion work #}
<a href="{{ extension.get_delete_url }}" class="btn btn-danger btn-link w-100">
<i class="i-trash"></i> {% trans 'Delete Extension' %}
</a>
</div>
{# TODO: remove comment to show btn delete if deletion works #}
{% comment %}
<div class="btn-col justify-content-end">
{# TODO: Make deletion work #}
<a href="{{ extension.get_delete_url }}" class="btn btn-danger btn-link w-100">
<i class="i-trash"></i> {% trans 'Delete Extension' %}
</a>
</div>
{% endcomment %}
</div>
</div>
</div>

View File

@ -32,8 +32,9 @@
{% endfor %}
</div>
</div>
{% elif not my_rating %}
<span class="text-muted">Be the first to review.</span>
{% else %}
<span class="text-muted">Be the first to review.</span>
{% endif %}
<div class="mt-3">

View File

@ -27,9 +27,11 @@
{% endif %}
{% for rating in object_list %}
{% include "ratings/components/review.html" %}
{% empty %}
{% trans "Be the first to review." %}
{% endfor %}
{% if not extension.ratings.listed.count and not my_rating %}
{% trans "Be the first to review." %}
{% endif %}
</div>
</div>