Skip to content

Commit

Permalink
Implement frontend layout for BST category field
Browse files Browse the repository at this point in the history
  • Loading branch information
allholy committed Jan 15, 2025
1 parent 85e94c0 commit e54a5ff
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 7 deletions.
4 changes: 3 additions & 1 deletion freesound/static/bw-frontend/src/pages/editDescribeSounds.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,6 @@ inputTypeSubmitElements.forEach(button => {
}
});
});
});
});

// Move json for BST category field in description form here
11 changes: 9 additions & 2 deletions sounds/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

from captcha.fields import ReCaptchaField
from django import forms
from django.core.exceptions import PermissionDenied
from django.core.exceptions import PermissionDenied, ValidationError
from django.db.models import Q
from django.forms import ModelForm, Textarea, TextInput
from django.core.signing import BadSignature, SignatureExpired
Expand Down Expand Up @@ -247,7 +247,8 @@ class SoundEditAndDescribeForm(forms.Form):
widget=forms.TextInput(attrs={'size': 65, 'class': 'inputText'}))
bst_category = forms.ChoiceField(
choices=Sound.BST_CATEGORY_CHOICES,
required=False
help_text="Choose the most appropriate catgeory and subcatgoery (you can only choose one). This category will be displayed as a filtering option in the Freesound side bar.",
required=True,
)
tags = TagField(
widget=forms.Textarea(attrs={'cols': 80, 'rows': 3}),
Expand Down Expand Up @@ -348,6 +349,12 @@ def clean_sources(self):

def clean_pack(self):
return _pack_form_clean_pack_helper(self.cleaned_data)

def clean_bst_category(self):
value = self.cleaned_data['bst_category']
if not '-' in value:
raise ValidationError("Please choose a subcategory.")
return value


class SoundCSVDescriptionForm(SoundEditAndDescribeForm):
Expand Down
11 changes: 11 additions & 0 deletions sounds/templatetags/bst_category.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from django import template

register = template.Library()

@register.filter
def get_top_level_bst_category(value):
"""
Extract the top level category from the given value.
The top level category is the part before the '-' in value.
"""
return value.split('-')[0] if '-' in value else value
3 changes: 2 additions & 1 deletion sounds/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,8 @@ def update_edited_sound(sound, data):
'sounds_per_round': forms_per_round,
'last_latlong': request.user.profile.get_last_latlong(),
'total_sounds_to_describe': len_original_describe_edit_sounds,
'next': request.GET.get('next', '')
'next': request.GET.get('next', ''),
'bst_taxonomy': settings.BROAD_SOUND_TAXONOMY
}

if request.method == "POST" and all_forms_validated_ok:
Expand Down
124 changes: 124 additions & 0 deletions templates/molecules/bst_category_form_field.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
{% load bst_category %}

<div class="bst-category-field">

{{ form.bst_category.errors }}
<div><label>Category</label></div>
{{ form.bst_category.as_hidden }}

<div class="top-buttons v-spacing-top-2">
{% for value, label in form.fields.bst_category.choices %}
{% if "-" not in value %}
<button type="button" class="btn no-hover {% if value == form.bst_category.value|get_top_level_bst_category %}btn-primary{% else %}btn-inverse{% endif %} cell_tooltip_left" data_value="{{ value }}">
{{ label }}
{% for item in bst_taxonomy %}
{% if item.category_code == value %}
<span class="tooltiptext">{{ item.description }}</span>
{% endif %}
{% endfor %}
</button>
{% endif %}
{% endfor %}
</div>

<div class="subcategory-buttons v-spacing-top-2 display-none">
<div><label>Subcategory</label></div>
<div class="row v-spacing-top-2">
{% for value, label in form.fields.bst_category.choices %}
{% if "-" in value %}
<button type="button" class="btn no-hover {% if value == form.bst_category.value %}btn-secondary{% else %}btn-inverse{% endif %} btn-subcategory cell_tooltip_left" top_level="{{ value|get_top_level_bst_category }}" data_value="{{ value }}">
{{ label }}
{% for item in bst_taxonomy %}
{% if item.category_code == value %}
<span class="tooltiptext">{{ item.description }}</span>
{% endif %}
{% endfor %}
</button>
{% endif %}
{% endfor %}
</div>
</div>
<div class="v-spacing-top-4">
<span class="helptext">{{ form.bst_category.help_text|safe }}</span>
</div>
</div>

<script>
document.addEventListener("DOMContentLoaded", function () {

const categoryFieldContainers = document.querySelectorAll(".bst-category-field");
categoryFieldContainers.forEach(container => {
const hiddenField = container.querySelectorAll("input[type=hidden]")[0];
const topButtons = container.querySelectorAll(".top-buttons .btn");
const subcategoryButtons = container.querySelectorAll(".btn-subcategory");
const subcategoryContainer = container.querySelector(".subcategory-buttons");

const updateSubcategoriesList = () => {
if (hiddenField.value === "") {
subcategoryContainer.classList.add("display-none");
return;
} else {
// Display subcategories that match the selected top-level category
// Triggered when a selection is made or updated.
const correspondingTopLevelValue = hiddenField.value.split("-")[0]; // Extract top-level value, if not already.
console.log(correspondingTopLevelValue);
if(correspondingTopLevelValue) {
subcategoryContainer.classList.remove("display-none");

subcategoryButtons.forEach(subBtn => {
const topLevelValue = subBtn.getAttribute("top_level");
const isTrue = topLevelValue === correspondingTopLevelValue;
// console.log("Is condition true?", isTrue);
subBtn.style.display = topLevelValue === correspondingTopLevelValue ? "inline-block" : "none";
});
}
}
}

// Event listener for top-level category buttons
topButtons.forEach(topBtn => {
topBtn.addEventListener("click", function () {
const selectedValue = this.getAttribute("data_value");

// Update hidden input value
hiddenField.value = selectedValue;

// Highlight the selected top-level category button
topButtons.forEach(btn => {
btn.classList.remove("btn-primary")
btn.classList.add("btn-inverse")
btn.setAttribute("aria-selected", "false")
})
this.classList.remove("btn-inverse")
this.classList.add("btn-primary")
this.setAttribute("aria-selected", "true")

updateSubcategoriesList();
});
});

// Event listener for subcategory buttons
subcategoryButtons.forEach(subBtn => {
subBtn.addEventListener("click", function () {
const subcategoryValue = this.getAttribute("data_value");

// Update hidden input value if subcategory is clicked
hiddenField.value = subcategoryValue;

// Highlight the selected subcategory button
subcategoryButtons.forEach(btn => {
btn.classList.remove("btn-secondary")
btn.classList.add("btn-inverse")
btn.setAttribute("aria-selected", "false")
})
this.classList.remove("btn-inverse")
this.classList.add("btn-secondary")
btn.setAttribute("aria-selected", "true");
});
});

updateSubcategoriesList();
});

});
</script>
4 changes: 1 addition & 3 deletions templates/sounds/edit_and_describe.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ <h5 class="text-light-grey v-spacing-2">Basic information</h5>
{{ form.name }}
</div>
<div>
{{ form.bst_category.errors }}
{{ form.bst_category.label_tag }}
{{ form.bst_category }}
{% include "molecules/bst_category_form_field.html" %}
</div>
<div class="v-spacing-top-3">
{% include "molecules/tags_form_field.html" %}
Expand Down

0 comments on commit e54a5ff

Please sign in to comment.