Replace the icon widget's plain text field with a visual searchable picker
The properties panel's "Icône" setting was a raw text input expecting a Font Awesome class name typed by hand — not something a non-developer using Forge Engine should have to know. It's now a button showing the current icon that opens a modal with the same searchable icon grid as the add-gallery; picking one replaces the selected element's icon and autosaves immediately, no typing required. The class name is still stored the same way (a hidden input feeding the existing generic attr:class control), so no backend changes were needed.
This commit is contained in:
@@ -178,6 +178,32 @@
|
|||||||
<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>
|
<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>
|
</div>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<!-- Sélecteur visuel d'icône, ouvert depuis le réglage "Icône" d'un
|
||||||
|
élément Icône déjà posé (voir plus bas dans la boucle des contrôles)
|
||||||
|
— même galerie que ci-dessus, mais choisir une icône ici REMPLACE
|
||||||
|
celle de l'élément sélectionné au lieu d'en ajouter un nouveau. Une
|
||||||
|
seule instance partagée (pas besoin de la refaire par élément). -->
|
||||||
|
<div class="modal" id="iconPickerModal">
|
||||||
|
<div class="modal-background" onclick="closeIconPicker()"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Choisir une icône</p>
|
||||||
|
<button class="delete" aria-label="Fermer" onclick="closeIconPicker()"></button>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<input type="text" class="input is-small" placeholder="Rechercher une icône..." oninput="filterIconPickerModal(this.value)">
|
||||||
|
<div class="iconGallery" id="iconPickerGrid" style="max-height:50vh;">
|
||||||
|
{% for slug in fontawesome_icons %}
|
||||||
|
<button type="button" class="iconGalleryTile" data-icon-search="{{ slug }}"
|
||||||
|
title="fa-{{ slug }}" onclick="chooseIcon('fa-solid fa-{{ slug }}')">
|
||||||
|
<i class="fa-solid fa-{{ slug }}"></i>
|
||||||
|
</button>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% if not selected_id %}
|
{% 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>
|
<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 %}
|
{% else %}
|
||||||
@@ -343,7 +369,15 @@
|
|||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<label class="label is-small">{{ c.label }}</label>
|
<label class="label is-small">{{ c.label }}</label>
|
||||||
{% if c.type in ('text', 'url') %}
|
{% if c.key == 'icon_class' %}
|
||||||
|
<div class="control">
|
||||||
|
<button type="button" class="button is-fullwidth" onclick="openIconPicker()">
|
||||||
|
<span class="icon"><i id="iconPickerPreview" class="{{ c.value or c.default }}"></i></span>
|
||||||
|
<span>Changer l'icône</span>
|
||||||
|
</button>
|
||||||
|
<input type="hidden" name="ctrl_{{ c.key }}" id="field-{{ c.key }}" value="{{ c.value or c.default }}">
|
||||||
|
</div>
|
||||||
|
{% elif c.type in ('text', 'url') %}
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input class="input {{ 'uploadTextField' if c.allow_upload else '' }}" type="text" name="ctrl_{{ c.key }}" value="{{ c.value }}" placeholder="{{ c.placeholder or '' }}" id="field-{{ c.key }}">
|
<input class="input {{ 'uploadTextField' if c.allow_upload else '' }}" type="text" name="ctrl_{{ c.key }}" value="{{ c.value }}" placeholder="{{ c.placeholder or '' }}" id="field-{{ c.key }}">
|
||||||
</div>
|
</div>
|
||||||
@@ -886,6 +920,44 @@ function filterIconGallery(query) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------- Sélecteur visuel d'icône (réglage "Icône" d'un élément déjà
|
||||||
|
// posé) — même liste que la galerie d'ajout ci-dessus, mais choisir une
|
||||||
|
// icône ici remplace celle de l'élément SÉLECTIONNÉ (champ caché
|
||||||
|
// "field-icon_class") au lieu d'en créer un nouveau. ----------
|
||||||
|
function openIconPicker() {
|
||||||
|
var modal = document.getElementById('iconPickerModal');
|
||||||
|
if (modal) modal.classList.add('is-active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeIconPicker() {
|
||||||
|
var modal = document.getElementById('iconPickerModal');
|
||||||
|
if (modal) modal.classList.remove('is-active');
|
||||||
|
}
|
||||||
|
|
||||||
|
function chooseIcon(iconClass) {
|
||||||
|
var input = document.getElementById('field-icon_class');
|
||||||
|
var preview = document.getElementById('iconPickerPreview');
|
||||||
|
if (input) input.value = iconClass;
|
||||||
|
if (preview) preview.className = iconClass;
|
||||||
|
closeIconPicker();
|
||||||
|
submitPropsForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterIconPickerModal(query) {
|
||||||
|
var q = query.trim().toLowerCase();
|
||||||
|
document.querySelectorAll('#iconPickerGrid .iconGalleryTile').forEach(function (tile) {
|
||||||
|
var match = !q || tile.dataset.iconSearch.indexOf(q) !== -1;
|
||||||
|
tile.classList.toggle('iconHidden', !match);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!window.__forgeIconPickerEscBound) {
|
||||||
|
window.__forgeIconPickerEscBound = true;
|
||||||
|
document.addEventListener('keydown', function (e) {
|
||||||
|
if (e.key === 'Escape') closeIconPicker();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (!window.__forgeIconGalleryDragBound) {
|
if (!window.__forgeIconGalleryDragBound) {
|
||||||
window.__forgeIconGalleryDragBound = true;
|
window.__forgeIconGalleryDragBound = true;
|
||||||
var draggedIconClass = null;
|
var draggedIconClass = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user