UNPKG

com.wallstop-studios.unity-helpers

Version:

Treasure chest of Unity developer tools

267 lines (231 loc) 9.85 kB
<!-- Custom head content for Unity Helpers documentation --> <!-- Theme CSS is imported via style.scss (@import url("theme.css")). Adding a link tag here would load theme.css twice. --> <!-- Theme initialization - runs before page renders to prevent flash --> <script> (function () { // Check for saved theme preference or default to dark const savedTheme = localStorage.getItem("unity-helpers-theme"); const theme = savedTheme || "dark"; document.documentElement.setAttribute("data-theme", theme); })(); </script> <!-- Mermaid diagrams with theme support --> {% include mermaid.html %} <style> /* Mermaid diagram container - minimal styling, relies on built-in themes */ .mermaid { text-align: center; margin: 1em 0; } /* Ensure mermaid SVGs are responsive */ .mermaid svg { max-width: 100%; height: auto; } </style> <!-- Sortable tables (Issue #126) --> <!-- Note: Tables with rowspan or colspan attributes may not sort correctly because the sorting logic assumes a simple grid structure where each row has the same number of cells aligned to column headers. --> <script> document.addEventListener("DOMContentLoaded", () => { // Initialize sortable tables const sortableTables = document.querySelectorAll("table[data-sortable]"); sortableTables.forEach((table) => { const headers = table.querySelectorAll("th"); headers.forEach((header, columnIndex) => { header.setAttribute("role", "columnheader"); header.setAttribute("tabindex", "0"); header.setAttribute("aria-sort", "none"); // Click handler header.addEventListener("click", () => { sortTableByColumn(table, columnIndex, header); }); // Keyboard handler for accessibility header.addEventListener("keydown", (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); sortTableByColumn(table, columnIndex, header); } }); }); }); function sortTableByColumn(table, columnIndex, header) { let tbody = table.querySelector("tbody"); if (!tbody) { // If no tbody, create one from tr elements const rows = Array.from(table.querySelectorAll("tr")); if (rows.length <= 1) return; tbody = document.createElement("tbody"); const headerRow = rows.shift(); const thead = document.createElement("thead"); thead.appendChild(headerRow); rows.forEach((row) => { tbody.appendChild(row); }); table.innerHTML = ""; table.appendChild(thead); table.appendChild(tbody); } const rows = Array.from(tbody.querySelectorAll("tr")); if (rows.length === 0) return; // Determine current sort direction const currentDir = table.dataset.sortDir || "none"; const currentCol = table.dataset.sortCol; let newDir; if (currentCol === String(columnIndex)) { // Toggle direction on same column newDir = currentDir === "asc" ? "desc" : "asc"; } else { // New column, default to ascending newDir = "asc"; } // Update sort state table.dataset.sortDir = newDir; table.dataset.sortCol = columnIndex; // Reset all header aria-sort attributes const allHeaders = table.querySelectorAll("th"); allHeaders.forEach((h) => { h.setAttribute("aria-sort", "none"); // Remove existing sort indicators const indicator = h.querySelector(".sort-indicator"); if (indicator) { indicator.textContent = ""; } }); // Update current header header.setAttribute("aria-sort", newDir === "asc" ? "ascending" : "descending"); // Add or update sort indicator let indicator = header.querySelector(".sort-indicator"); if (!indicator) { indicator = document.createElement("span"); indicator.className = "sort-indicator"; indicator.setAttribute("aria-hidden", "true"); header.appendChild(indicator); } indicator.textContent = newDir === "asc" ? " \u25B2" : " \u25BC"; // Sort the rows rows.sort((rowA, rowB) => { const cellA = rowA.cells[columnIndex]; const cellB = rowB.cells[columnIndex]; if (!cellA || !cellB) return 0; const valueA = getSortableCellValue(cellA); const valueB = getSortableCellValue(cellB); // Try to parse as numbers (handle commas, percentages, units) const numA = parseNumericValue(valueA); const numB = parseNumericValue(valueB); let comparison; if (numA !== null && numB !== null) { // Numeric comparison comparison = numA - numB; } else { // String comparison (case-insensitive) comparison = valueA.toLowerCase().localeCompare(valueB.toLowerCase()); } return newDir === "asc" ? comparison : -comparison; }); // Re-append sorted rows rows.forEach((row) => { tbody.appendChild(row); }); } function getSortableCellValue(cell) { return (cell.getAttribute("data-sort-value") || cell.textContent || "").trim(); } function parseNumericValue(value) { if ( value === "" || value === "n/a" || value === "N/A" || value === "-" || value === "pending" || value === "Pending" ) { return null; } // Remove common formatting characters, preserving negative sign // Handle negative numbers with commas like "-1,234" const cleaned = value .replace(/,/g, "") // Remove thousands separators .replace(/\s+/g, " ") // Normalize whitespace .trim(); // Handle percentages/multipliers (e.g., "0.95x", "121.48x", "-0.5x") const multiplierMatch = cleaned.match(/^(-?[\d.]+)x$/i); if (multiplierMatch) { return parseFloat(multiplierMatch[1]); } // Handle time values (e.g., "0.030 ms", "2.13 s", "1.12 s") const timeMatch = cleaned.match(/^(-?[\d.]+)\s*(ms|s)$/i); if (timeMatch) { const timeValue = parseFloat(timeMatch[1]); const unit = timeMatch[2].toLowerCase(); // Convert to milliseconds for consistent comparison return unit === "s" ? timeValue * 1000 : timeValue; } // Handle ops/sec values (e.g., "1,309,300,000", "63.5M", "868.3K", "-5K") const suffixMatch = cleaned.match(/^(-?[\d.]+)\s*([KMB])?$/i); if (suffixMatch) { let numValue = parseFloat(suffixMatch[1]); const suffix = (suffixMatch[2] || "").toUpperCase(); if (suffix === "K") numValue *= 1000; else if (suffix === "M") numValue *= 1000000; else if (suffix === "B") numValue *= 1000000000; if (!isNaN(numValue)) return numValue; } // Handle parenthetical time values (e.g., "3 (0.254s)") const parenMatch = cleaned.match(/^(-?\d+)\s*\([\d.]+s\)$/); if (parenMatch) { return parseInt(parenMatch[1], 10); } // Try direct numeric parse const directNum = parseFloat(cleaned); if (!isNaN(directNum)) { return directNum; } return null; } }); </script> <!-- Theme toggle button (injected into body via JS) --> <script> document.addEventListener("DOMContentLoaded", () => { // Create theme toggle button const toggle = document.createElement("button"); toggle.className = "theme-toggle"; toggle.setAttribute("aria-label", "Toggle theme"); toggle.setAttribute("title", "Toggle light/dark theme"); toggle.innerHTML = ` <svg class="sun-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58a.996.996 0 0 0-1.41 0 .996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37a.996.996 0 0 0-1.41 0 .996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0a.996.996 0 0 0 0-1.41l-1.06-1.06zm1.06-10.96a.996.996 0 0 0 0-1.41.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36a.996.996 0 0 0 0-1.41.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"/> </svg> <svg class="moon-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> <path d="M12 3a9 9 0 1 0 9 9c0-.46-.04-.92-.1-1.36a5.389 5.389 0 0 1-4.4 2.26 5.403 5.403 0 0 1-3.14-9.8c-.44-.06-.9-.1-1.36-.1z"/> </svg> <span>Theme</span> `; // Add click handler toggle.addEventListener("click", () => { const currentTheme = document.documentElement.getAttribute("data-theme"); const newTheme = currentTheme === "dark" ? "light" : "dark"; document.documentElement.setAttribute("data-theme", newTheme); localStorage.setItem("unity-helpers-theme", newTheme); // Update mermaid theme if diagrams exist // Check for window.updateMermaidTheme (mermaid is module-scoped, not global) if (typeof window.updateMermaidTheme === "function" && document.querySelector(".mermaid")) { window.updateMermaidTheme(newTheme); } }); // Insert toggle after header if one exists, otherwise at start of body const header = document.querySelector("header"); if (header && header.nextSibling) { header.parentNode.insertBefore(toggle, header.nextSibling); } else { document.body.insertBefore(toggle, document.body.firstChild); } }); </script>