UNPKG

@govbr-ds/webcomponents

Version:

Biblioteca de Web Components baseado no GovBR-DS

260 lines (223 loc) 9.15 kB
const CONTENT_ELEMENT = document.getElementById('content') const COMPONENTS_FILTER = document.getElementById('components-filter') const CLEAR_COMPONENTS_FILTER = document.getElementById('clear-components-filter') let renderedFilesContent = {} let currentLoadedPath = null function removeRelatedScripts() { const existing = document.querySelectorAll('script[data-related-script="true"]') existing.forEach((el) => el.parentNode && el.parentNode.removeChild(el)) } function getBaseUrl() { // Se o Pages injetou um prefixo conhecido, usa-o (terminado com /) if (typeof window.WWW_PREFIX === 'string' && window.WWW_PREFIX.length > 0) { return window.WWW_PREFIX.replace(/\/+$/, '') } const baseUrl = window.location.pathname.split('/').slice(0, -1).join('/') return baseUrl === '' ? '' : baseUrl } function loadPage(path, files) { CONTENT_ELEMENT.innerHTML = '' renderedFilesContent = {} if (COMPONENTS_FILTER) COMPONENTS_FILTER.value = '' // Limpa o campo de busca ao trocar de página if (CLEAR_COMPONENTS_FILTER) CLEAR_COMPONENTS_FILTER.style.display = 'none' // Esconde o botão de limpar const baseUrl = getBaseUrl() // Remove scripts relacionados anteriores para evitar duplicação removeRelatedScripts() const promises = files.map((file) => fetch(`${baseUrl}/pages/components/${path}/${file}.html`).then((response) => response.text().then((text) => ({ file, text })) ) ) Promise.all(promises) .then((results) => { if (results.length > 0) { results.forEach(({ file, text }) => { const contentDiv = document.createElement('div') contentDiv.classList.add('file', 'mt-2', 'mb-5') const titleHTML = `<h3>${file}</h3><div class="br-divider"></div>` contentDiv.insertAdjacentHTML('beforeend', titleHTML + text) renderedFilesContent[file] = { content: text.toLowerCase(), element: contentDiv } CONTENT_ELEMENT.appendChild(contentDiv) }) } const relatedScript = document.createElement('script') relatedScript.src = `${getBaseUrl()}/pages/components/${path}/index.js` relatedScript.setAttribute('data-related-script', 'true') relatedScript.id = `related-script-${path}` document.body.appendChild(relatedScript) currentLoadedPath = path }) .catch((error) => { console.log('Erro ao carregar a página:', error) }) } function loadMarkdown(path) { CONTENT_ELEMENT.innerHTML = '' renderedFilesContent = {} if (COMPONENTS_FILTER) COMPONENTS_FILTER.value = '' // Limpa o campo de busca ao trocar de página if (CLEAR_COMPONENTS_FILTER) CLEAR_COMPONENTS_FILTER.style.display = 'none' // Esconde o botão de limpar const baseUrl = getBaseUrl() // Remove scripts relacionados anteriores para evitar duplicação removeRelatedScripts() fetch(`${baseUrl}/assets/stencil-generated-docs/${path}.md`) .then((response) => response.text()) .then((text) => { const contentDiv = document.createElement('div') contentDiv.classList.add('file', 'mt-2', 'mb-5') contentDiv.innerHTML = marked.parse(text) CONTENT_ELEMENT.appendChild(contentDiv) currentLoadedPath = path renderedFilesContent[path] = { content: contentDiv.innerHTML.toLowerCase(), element: contentDiv } }) .catch((error) => { console.error('Erro ao carregar a página:', error) }) } function filterContent() { const filterValue = (COMPONENTS_FILTER?.value || '').toLowerCase() const normalizedFilterValue = filterValue.normalize('NFD').replace(/[\u0300-\u036f]/g, '') Object.values(renderedFilesContent).forEach(({ content, element }) => { const titleElement = element.querySelector('h3') const titleText = titleElement ? titleElement.textContent.toLowerCase() : '' const normalizedTitleText = titleText.normalize('NFD').replace(/[\u0300-\u036f]/g, '') // Inicializa o conteúdo do shadow DOM para componentes personalizados let shadowContent = '' // Função para coletar o conteúdo dos slots de um custom element const collectSlotContent = (customElement) => { if (customElement.shadowRoot) { const slots = customElement.shadowRoot.querySelectorAll('slot') slots.forEach((slot) => { const assignedNodes = slot.assignedNodes() assignedNodes.forEach((node) => { shadowContent += node.textContent.toLowerCase() + ' ' }) }) } } // Procura todos os custom elements dentro do elemento const customElements = element.querySelectorAll('*') customElements.forEach((customElement) => { if (customElement.tagName.includes('-')) { // Identifica se é um custom element collectSlotContent(customElement) } }) // Filtra o conteúdo const combinedContent = content + ' ' + shadowContent // Combina conteúdo e shadow content if (element.querySelector('table')) { const rows = element.querySelectorAll('table tr') rows.forEach((row, index) => { if (index === 0) { row.style.display = '' } else { const rowText = row.textContent .toLowerCase() .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') row.style.display = rowText.includes(normalizedFilterValue) ? '' : 'none' } }) } else if ( normalizedFilterValue && (combinedContent.includes(normalizedFilterValue) || normalizedTitleText.includes(normalizedFilterValue)) ) { element.style.display = '' // Exibe o elemento se o conteúdo combinado inclui o valor filtrado } else { element.style.display = normalizedFilterValue ? 'none' : '' // Oculta se não houver filtro } }) if (CLEAR_COMPONENTS_FILTER) CLEAR_COMPONENTS_FILTER.style.display = filterValue ? 'block' : 'none' } // Liga eventos apenas se os elementos existirem if (COMPONENTS_FILTER) { COMPONENTS_FILTER.addEventListener('input', filterContent) } if (CLEAR_COMPONENTS_FILTER) { CLEAR_COMPONENTS_FILTER.addEventListener('click', () => { if (COMPONENTS_FILTER) COMPONENTS_FILTER.value = '' filterContent() }) } function loadPageFromURL() { const baseUrl = getBaseUrl() const currentPath = window.location.pathname.replace(baseUrl, '').split('/').filter(Boolean)[0] || '' // Evita recarregar a mesma rota em chamadas repetidas if (currentPath === currentLoadedPath) { return } const menuItems = Array.isArray(window.MENU_ITEMS) ? window.MENU_ITEMS : [] const menuBody = typeof window.MENU_BODY !== 'undefined' ? window.MENU_BODY : null // Remove seleções antigas if (menuBody) Array.from(menuBody.children).forEach((item) => item.classList.remove('active')) menuItems.forEach((page) => { if (!page || !page.path) return const link = menuBody ? Array.from(menuBody.children).find((item) => item.getAttribute('href') === `/${page.path}`) : null if (page.path === currentPath) { if (link) { link.classList.add('active') } if (Array.isArray(page.files)) { loadPage(page.path, page.files) } } }) const staticItems = window.MENU_STATIC_ITEMS if (Array.isArray(staticItems)) { staticItems.forEach((page) => { if (!page || !page.path) return const link = menuBody ? Array.from(menuBody.children).find((item) => item.getAttribute('href') === `/${page.path}`) : null if (page.path === currentPath) { if (link) { link.classList.add('active') } loadMarkdown(page.path) } }) } if (currentPath.trim().length === 0 && menuItems.length > 0) { const firstPage = menuItems.find((p) => p && p.path && Array.isArray(p.files)) if (firstPage) { loadPage(firstPage.path, firstPage.files) const firstLink = menuBody ? menuBody.querySelector('.menu-item') : null if (firstLink) { firstLink.classList.add('active') window.history.pushState({}, '', `${baseUrl}/${firstPage.path}`) } } } } // Aguarda DOM e dados do menu antes de tentar carregar a rota atual function isMenuReady() { const hasItems = (Array.isArray(window.MENU_ITEMS) && window.MENU_ITEMS.length > 0) || (Array.isArray(window.MENU_STATIC_ITEMS) && window.MENU_STATIC_ITEMS.length > 0) return !!CONTENT_ELEMENT && hasItems } function bootstrapRouting(attempt = 0) { const maxAttempts = 200 // ~10s com 50ms if (!isMenuReady()) { if (attempt < maxAttempts) { setTimeout(() => bootstrapRouting(attempt + 1), 50) } else { // Última tentativa mesmo sem menu (evita ficar em branco caso os itens não sejam necessários) try { loadPageFromURL() } catch (err) { console.warn('Falha ao carregar rota sem menu pronto:', err) } } return } loadPageFromURL() } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => bootstrapRouting()) } else { bootstrapRouting() } // Garante atualização ao navegar com back/forward window.addEventListener('popstate', () => bootstrapRouting())