V1.4.9
This commit is contained in:
parent
4e6b10f0bc
commit
99a83d106c
@ -537,7 +537,7 @@ function scmanutention_validate_item_reference(PDO $db, int $owner_auth_id, stri
|
||||
];
|
||||
}
|
||||
|
||||
function scmanutention_search_available_items(PDO $db, int $owner_auth_id, string $query, int $limit = 12): array
|
||||
function scmanutention_search_available_items(PDO $db, int $owner_auth_id, string $query, ?int $limit = 25, int $offset = 0): array
|
||||
{
|
||||
$query = trim($query);
|
||||
if ($query === '') {
|
||||
@ -548,7 +548,12 @@ function scmanutention_search_available_items(PDO $db, int $owner_auth_id, strin
|
||||
$exact = $escaped;
|
||||
$prefix = $escaped . '%';
|
||||
$contains = '%' . $escaped . '%';
|
||||
$limit = max(1, min(30, $limit));
|
||||
$limit_clause = '';
|
||||
$offset = max(0, $offset);
|
||||
if ($limit !== null && $limit > 0) {
|
||||
$limit = max(1, min(100, $limit));
|
||||
$limit_clause = ' LIMIT ' . (int) $limit . ' OFFSET ' . (int) $offset;
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT *
|
||||
@ -607,7 +612,7 @@ function scmanutention_search_available_items(PDO $db, int $owner_auth_id, strin
|
||||
CHAR_LENGTH(result_name) ASC,
|
||||
result_name ASC,
|
||||
result_key ASC
|
||||
LIMIT {$limit}";
|
||||
{$limit_clause}";
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute([
|
||||
|
||||
@ -133,7 +133,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET' && (string) ($_GET['ajax'] ?? '') === '
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
$query = trim((string) ($_GET['q'] ?? ''));
|
||||
$suggestion_rows = scmanutention_search_available_items($db, $current_owner_auth_id, $query, 14);
|
||||
$offset = max(0, (int) ($_GET['offset'] ?? 0));
|
||||
$limit = max(1, min(50, (int) ($_GET['limit'] ?? 25)));
|
||||
$suggestion_rows = scmanutention_search_available_items($db, $current_owner_auth_id, $query, $limit + 1, $offset);
|
||||
$has_more = count($suggestion_rows) > $limit;
|
||||
if ($has_more) {
|
||||
array_pop($suggestion_rows);
|
||||
}
|
||||
|
||||
$suggestion_custom_ids = [];
|
||||
|
||||
foreach ($suggestion_rows as $row) {
|
||||
@ -165,7 +172,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET' && (string) ($_GET['ajax'] ?? '') === '
|
||||
];
|
||||
}, $suggestion_rows);
|
||||
|
||||
echo json_encode(['items' => $items], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
echo json_encode([
|
||||
'items' => $items,
|
||||
'hasMore' => $has_more,
|
||||
'nextOffset' => $offset + count($items),
|
||||
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -809,6 +820,7 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
z-index: 9999;
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
overscroll-behavior: contain;
|
||||
box-shadow: 0 22px 40px rgba(0, 0, 0, 0.38);
|
||||
}
|
||||
|
||||
@ -885,6 +897,13 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
}
|
||||
|
||||
.picker-status {
|
||||
padding: 0.65rem 0.8rem 0.45rem;
|
||||
color: var(--text-soft);
|
||||
font-size: 0.78rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.picker-selection {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
@ -1347,7 +1366,7 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
<input type="hidden" name="action" value="add_item">
|
||||
<input type="hidden" name="sheet_id" value="<?php echo (int) $selected_sheet['cl_scmanutention_id']; ?>">
|
||||
|
||||
<div class="picker" data-item-picker-root data-endpoint="scmanutention.php?ajax=item_suggestions" data-min-query="2">
|
||||
<div class="picker" data-item-picker-root data-endpoint="scmanutention.php?ajax=item_suggestions" data-min-query="2" data-page-size="25">
|
||||
<input type="hidden" name="item_source" data-picker-source value="">
|
||||
<input type="hidden" name="item_scobjs_id" data-picker-scobjs value="">
|
||||
<input type="hidden" name="item_scitemcustom_id" data-picker-scitemcustom value="">
|
||||
@ -1365,7 +1384,7 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
<div class="picker-dropdown hidden" data-picker-dropdown></div>
|
||||
</div>
|
||||
|
||||
<div class="helper">La recherche démarre à partir de 2 caractères et distingue les objets de base des objets persos.</div>
|
||||
<div class="helper">La recherche démarre à partir de 2 caractères, distingue les objets de base des objets persos, et charge la suite au scroll pour accéder à tous les résultats sans agrandir le panneau.</div>
|
||||
|
||||
<div class="picker-selection hidden" data-picker-selection>
|
||||
<div class="picker-selection-main">
|
||||
@ -1588,6 +1607,7 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
function initPicker(root) {
|
||||
var endpoint = root.getAttribute('data-endpoint') || 'scmanutention.php?ajax=item_suggestions';
|
||||
var minQuery = parseInt(root.getAttribute('data-min-query') || '2', 10);
|
||||
var pageSize = parseInt(root.getAttribute('data-page-size') || '25', 10);
|
||||
var input = root.querySelector('[data-picker-input]');
|
||||
var dropdown = root.querySelector('[data-picker-dropdown]');
|
||||
var sourceInput = root.querySelector('[data-picker-source]');
|
||||
@ -1602,6 +1622,11 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
var requestTimer = null;
|
||||
var requestToken = 0;
|
||||
var selectedItem = null;
|
||||
var loadedItems = [];
|
||||
var currentQuery = '';
|
||||
var nextOffset = 0;
|
||||
var hasMore = false;
|
||||
var isFetchingMore = false;
|
||||
|
||||
if (!input || !dropdown || !sourceInput || !scobjsInput || !scitemcustomInput || !selection || !nameNode || !metaNode || !imageNode) {
|
||||
return;
|
||||
@ -1618,6 +1643,11 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
function hideDropdown() {
|
||||
dropdown.innerHTML = '';
|
||||
dropdown.classList.add('hidden');
|
||||
loadedItems = [];
|
||||
currentQuery = '';
|
||||
nextOffset = 0;
|
||||
hasMore = false;
|
||||
isFetchingMore = false;
|
||||
}
|
||||
|
||||
function showDropdown() {
|
||||
@ -1681,76 +1711,105 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
}
|
||||
}
|
||||
|
||||
function renderItems(items, query) {
|
||||
if (!Array.isArray(items) || items.length === 0) {
|
||||
function buildOptionHtml(item, index) {
|
||||
var meta = [];
|
||||
if (item.type) {
|
||||
meta.push(escapeHtml(item.type));
|
||||
}
|
||||
if (item.subtype) {
|
||||
meta.push(escapeHtml(item.subtype));
|
||||
}
|
||||
if (item.sourceLabel) {
|
||||
meta.push(escapeHtml(item.sourceLabel));
|
||||
}
|
||||
|
||||
var statsPreview = Array.isArray(item.statsPreview) ? item.statsPreview : [];
|
||||
var statsHtml = '';
|
||||
if ((item.source || '') === 'custom') {
|
||||
if (statsPreview.length > 0) {
|
||||
statsHtml = '<div class="picker-option-stats">' +
|
||||
statsPreview.map(function (statText) {
|
||||
return '<span class="stat-pill">' + escapeHtml(statText) + '</span>';
|
||||
}).join('') +
|
||||
((item.statsPreviewMore || 0) > 0 ? '<span class="picker-option-more">+' + escapeHtml(item.statsPreviewMore) + '</span>' : '') +
|
||||
'</div>';
|
||||
} else {
|
||||
statsHtml = '<div class="picker-option-stats picker-option-stats-empty">Aucune stat</div>';
|
||||
}
|
||||
}
|
||||
|
||||
return '' +
|
||||
'<button type="button" class="picker-option' + (index === 0 ? ' is-active' : '') + '" ' +
|
||||
'data-key="' + escapeHtml(item.key || '') + '" ' +
|
||||
'data-source="' + escapeHtml(item.source || '') + '" ' +
|
||||
'data-source-label="' + escapeHtml(item.sourceLabel || '') + '" ' +
|
||||
'data-scobjs-id="' + escapeHtml(item.scobjs_id || '') + '" ' +
|
||||
'data-scitemcustom-id="' + escapeHtml(item.scitemcustom_id || '') + '" ' +
|
||||
'data-name="' + escapeHtml(item.name || '') + '" ' +
|
||||
'data-type="' + escapeHtml(item.type || '') + '" ' +
|
||||
'data-subtype="' + escapeHtml(item.subtype || '') + '" ' +
|
||||
'data-uuid="' + escapeHtml(item.uuid || '') + '" ' +
|
||||
'data-rarity="' + escapeHtml(item.rarity || '') + '">' +
|
||||
'<div class="picker-option-row">' +
|
||||
'<div class="picker-option-main">' +
|
||||
'<strong class="item-name ' + escapeHtml(rarityClass(item.rarity || '')) + '">' + escapeHtml(item.name || '') + '</strong>' +
|
||||
'<div class="item-submeta">' + meta.join(' / ') + '</div>' +
|
||||
'</div>' +
|
||||
statsHtml +
|
||||
'</div>' +
|
||||
'</button>';
|
||||
}
|
||||
|
||||
function renderItems(items, query, append) {
|
||||
var safeItems = Array.isArray(items) ? items : [];
|
||||
loadedItems = append ? loadedItems.concat(safeItems) : safeItems.slice();
|
||||
|
||||
if (loadedItems.length === 0) {
|
||||
dropdown.innerHTML = '<div class="picker-option"><strong>Aucun objet trouvé</strong><div class="item-submeta">Aucun résultat pour “' + escapeHtml(query) + '”.</div></div>';
|
||||
showDropdown();
|
||||
return;
|
||||
}
|
||||
|
||||
dropdown.innerHTML = items.map(function (item, index) {
|
||||
var meta = [];
|
||||
if (item.type) {
|
||||
meta.push(escapeHtml(item.type));
|
||||
}
|
||||
if (item.subtype) {
|
||||
meta.push(escapeHtml(item.subtype));
|
||||
}
|
||||
if (item.sourceLabel) {
|
||||
meta.push(escapeHtml(item.sourceLabel));
|
||||
}
|
||||
var html = loadedItems.map(buildOptionHtml).join('');
|
||||
if (hasMore) {
|
||||
html += '<div class="picker-status">Fais défiler pour charger la suite…</div>';
|
||||
} else if (loadedItems.length > pageSize) {
|
||||
html += '<div class="picker-status">Tous les résultats trouvés sont chargés.</div>';
|
||||
}
|
||||
|
||||
var statsPreview = Array.isArray(item.statsPreview) ? item.statsPreview : [];
|
||||
var statsHtml = '';
|
||||
if ((item.source || '') === 'custom') {
|
||||
if (statsPreview.length > 0) {
|
||||
statsHtml = '<div class="picker-option-stats">' +
|
||||
statsPreview.map(function (statText) {
|
||||
return '<span class="stat-pill">' + escapeHtml(statText) + '</span>';
|
||||
}).join('') +
|
||||
((item.statsPreviewMore || 0) > 0 ? '<span class="picker-option-more">+' + escapeHtml(item.statsPreviewMore) + '</span>' : '') +
|
||||
'</div>';
|
||||
} else {
|
||||
statsHtml = '<div class="picker-option-stats picker-option-stats-empty">Aucune stat</div>';
|
||||
}
|
||||
}
|
||||
|
||||
return '' +
|
||||
'<button type="button" class="picker-option' + (index === 0 ? ' is-active' : '') + '" ' +
|
||||
'data-key="' + escapeHtml(item.key || '') + '" ' +
|
||||
'data-source="' + escapeHtml(item.source || '') + '" ' +
|
||||
'data-source-label="' + escapeHtml(item.sourceLabel || '') + '" ' +
|
||||
'data-scobjs-id="' + escapeHtml(item.scobjs_id || '') + '" ' +
|
||||
'data-scitemcustom-id="' + escapeHtml(item.scitemcustom_id || '') + '" ' +
|
||||
'data-name="' + escapeHtml(item.name || '') + '" ' +
|
||||
'data-type="' + escapeHtml(item.type || '') + '" ' +
|
||||
'data-subtype="' + escapeHtml(item.subtype || '') + '" ' +
|
||||
'data-uuid="' + escapeHtml(item.uuid || '') + '" ' +
|
||||
'data-rarity="' + escapeHtml(item.rarity || '') + '">' +
|
||||
'<div class="picker-option-row">' +
|
||||
'<div class="picker-option-main">' +
|
||||
'<strong class="item-name ' + escapeHtml(rarityClass(item.rarity || '')) + '">' + escapeHtml(item.name || '') + '</strong>' +
|
||||
'<div class="item-submeta">' + meta.join(' / ') + '</div>' +
|
||||
'</div>' +
|
||||
statsHtml +
|
||||
'</div>' +
|
||||
'</button>';
|
||||
}).join('');
|
||||
dropdown.innerHTML = html;
|
||||
showDropdown();
|
||||
}
|
||||
|
||||
function fetchSuggestions(query) {
|
||||
function fetchSuggestions(query, options) {
|
||||
var settings = options || {};
|
||||
var append = !!settings.append;
|
||||
var trimmedQuery = (query || '').trim();
|
||||
if (trimmedQuery.length < minQuery) {
|
||||
hideDropdown();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!append) {
|
||||
currentQuery = trimmedQuery;
|
||||
nextOffset = 0;
|
||||
hasMore = false;
|
||||
isFetchingMore = false;
|
||||
} else if (isFetchingMore || !hasMore) {
|
||||
return;
|
||||
}
|
||||
|
||||
var fetchOffset = append ? nextOffset : 0;
|
||||
var separator = endpoint.indexOf('?') === -1 ? '?' : '&';
|
||||
var url = endpoint + separator + 'q=' + encodeURIComponent(trimmedQuery);
|
||||
var currentToken = ++requestToken;
|
||||
dropdown.innerHTML = '<div class="picker-option"><strong>Recherche en cours...</strong><div class="item-submeta">Chargement des suggestions.</div></div>';
|
||||
showDropdown();
|
||||
var url = endpoint + separator + 'q=' + encodeURIComponent(trimmedQuery) + '&offset=' + encodeURIComponent(fetchOffset) + '&limit=' + encodeURIComponent(pageSize);
|
||||
var currentToken = append ? requestToken : ++requestToken;
|
||||
|
||||
if (!append) {
|
||||
dropdown.innerHTML = '<div class="picker-option"><strong>Recherche en cours...</strong><div class="item-submeta">Chargement des suggestions.</div></div>';
|
||||
showDropdown();
|
||||
} else {
|
||||
isFetchingMore = true;
|
||||
}
|
||||
|
||||
fetch(url, {
|
||||
headers: { 'X-Requested-With': 'XMLHttpRequest' },
|
||||
@ -1766,12 +1825,19 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
if (currentToken !== requestToken) {
|
||||
return;
|
||||
}
|
||||
renderItems(payload && Array.isArray(payload.items) ? payload.items : [], trimmedQuery);
|
||||
|
||||
hasMore = !!(payload && payload.hasMore);
|
||||
nextOffset = payload && typeof payload.nextOffset !== 'undefined'
|
||||
? parseInt(payload.nextOffset, 10) || 0
|
||||
: ((append ? loadedItems.length : 0) + ((payload && Array.isArray(payload.items)) ? payload.items.length : 0));
|
||||
isFetchingMore = false;
|
||||
renderItems(payload && Array.isArray(payload.items) ? payload.items : [], trimmedQuery, append);
|
||||
})
|
||||
.catch(function () {
|
||||
if (currentToken !== requestToken) {
|
||||
return;
|
||||
}
|
||||
isFetchingMore = false;
|
||||
dropdown.innerHTML = '<div class="picker-option"><strong>Recherche indisponible</strong><div class="item-submeta">Impossible de charger les suggestions.</div></div>';
|
||||
showDropdown();
|
||||
});
|
||||
@ -1797,6 +1863,16 @@ $page_access_widget = auth_render_page_access_widget('scmanutention.php', 'Manut
|
||||
}
|
||||
});
|
||||
|
||||
dropdown.addEventListener('scroll', function () {
|
||||
if (!hasMore || isFetchingMore || dropdown.classList.contains('hidden')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (dropdown.scrollTop + dropdown.clientHeight >= dropdown.scrollHeight - 80) {
|
||||
fetchSuggestions(currentQuery || input.value || '', { append: true });
|
||||
}
|
||||
});
|
||||
|
||||
dropdown.addEventListener('click', function (event) {
|
||||
var option = event.target.closest('.picker-option[data-source]');
|
||||
if (!option) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user