0). */ function bo_nav_link(string $href, string $icon, string $label, array $o = []): string { $active = array_key_exists('active', $o) ? (bool) $o['active'] : bo_nav_active($o['match'] ?? strtok($href, '?')); $mod = isset($o['mod']) ? ' bo-nav-item--' . $o['mod'] : ''; $l = htmlspecialchars($label, ENT_QUOTES, 'UTF-8'); $badge = (int) ($o['badge'] ?? 0); return '' . '' . '' . $l . '' . ($badge > 0 ? '' . $badge . '' : '') . '' . "\n"; } /** * Abre um grupo recolhível. O conteúdo fica em buffer até bo_nav_group_end(), * que precisa de saber se algum item é o ativo (o grupo da página atual abre * sempre). $sub = subcategoria dentro de outro grupo (recuada). */ function bo_nav_group_start(string $key, string $label, string $icon, bool $sub = false): void { $GLOBALS['bo_nav_group_stack'][] = [$key, $label, $icon, $sub]; ob_start(); } /** Fecha o grupo aberto por bo_nav_group_start() e escreve o markup final. */ function bo_nav_group_end(): void { $items = (string) ob_get_clean(); [$key, $label, $icon, $sub] = array_pop($GLOBALS['bo_nav_group_stack']); $hasActive = str_contains($items, 'aria-current="page"'); $id = 'bo-grp-' . $key; $k = htmlspecialchars($key, ENT_QUOTES, 'UTF-8'); $l = htmlspecialchars($label, ENT_QUOTES, 'UTF-8'); $i = htmlspecialchars($icon, ENT_QUOTES, 'UTF-8'); $short = htmlspecialchars(mb_strtoupper(mb_substr($label, 0, 4)), ENT_QUOTES, 'UTF-8'); echo '
' . '' . '
' . $items . '
' . '
' . "\n"; } /** Iniciais para o avatar (até duas letras: primeiro e último nome). */ function bo_initials(string $name): string { // Só palavras que começam por letra: «Staff (dev)» dá «SD»... não «S(». preg_match_all('/\p{L}[\p{L}\p{M}-]*/u', $name, $m); $parts = $m[0]; if (!$parts) return '?'; $first = mb_substr($parts[0], 0, 1); $last = count($parts) > 1 ? mb_substr($parts[count($parts) - 1], 0, 1) : ''; return mb_strtoupper($first . $last); }