Add a Font Awesome icon gallery to the properties panel, draggable onto the screen
New "Icône" widget (<i class="...">, color/size customizable exactly like any other widget) plus a curated set of ~250 verified Font Awesome 5 Free solid icon names (fontawesome_icons.py) — not the full ~1500-icon catalog, since an embedded list needs to be guaranteed accurate (a wrong class name silently renders as a blank glyph); any other valid FA5 class still works by typing it directly into the widget's "Icône" setting. The gallery lives in the (now always-visible) top of the right floating panel, searchable, with each tile both clickable and HTML5-draggable onto the canvas — either action posts to element_add with the chosen icon_class, which now seeds the new element's class instead of leaving it on the generic default. Available in the element-type template editor too, since it reuses screen_edit.html. Loads Font Awesome 5.15.4 (cdnjs) alongside the existing animate.css/Bulma links, in both the editor and /play. Verified end to end: gallery renders with real icon glyphs, clicking/simulated-drop creates a correctly-classed <i> element, and the actual /play route renders it with Font Awesome loaded. Full suite green (89).
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="{{ google_fonts_link }}">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||
{% endblock %}
|
||||
{% block breadcrumb %}
|
||||
<li><a href="{{ url_for('game_dashboard', slug=game.slug) }}">{{ game.name }}</a></li>
|
||||
@@ -161,6 +162,22 @@
|
||||
<button type="button" class="button is-small" onclick="closePropsPanel()" title="Fermer">✕</button>
|
||||
</div>
|
||||
<div class="floatPanelBody">
|
||||
<details class="propGroup">
|
||||
<summary>🔤 Icônes (Font Awesome)</summary>
|
||||
<div class="propGroupBody">
|
||||
<input type="text" class="input is-small" id="iconGallerySearch" placeholder="Rechercher une icône..." oninput="filterIconGallery(this.value)">
|
||||
<div class="iconGallery" id="iconGallery">
|
||||
{% for slug in fontawesome_icons %}
|
||||
<button type="button" class="iconGalleryTile" draggable="true"
|
||||
data-icon-class="fa-solid fa-{{ slug }}" data-icon-search="{{ slug }}"
|
||||
title="fa-{{ slug }}" onclick="addIconElement('fa-solid fa-{{ slug }}')">
|
||||
<i class="fa-solid fa-{{ slug }}"></i>
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<p class="help">Glisse une icône sur le canevas, ou clique dessus pour l'ajouter — sa couleur et sa taille se règlent ensuite dans ses propriétés ("Icône"). Sélection non exhaustive : n'importe quelle autre classe Font Awesome 5 reste utilisable en la tapant directement dans le réglage "Icône".</p>
|
||||
</div>
|
||||
</details>
|
||||
{% if not selected_id %}
|
||||
<div class="hint" style="padding:20px 0;">Sélectionne un élément (sur l'écran ou dans la liste) pour voir et modifier ses propriétés ici.</div>
|
||||
{% else %}
|
||||
@@ -841,6 +858,67 @@ if (!window.__forgeFloatPanelDragBound) {
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- Galerie d'icônes Font Awesome (panneau de propriétés) ----------
|
||||
//
|
||||
// Cliquer une tuile l'ajoute directement (comme n'importe quelle tuile de
|
||||
// "Ajouter un élément sur l'écran") ; la glisser jusque sur le canevas fait
|
||||
// la même chose au relâchement — le drag HTML5 natif ne permet pas de créer
|
||||
// un vrai élément positionné exactement sous le curseur sans aller-retour
|
||||
// serveur supplémentaire, donc on se contente ici de déclencher le même
|
||||
// ajout "à la position par défaut" que le clic, l'utilisateur repositionne
|
||||
// ensuite comme pour tout nouvel élément.
|
||||
function addIconElement(iconClass) {
|
||||
var form = document.createElement('form');
|
||||
form.method = 'post';
|
||||
form.action = "{{ url_for('element_add', slug=game.slug, screen_id=screen.id) }}";
|
||||
form.innerHTML =
|
||||
'<input type="hidden" name="widget" value="icone">' +
|
||||
'<input type="hidden" name="icon_class" value="' + iconClass.replace(/"/g, '"') + '">';
|
||||
document.body.appendChild(form);
|
||||
form.requestSubmit();
|
||||
}
|
||||
|
||||
function filterIconGallery(query) {
|
||||
var q = query.trim().toLowerCase();
|
||||
document.querySelectorAll('#iconGallery .iconGalleryTile').forEach(function (tile) {
|
||||
var match = !q || tile.dataset.iconSearch.indexOf(q) !== -1;
|
||||
tile.classList.toggle('iconHidden', !match);
|
||||
});
|
||||
}
|
||||
|
||||
if (!window.__forgeIconGalleryDragBound) {
|
||||
window.__forgeIconGalleryDragBound = true;
|
||||
var draggedIconClass = null;
|
||||
|
||||
document.addEventListener('dragstart', function (e) {
|
||||
var tile = e.target.closest('.iconGalleryTile');
|
||||
if (!tile) return;
|
||||
draggedIconClass = tile.dataset.iconClass;
|
||||
tile.classList.add('dragging');
|
||||
e.dataTransfer.effectAllowed = 'copy';
|
||||
try { e.dataTransfer.setData('text/plain', draggedIconClass); } catch (err) {}
|
||||
});
|
||||
|
||||
document.addEventListener('dragend', function (e) {
|
||||
var tile = e.target.closest('.iconGalleryTile');
|
||||
if (tile) tile.classList.remove('dragging');
|
||||
draggedIconClass = null;
|
||||
});
|
||||
|
||||
document.addEventListener('dragover', function (e) {
|
||||
if (!draggedIconClass || !e.target.closest('#canvas')) return;
|
||||
e.preventDefault();
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
});
|
||||
|
||||
document.addEventListener('drop', function (e) {
|
||||
if (!draggedIconClass || !e.target.closest('#canvas')) return;
|
||||
e.preventDefault();
|
||||
addIconElement(draggedIconClass);
|
||||
draggedIconClass = null;
|
||||
});
|
||||
}
|
||||
|
||||
// ---------- Surbrillance sur le canevas de l'élément sélectionné ----------
|
||||
//
|
||||
// Un élément posé DIRECTEMENT sur l'écran a déjà son cadre de sélection via
|
||||
|
||||
Reference in New Issue
Block a user