Compare commits
No commits in common. "master" and "1.0.9" have entirely different histories.
@ -1,192 +0,0 @@
|
|||||||
<script>
|
|
||||||
// Tab switching JS (unchanged)
|
|
||||||
function switchTab(tabId, button) {
|
|
||||||
const contents = document.querySelectorAll('.tab-content');
|
|
||||||
contents.forEach(content => content.classList.remove('active'));
|
|
||||||
|
|
||||||
const buttons = document.querySelectorAll('.tab-button');
|
|
||||||
buttons.forEach(b => b.classList.remove('active'));
|
|
||||||
|
|
||||||
document.getElementById(tabId).classList.add('active');
|
|
||||||
button.classList.add('active');
|
|
||||||
|
|
||||||
if (tabId === 'content6') {
|
|
||||||
setTimeout(initTags, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tags Init (with custom dropdown autocomplete)
|
|
||||||
let tagsListeners = [];
|
|
||||||
function initTags() {
|
|
||||||
const tagsInput = document.getElementById('tags-input');
|
|
||||||
const tagsField = document.getElementById('tags');
|
|
||||||
const hiddenTags = document.getElementById('tags_json');
|
|
||||||
const dropdown = document.getElementById('tags-dropdown');
|
|
||||||
|
|
||||||
if (!tagsInput || !tagsField || !hiddenTags || !dropdown) {
|
|
||||||
console.warn('Tags elements missing');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean old listeners
|
|
||||||
tagsListeners.forEach(ls => ls());
|
|
||||||
tagsListeners = [];
|
|
||||||
|
|
||||||
let tags = [];
|
|
||||||
let existingTags = [];
|
|
||||||
try {
|
|
||||||
tags = JSON.parse(tagsInput.dataset.tags || '[]');
|
|
||||||
existingTags = JSON.parse(tagsInput.dataset.existingTags || '[]');
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('JSON error:', e);
|
|
||||||
tags = [];
|
|
||||||
existingTags = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let selectedIndex = -1; // For keyboard nav
|
|
||||||
|
|
||||||
// Render chips (unchanged)
|
|
||||||
function renderTags() {
|
|
||||||
tagsInput.innerHTML = '';
|
|
||||||
tags.forEach((tag, index) => {
|
|
||||||
const chip = document.createElement('span');
|
|
||||||
chip.className = 'tag-chip';
|
|
||||||
chip.innerHTML = `${tag} <button type="button" class="tag-remove">×</button>`;
|
|
||||||
chip.querySelector('.tag-remove').onclick = () => {
|
|
||||||
tags.splice(index, 1);
|
|
||||||
renderTags();
|
|
||||||
hiddenTags.value = JSON.stringify(tags);
|
|
||||||
};
|
|
||||||
tagsInput.appendChild(chip);
|
|
||||||
});
|
|
||||||
tagsInput.appendChild(tagsField);
|
|
||||||
tagsInput.appendChild(dropdown); // Re-add dropdown
|
|
||||||
hiddenTags.value = JSON.stringify(tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter and render dropdown
|
|
||||||
function updateDropdown() {
|
|
||||||
const value = tagsField.value.toLowerCase().trim();
|
|
||||||
dropdown.innerHTML = '';
|
|
||||||
dropdown.setAttribute('aria-expanded', value ? 'true' : 'false');
|
|
||||||
selectedIndex = -1;
|
|
||||||
|
|
||||||
if (!value) return;
|
|
||||||
|
|
||||||
const matches = existingTags.filter(tag =>
|
|
||||||
tag.toLowerCase().startsWith(value) && !tags.includes(tag.toLowerCase())
|
|
||||||
).slice(0, 10); // Top 10 matches, exclude existing
|
|
||||||
|
|
||||||
matches.forEach((tag, index) => {
|
|
||||||
const li = document.createElement('li');
|
|
||||||
li.textContent = tag;
|
|
||||||
li.setAttribute('role', 'option');
|
|
||||||
li.onclick = () => selectTag(tag);
|
|
||||||
li.onmouseover = () => { selectedIndex = index; updateHighlight(); };
|
|
||||||
dropdown.appendChild(li);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matches.length) dropdown.parentElement.classList.add('has-dropdown');
|
|
||||||
else dropdown.parentElement.classList.remove('has-dropdown');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Highlight selected in dropdown
|
|
||||||
function updateHighlight() {
|
|
||||||
dropdown.querySelectorAll('li').forEach((li, index) => {
|
|
||||||
li.classList.toggle('selected', index === selectedIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select tag from dropdown
|
|
||||||
function selectTag(tag) {
|
|
||||||
addTag(tag, true); // Add as chip, refocus
|
|
||||||
tagsField.value = ''; // Clear input
|
|
||||||
dropdown.setAttribute('aria-expanded', 'false');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add tag (unchanged)
|
|
||||||
function addTag(inputValue, refocus = false) {
|
|
||||||
const tag = inputValue.trim().toLowerCase().replace(/[^\w-]/g, '');
|
|
||||||
if (tag && tag.length > 0 && tag.length <= 50 && !tags.includes(tag)) {
|
|
||||||
tags.push(tag);
|
|
||||||
renderTags();
|
|
||||||
if (refocus) tagsField.focus();
|
|
||||||
}
|
|
||||||
tagsField.value = '';
|
|
||||||
updateDropdown(); // Hide dropdown after add
|
|
||||||
}
|
|
||||||
|
|
||||||
// Events: Enter/comma/TAB adds tag (no form submit)
|
|
||||||
const keydownListener = (e) => {
|
|
||||||
console.log('Keydown:', e.key, 'Value:', tagsField.value, 'Selected:', selectedIndex);
|
|
||||||
if (dropdown.getAttribute('aria-expanded') === 'true') {
|
|
||||||
if (e.key === 'ArrowDown') {
|
|
||||||
e.preventDefault();
|
|
||||||
selectedIndex = Math.min(selectedIndex + 1, dropdown.querySelectorAll('li').length - 1);
|
|
||||||
updateHighlight();
|
|
||||||
dropdown.querySelector(`li:nth-child(${selectedIndex + 1})`).scrollIntoView({ block: 'nearest' });
|
|
||||||
} else if (e.key === 'ArrowUp') {
|
|
||||||
e.preventDefault();
|
|
||||||
selectedIndex = Math.max(selectedIndex - 1, 0);
|
|
||||||
updateHighlight();
|
|
||||||
dropdown.querySelector(`li:nth-child(${selectedIndex + 1})`).scrollIntoView({ block: 'nearest' });
|
|
||||||
} else if ((e.key === 'Enter' || e.key === 'Tab') && selectedIndex >= 0) {
|
|
||||||
e.preventDefault();
|
|
||||||
const selected = dropdown.querySelector(`li:nth-child(${selectedIndex + 1})`).textContent;
|
|
||||||
selectTag(selected); // Adds chip, refocuses
|
|
||||||
} else if (e.key === 'Escape') {
|
|
||||||
e.preventDefault();
|
|
||||||
tagsField.blur();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback for no dropdown or non-selected Tab/Enter
|
|
||||||
if (!dropdown.getAttribute('aria-expanded') === 'true' || selectedIndex < 0) {
|
|
||||||
if (e.key === 'Enter' || e.key === ',') {
|
|
||||||
e.preventDefault();
|
|
||||||
addTag(tagsField.value, true);
|
|
||||||
} else if (e.key === 'Tab') {
|
|
||||||
e.preventDefault();
|
|
||||||
if (tagsField.value.trim()) addTag(tagsField.value, true); // Add typed value, refocus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
tagsField.addEventListener('keydown', keydownListener);
|
|
||||||
tagsListeners.push(() => tagsField.removeEventListener('keydown', keydownListener));
|
|
||||||
|
|
||||||
const inputListener = () => updateDropdown();
|
|
||||||
tagsField.addEventListener('input', inputListener);
|
|
||||||
tagsListeners.push(() => tagsField.removeEventListener('input', inputListener));
|
|
||||||
|
|
||||||
const blurListener = () => {
|
|
||||||
setTimeout(() => dropdown.setAttribute('aria-expanded', 'false'), 150); // Hide after blur
|
|
||||||
if (tagsField.value.trim()) addTag(tagsField.value);
|
|
||||||
};
|
|
||||||
tagsField.addEventListener('blur', blurListener);
|
|
||||||
tagsListeners.push(() => tagsField.removeEventListener('blur', blurListener));
|
|
||||||
|
|
||||||
// Initial render
|
|
||||||
renderTags();
|
|
||||||
console.log('Tags ready with autocomplete, loaded:', tags.length, 'tags');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init on load + observe (unchanged)
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const tagsTab = document.getElementById('content6');
|
|
||||||
if (tagsTab && tagsTab.classList.contains('active')) {
|
|
||||||
initTags();
|
|
||||||
}
|
|
||||||
|
|
||||||
const observer = new MutationObserver((mutations) => {
|
|
||||||
mutations.forEach((mutation) => {
|
|
||||||
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
|
||||||
const target = mutation.target;
|
|
||||||
if (target.id === 'content6' && target.classList.contains('active')) {
|
|
||||||
initTags();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
observer.observe(document.body, { subtree: true, attributes: true });
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user