@@ -0,0 +1,2 @@
|
|||||||
|
import * as header from "./components/header.js";
|
||||||
|
import * as sidebar from "./components/sidebar.js";
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
class Header extends HTMLElement {
|
||||||
|
|
||||||
|
constructor(){
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.innerHTML = `
|
||||||
|
<div class="header-box">
|
||||||
|
<a class="logo" href="/index.html">Spearhead</a>
|
||||||
|
<div class="header-right">
|
||||||
|
<nav>
|
||||||
|
<a href="/index.html">Home</a>
|
||||||
|
|
||||||
|
<div class="dropdown">
|
||||||
|
<a href="/pages/tutorials.html">Tutorials</a>
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<a href="/pages/tutorials.html">Quick Starts</a>
|
||||||
|
<a href="/pages/advanced/CAP.html">Advanced: CAP</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<a href="/pages/persistence.html">Persistence</a>
|
||||||
|
<a href="/pages/reference.html">Reference</a>
|
||||||
|
<a href="/pages/spearheadapi.html">API</a>
|
||||||
|
</nav>
|
||||||
|
<button id="theme-toggle" class="theme-toggle" title="Toggle light/dark theme">
|
||||||
|
<svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<path d="M12 11.807A9.002 9.002 0 0 1 10.049 2a9.942 9.942 0 0 0-5.12 2.735c-3.905 3.905-3.905 10.237 0 14.142 3.906 3.906 10.237 3.905 14.143 0a9.946 9.946 0 0 0 2.735-5.119A9.003 9.003 0 0 1 12 11.807z"/>
|
||||||
|
</svg>
|
||||||
|
<svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" style="display: none;">
|
||||||
|
<path d="M6.995 12c0 2.761 2.246 5.007 5.007 5.007s5.007-2.246 5.007-5.007-2.246-5.007-5.007-5.007S6.995 9.239 6.995 12zM12 8.993c1.658 0 3.007 1.349 3.007 3.007S13.658 15.007 12 15.007 8.993 13.658 8.993 12 10.342 8.993 12 8.993zM10.998 19H12.998V22H10.998zM10.998 2H12.998V5H10.998zM1.998 11H4.998V13H1.998zM18.998 11H21.998V13H18.998z"/>
|
||||||
|
<path transform="rotate(-45.017 5.986 18.01)" d="M4.986 17.01H6.986V19.01H4.986z"/>
|
||||||
|
<path transform="rotate(-45.001 18.008 5.99)" d="M17.008 4.99H19.008V6.99H17.008z"/>
|
||||||
|
<path transform="rotate(-134.983 5.988 5.99)" d="M4.988 4.99H6.988V6.99H4.988z"/>
|
||||||
|
<path transform="rotate(134.999 18.008 18.01)" d="M17.008 17.01H19.008V19.01H17.008z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('app-header', Header);
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
class Sidebar extends HTMLElement {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.navItems = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
connectedCallback() {
|
||||||
|
this.render();
|
||||||
|
this.scanHeaders();
|
||||||
|
this.setupScrollListener();
|
||||||
|
this.highlightActiveSection();
|
||||||
|
}
|
||||||
|
|
||||||
|
scanHeaders() {
|
||||||
|
// Clear existing nav items
|
||||||
|
this.navItems = [];
|
||||||
|
|
||||||
|
// Find all h2 and h3 elements with IDs in the content area
|
||||||
|
const contentWrapper = document.querySelector('.content-wrapper');
|
||||||
|
if (!contentWrapper) return;
|
||||||
|
|
||||||
|
const headers = contentWrapper.querySelectorAll('h2[id], h3[id]');
|
||||||
|
|
||||||
|
headers.forEach(header => {
|
||||||
|
const id = header.getAttribute('id');
|
||||||
|
const text = header.textContent.trim();
|
||||||
|
const level = header.tagName.toLowerCase();
|
||||||
|
|
||||||
|
this.navItems.push({
|
||||||
|
id,
|
||||||
|
text,
|
||||||
|
level,
|
||||||
|
element: header
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.renderNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
|
renderNavigation() {
|
||||||
|
const ul = this.querySelector('ul');
|
||||||
|
if (!ul) return;
|
||||||
|
|
||||||
|
// Clear existing content
|
||||||
|
ul.innerHTML = '';
|
||||||
|
|
||||||
|
let currentH2Li = null;
|
||||||
|
|
||||||
|
this.navItems.forEach(item => {
|
||||||
|
if (item.level === 'h2') {
|
||||||
|
// Create h2 item
|
||||||
|
const li = document.createElement('li');
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = `#${item.id}`;
|
||||||
|
a.className = 'side-nav-h2';
|
||||||
|
a.textContent = item.text;
|
||||||
|
a.addEventListener('click', (e) => this.handleNavClick(e, item.id));
|
||||||
|
|
||||||
|
li.appendChild(a);
|
||||||
|
ul.appendChild(li);
|
||||||
|
currentH2Li = li;
|
||||||
|
} else if (item.level === 'h3' && currentH2Li) {
|
||||||
|
// Create h3 item under current h2
|
||||||
|
let subUl = currentH2Li.querySelector('ul');
|
||||||
|
if (!subUl) {
|
||||||
|
subUl = document.createElement('ul');
|
||||||
|
currentH2Li.appendChild(subUl);
|
||||||
|
}
|
||||||
|
|
||||||
|
const li = document.createElement('li');
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = `#${item.id}`;
|
||||||
|
a.className = 'side-nav-h3';
|
||||||
|
a.textContent = item.text;
|
||||||
|
a.addEventListener('click', (e) => this.handleNavClick(e, item.id));
|
||||||
|
|
||||||
|
li.appendChild(a);
|
||||||
|
subUl.appendChild(li);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleNavClick(e, targetId) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
// Remove active class from all links
|
||||||
|
this.querySelectorAll('a').forEach(link => {
|
||||||
|
link.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add active class to clicked link
|
||||||
|
e.target.classList.add('active');
|
||||||
|
|
||||||
|
// Smooth scroll to target
|
||||||
|
const targetElement = document.getElementById(targetId);
|
||||||
|
if (targetElement) {
|
||||||
|
targetElement.scrollIntoView({
|
||||||
|
behavior: 'smooth',
|
||||||
|
block: 'start'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupScrollListener() {
|
||||||
|
let ticking = false;
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
if (!ticking) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
this.highlightActiveSection();
|
||||||
|
ticking = false;
|
||||||
|
});
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener('scroll', handleScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
highlightActiveSection() {
|
||||||
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||||||
|
const offset = 100; // Offset for highlighting
|
||||||
|
|
||||||
|
let activeId = '';
|
||||||
|
|
||||||
|
// Find the currently visible section
|
||||||
|
this.navItems.forEach(item => {
|
||||||
|
const element = item.element;
|
||||||
|
const rect = element.getBoundingClientRect();
|
||||||
|
const elementTop = rect.top + scrollTop;
|
||||||
|
|
||||||
|
if (elementTop <= scrollTop + offset) {
|
||||||
|
activeId = item.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update active state
|
||||||
|
this.querySelectorAll('a').forEach(link => {
|
||||||
|
link.classList.remove('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (activeId) {
|
||||||
|
const activeLink = this.querySelector(`a[href="#${activeId}"]`);
|
||||||
|
if (activeLink) {
|
||||||
|
activeLink.classList.add('active');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
this.innerHTML = `
|
||||||
|
<div class="side-nav">
|
||||||
|
<h4 class="side-nav-title"></h4>
|
||||||
|
<ul>
|
||||||
|
<!-- Navigation items will be populated automatically -->
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to refresh the sidebar when content changes
|
||||||
|
refresh() {
|
||||||
|
this.scanHeaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method to set the sidebar title
|
||||||
|
setTitle(title) {
|
||||||
|
const titleElement = this.querySelector('.side-nav-title');
|
||||||
|
if (titleElement) {
|
||||||
|
titleElement.textContent = title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the custom element
|
||||||
|
customElements.define('app-sidebar', Sidebar);
|
||||||
|
|
||||||
|
export default Sidebar;
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" data-theme="dark">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Spearhead Persistence</title>
|
||||||
|
<link rel="stylesheet" href="/style/style.css">
|
||||||
|
<script src="/js/site.js"></script>
|
||||||
|
<style>
|
||||||
|
.side-nav a.active {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #4fc3f7;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script type="module" src="/js/components.js"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<app-header></app-header>
|
||||||
|
</header>
|
||||||
|
<main>
|
||||||
|
<div class="reference-container">
|
||||||
|
<app-sidebar></app-sidebar>
|
||||||
|
|
||||||
|
<div class="content-wrapper">
|
||||||
|
<h1>Advanced Tutorial: CAP</h1>
|
||||||
|
<p style="font-size: 24px;">
|
||||||
|
<b>You're a little early! <br>
|
||||||
|
We're still working on this tutorial, but it will be available soon.</b>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 Spearhead Project</p>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
```
|
||||||
@@ -306,6 +306,8 @@ function CapBase:CheckAndScheduleIntercept()
|
|||||||
|
|
||||||
local interceptZoneIDs = {}
|
local interceptZoneIDs = {}
|
||||||
|
|
||||||
|
local interceptZoneIDs = {}
|
||||||
|
|
||||||
local airbase = Airbase.getByName(self.airbaseName)
|
local airbase = Airbase.getByName(self.airbaseName)
|
||||||
if not airbase then
|
if not airbase then
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user