control panel head changes
This commit is contained in:
parent
fd9a71c4d6
commit
0d91b61bd0
@ -1,5 +1,6 @@
|
|||||||
<script>
|
// /js/tabs.js
|
||||||
// Tab switching JS (unchanged)
|
|
||||||
|
// Tab switching
|
||||||
function switchTab(tabId, button) {
|
function switchTab(tabId, button) {
|
||||||
const contents = document.querySelectorAll('.tab-content');
|
const contents = document.querySelectorAll('.tab-content');
|
||||||
contents.forEach(content => content.classList.remove('active'));
|
contents.forEach(content => content.classList.remove('active'));
|
||||||
@ -17,6 +18,7 @@ function switchTab(tabId, button) {
|
|||||||
|
|
||||||
// Tags Init (with custom dropdown autocomplete)
|
// Tags Init (with custom dropdown autocomplete)
|
||||||
let tagsListeners = [];
|
let tagsListeners = [];
|
||||||
|
|
||||||
function initTags() {
|
function initTags() {
|
||||||
const tagsInput = document.getElementById('tags-input');
|
const tagsInput = document.getElementById('tags-input');
|
||||||
const tagsField = document.getElementById('tags');
|
const tagsField = document.getElementById('tags');
|
||||||
@ -28,7 +30,7 @@ function initTags() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean old listeners
|
// Remove old listeners
|
||||||
tagsListeners.forEach(ls => ls());
|
tagsListeners.forEach(ls => ls());
|
||||||
tagsListeners = [];
|
tagsListeners = [];
|
||||||
|
|
||||||
@ -39,13 +41,10 @@ function initTags() {
|
|||||||
existingTags = JSON.parse(tagsInput.dataset.existingTags || '[]');
|
existingTags = JSON.parse(tagsInput.dataset.existingTags || '[]');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('JSON error:', e);
|
console.warn('JSON error:', e);
|
||||||
tags = [];
|
|
||||||
existingTags = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectedIndex = -1; // For keyboard nav
|
let selectedIndex = -1;
|
||||||
|
|
||||||
// Render chips (unchanged)
|
|
||||||
function renderTags() {
|
function renderTags() {
|
||||||
tagsInput.innerHTML = '';
|
tagsInput.innerHTML = '';
|
||||||
tags.forEach((tag, index) => {
|
tags.forEach((tag, index) => {
|
||||||
@ -60,11 +59,10 @@ function initTags() {
|
|||||||
tagsInput.appendChild(chip);
|
tagsInput.appendChild(chip);
|
||||||
});
|
});
|
||||||
tagsInput.appendChild(tagsField);
|
tagsInput.appendChild(tagsField);
|
||||||
tagsInput.appendChild(dropdown); // Re-add dropdown
|
tagsInput.appendChild(dropdown);
|
||||||
hiddenTags.value = JSON.stringify(tags);
|
hiddenTags.value = JSON.stringify(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter and render dropdown
|
|
||||||
function updateDropdown() {
|
function updateDropdown() {
|
||||||
const value = tagsField.value.toLowerCase().trim();
|
const value = tagsField.value.toLowerCase().trim();
|
||||||
dropdown.innerHTML = '';
|
dropdown.innerHTML = '';
|
||||||
@ -73,9 +71,9 @@ function initTags() {
|
|||||||
|
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|
||||||
const matches = existingTags.filter(tag =>
|
const matches = existingTags
|
||||||
tag.toLowerCase().startsWith(value) && !tags.includes(tag.toLowerCase())
|
.filter(tag => tag.toLowerCase().startsWith(value) && !tags.includes(tag.toLowerCase()))
|
||||||
).slice(0, 10); // Top 10 matches, exclude existing
|
.slice(0, 10);
|
||||||
|
|
||||||
matches.forEach((tag, index) => {
|
matches.forEach((tag, index) => {
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
@ -90,21 +88,18 @@ function initTags() {
|
|||||||
else dropdown.parentElement.classList.remove('has-dropdown');
|
else dropdown.parentElement.classList.remove('has-dropdown');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Highlight selected in dropdown
|
|
||||||
function updateHighlight() {
|
function updateHighlight() {
|
||||||
dropdown.querySelectorAll('li').forEach((li, index) => {
|
dropdown.querySelectorAll('li').forEach((li, index) => {
|
||||||
li.classList.toggle('selected', index === selectedIndex);
|
li.classList.toggle('selected', index === selectedIndex);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select tag from dropdown
|
|
||||||
function selectTag(tag) {
|
function selectTag(tag) {
|
||||||
addTag(tag, true); // Add as chip, refocus
|
addTag(tag, true);
|
||||||
tagsField.value = ''; // Clear input
|
tagsField.value = '';
|
||||||
dropdown.setAttribute('aria-expanded', 'false');
|
dropdown.setAttribute('aria-expanded', 'false');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tag (unchanged)
|
|
||||||
function addTag(inputValue, refocus = false) {
|
function addTag(inputValue, refocus = false) {
|
||||||
const tag = inputValue.trim().toLowerCase().replace(/[^\w-]/g, '');
|
const tag = inputValue.trim().toLowerCase().replace(/[^\w-]/g, '');
|
||||||
if (tag && tag.length > 0 && tag.length <= 50 && !tags.includes(tag)) {
|
if (tag && tag.length > 0 && tag.length <= 50 && !tags.includes(tag)) {
|
||||||
@ -113,44 +108,42 @@ function initTags() {
|
|||||||
if (refocus) tagsField.focus();
|
if (refocus) tagsField.focus();
|
||||||
}
|
}
|
||||||
tagsField.value = '';
|
tagsField.value = '';
|
||||||
updateDropdown(); // Hide dropdown after add
|
updateDropdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Events: Enter/comma/TAB adds tag (no form submit)
|
|
||||||
const keydownListener = (e) => {
|
const keydownListener = (e) => {
|
||||||
console.log('Keydown:', e.key, 'Value:', tagsField.value, 'Selected:', selectedIndex);
|
|
||||||
if (dropdown.getAttribute('aria-expanded') === 'true') {
|
if (dropdown.getAttribute('aria-expanded') === 'true') {
|
||||||
if (e.key === 'ArrowDown') {
|
if (e.key === 'ArrowDown') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
selectedIndex = Math.min(selectedIndex + 1, dropdown.querySelectorAll('li').length - 1);
|
selectedIndex = Math.min(selectedIndex + 1, dropdown.querySelectorAll('li').length - 1);
|
||||||
updateHighlight();
|
updateHighlight();
|
||||||
dropdown.querySelector(`li:nth-child(${selectedIndex + 1})`).scrollIntoView({ block: 'nearest' });
|
dropdown.querySelector(`li:nth-child(${selectedIndex + 1})`)?.scrollIntoView({ block: 'nearest' });
|
||||||
} else if (e.key === 'ArrowUp') {
|
} else if (e.key === 'ArrowUp') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
selectedIndex = Math.max(selectedIndex - 1, 0);
|
selectedIndex = Math.max(selectedIndex - 1, 0);
|
||||||
updateHighlight();
|
updateHighlight();
|
||||||
dropdown.querySelector(`li:nth-child(${selectedIndex + 1})`).scrollIntoView({ block: 'nearest' });
|
dropdown.querySelector(`li:nth-child(${selectedIndex + 1})`)?.scrollIntoView({ block: 'nearest' });
|
||||||
} else if ((e.key === 'Enter' || e.key === 'Tab') && selectedIndex >= 0) {
|
} else if ((e.key === 'Enter' || e.key === 'Tab') && selectedIndex >= 0) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const selected = dropdown.querySelector(`li:nth-child(${selectedIndex + 1})`).textContent;
|
const selected = dropdown.querySelector(`li:nth-child(${selectedIndex + 1})`)?.textContent;
|
||||||
selectTag(selected); // Adds chip, refocuses
|
if (selected) selectTag(selected);
|
||||||
} else if (e.key === 'Escape') {
|
} else if (e.key === 'Escape') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
tagsField.blur();
|
tagsField.blur();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for no dropdown or non-selected Tab/Enter
|
|
||||||
if (!dropdown.getAttribute('aria-expanded') === 'true' || selectedIndex < 0) {
|
if (!dropdown.getAttribute('aria-expanded') === 'true' || selectedIndex < 0) {
|
||||||
if (e.key === 'Enter' || e.key === ',') {
|
if (e.key === 'Enter' || e.key === ',') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
addTag(tagsField.value, true);
|
addTag(tagsField.value, true);
|
||||||
} else if (e.key === 'Tab') {
|
} else if (e.key === 'Tab') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (tagsField.value.trim()) addTag(tagsField.value, true); // Add typed value, refocus
|
if (tagsField.value.trim()) addTag(tagsField.value, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
tagsField.addEventListener('keydown', keydownListener);
|
tagsField.addEventListener('keydown', keydownListener);
|
||||||
tagsListeners.push(() => tagsField.removeEventListener('keydown', keydownListener));
|
tagsListeners.push(() => tagsField.removeEventListener('keydown', keydownListener));
|
||||||
|
|
||||||
@ -159,21 +152,19 @@ tagsListeners.push(() => tagsField.removeEventListener('keydown', keydownListene
|
|||||||
tagsListeners.push(() => tagsField.removeEventListener('input', inputListener));
|
tagsListeners.push(() => tagsField.removeEventListener('input', inputListener));
|
||||||
|
|
||||||
const blurListener = () => {
|
const blurListener = () => {
|
||||||
setTimeout(() => dropdown.setAttribute('aria-expanded', 'false'), 150); // Hide after blur
|
setTimeout(() => dropdown.setAttribute('aria-expanded', 'false'), 150);
|
||||||
if (tagsField.value.trim()) addTag(tagsField.value);
|
if (tagsField.value.trim()) addTag(tagsField.value);
|
||||||
};
|
};
|
||||||
tagsField.addEventListener('blur', blurListener);
|
tagsField.addEventListener('blur', blurListener);
|
||||||
tagsListeners.push(() => tagsField.removeEventListener('blur', blurListener));
|
tagsListeners.push(() => tagsField.removeEventListener('blur', blurListener));
|
||||||
|
|
||||||
// Initial render
|
|
||||||
renderTags();
|
renderTags();
|
||||||
console.log('Tags ready with autocomplete, loaded:', tags.length, 'tags');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init on load + observe (unchanged)
|
// Init on load and observe tab changes
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const tagsTab = document.getElementById('content6');
|
const tagsTab = document.getElementById('content6');
|
||||||
if (tagsTab && tagsTab.classList.contains('active')) {
|
if (tagsTab?.classList.contains('active')) {
|
||||||
initTags();
|
initTags();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,4 +180,6 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
observer.observe(document.body, { subtree: true, attributes: true });
|
observer.observe(document.body, { subtree: true, attributes: true });
|
||||||
});
|
});
|
||||||
</script>
|
|
||||||
|
// Export switchTab for external use if needed
|
||||||
|
window.switchTab = switchTab;
|
||||||
@ -2,7 +2,7 @@
|
|||||||
<html class="no-js" lang="en">
|
<html class="no-js" lang="en">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{% include ['@override/head.html.twig', '@novaconium/head.html.twig'] %}
|
{% include ['@novaconium/cp/head.html.twig'] %}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="{{ pageid | default('pageid') }}" class="{{ pageclass | default('pageclass') }}" >
|
<body id="{{ pageid | default('pageid') }}" class="{{ pageclass | default('pageclass') }}" >
|
||||||
@ -28,8 +28,6 @@
|
|||||||
<div class="copyright">© {{ 'now' | date('Y') }} Novaconium</div>
|
<div class="copyright">© {{ 'now' | date('Y') }} Novaconium</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
{% include '@novaconium/javascript/page-edit.html.twig' %}
|
|
||||||
|
|
||||||
{% if editor == 'ace' %}
|
{% if editor == 'ace' %}
|
||||||
{% include '@novaconium/javascript/ace.html.twig' %}
|
{% include '@novaconium/javascript/ace.html.twig' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
60
twig/cp/head.html.twig
Normal file
60
twig/cp/head.html.twig
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
{# =============================================================================
|
||||||
|
<HEAD>
|
||||||
|
=============================================================================
|
||||||
|
#}
|
||||||
|
|
||||||
|
<meta charset="utf-8">
|
||||||
|
|
||||||
|
<title>{{ title | default('Welcome To Novaconium') }}</title>
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
{# SEO & METADATA #}
|
||||||
|
<meta name="generator" content="Novaconium" />
|
||||||
|
<meta name="description" content="{{ description | default('No description given') }}">
|
||||||
|
<meta name="keywords" content="{{ keywords | default('website') }}">
|
||||||
|
<meta name="author" content="{{ author | default('anonymous') }}">
|
||||||
|
|
||||||
|
{# DARK MODE & THEME HINTS #}
|
||||||
|
<meta name="color-scheme" content="dark">
|
||||||
|
<meta name="theme-color" content="#000000">
|
||||||
|
|
||||||
|
{# OPEN GRAPH (OG) FOR SOCIAL SHARING #}
|
||||||
|
<meta property="og:title" content="{{ title | default('Welcome') }}">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:url" content="{{ app_url | default(request.scheme ~ '://' ~ request.host) }}">
|
||||||
|
<meta property="og:image" content="{{ og_image | default('/icon.png') }}">
|
||||||
|
|
||||||
|
{# PWA & FAVICONS #}
|
||||||
|
<link rel="manifest" href="site.webmanifest">
|
||||||
|
<link rel="apple-touch-icon" href="/icon.png">
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
|
||||||
|
{# GOOGLE FONTS (CDN VIA PRECONNECT) #}
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link
|
||||||
|
href="{{ fonts | default('https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&family=Roboto+Mono&family=Source+Code+Pro&family=Lato&family=Poppins&family=Material+Icons&family=Material+Icons+Outlined&family=VT323:wght@400&family=Fira+Code:wght@400;500&display=swap') }}"
|
||||||
|
rel="stylesheet"
|
||||||
|
>
|
||||||
|
|
||||||
|
{% if editor == 'ace' %}
|
||||||
|
<!-- ACE Editor -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/ace.min.css">
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/ace.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/mode-html.min.js"></script> {# HTML syntax #}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/theme-tomorrow_night.min.js"></script> {# Dark theme #}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/ext-language_tools.min.js"></script> {# Autocomplete #}
|
||||||
|
<!-- END ACE Editor -->
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{# highlight.js #}
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||||
|
<script>hljs.highlightAll();</script>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/ir-black.min.css">
|
||||||
|
|
||||||
|
<script src="/js/tabs.js"></script>
|
||||||
|
|
||||||
|
{# STYLESHEET #}
|
||||||
|
<link rel="stylesheet" href="/css/novaconium.css">
|
||||||
@ -39,22 +39,7 @@
|
|||||||
>
|
>
|
||||||
|
|
||||||
{# STYLESHEET #}
|
{# STYLESHEET #}
|
||||||
{% if pageclass == "novaconium" %}
|
|
||||||
<link rel="stylesheet" href="/css/novaconium.css">
|
|
||||||
{% else %}
|
|
||||||
<link rel="stylesheet" href="/css/main.css">
|
<link rel="stylesheet" href="/css/main.css">
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
{% if editor == 'ace' %}
|
|
||||||
<!-- ACE Editor -->
|
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/ace.min.css">
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/ace.js"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/mode-html.min.js"></script> {# HTML syntax #}
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/theme-tomorrow_night.min.js"></script> {# Dark theme #}
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.35.2/ext-language_tools.min.js"></script> {# Autocomplete #}
|
|
||||||
<!-- END ACE Editor -->
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# highlight.js #}
|
{# highlight.js #}
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user