Skip to content
Extensões

Mind Map

Mapa mental com no central, ramos radiais de 2 niveis, zoom/pan/drag interativo e layout radial automatico. Componente React Island baseado em @xyflow/react.

components/mind-map.cssislands/src/islands/MindMap/Abrir no Figma

Uso

Quando e como utilizar o mapa mental para visualizar hierarquias radiais de ate 2 niveis de profundidade.

  • JSON data — Carrega a estrutura do mapa de um endpoint JSON via atributo data-props no container. O componente faz fetch automatico ao montar.
  • Inline data — Dados passados diretamente no atributo data-props como JSON serializado, sem fetch externo.
  • Programmatic — Monta a ilha manualmente via ArenitoIslands.mount() passando o elemento container e as props como objeto JavaScript.
Este e um React Island que requer o script arenito-islands.js carregado na pagina. O bundle inclui @xyflow/react e o algoritmo de layout radial. Sem o script, o container permanece vazio.

Exemplos

Veja abaixo os exemplos interativos do mapa mental, incluindo expand/collapse, layout radial e dark mode.

Carregando exemplo...

Anatomia

Estrutura interna do componente e seus elementos.

ParteElementoDescricao
Container.mind-mapWrapper principal com position relative e width 100%
Canvas.mind-map__canvasArea do ReactFlow com zoom, pan e drag
Node.mind-map__nodeNo base circular com padding 12px
Root.mind-map__node--rootNo central 80x80, background brand, texto branco
Category.mind-map__node--categoryNo nivel 1, 60x60, cor configuravel por categoria
Leaf.mind-map__node--leafNo nivel 2, 44x44, estilo mais sutil e leve
Collapsed.mind-map__node--collapsedModificador para nos com filhos ocultos, borda dashed
Node Label.mind-map__node-labelTexto do no, Inter 12px w500, centralizado
Node Icon.mind-map__node-iconIcone SVG 16x16 acima do label
Node Badge.mind-map__node-badgeBadge numerico no canto superior direito
Edge.mind-map__edgeAresta bezier com stroke 2px
Edge Level 1.mind-map__edge--level-1Aresta root→categoria, stroke 2.5px
Edge Level 2.mind-map__edge--level-2Aresta categoria→folha, stroke 1.5px, opacity 0.6
Controls.mind-map__controlsBotoes de zoom no canto inferior esquerdo
Vazio.mind-map__emptyEstado sem dados, borda dashed e padding 48px

Layout Radial

O algoritmo de layout radial distribui os nos automaticamente ao redor do no central.

O Mind Map utiliza um algoritmo de layout radial que posiciona os nos em circulos concentricos ao redor do no raiz. O calculo de posicao segue estas regras:

  1. O no raiz e posicionado no centro do canvas (0, 0).
  2. Os nos de categoria (nivel 1) sao distribuidos uniformemente em um circulo de raio R1 = 200px. O angulo de cada categoria e calculado por:
js
const angle = (index / totalCategories) * 2 * Math.PI;
const x = Math.cos(angle) * R1;
const y = Math.sin(angle) * R1;
  1. Os nos folha (nivel 2) sao distribuidos em um arco ao redor de sua categoria pai, com raio R2 = 120px. O arco e centrado na direcao do pai em relacao ao root:
js
const parentAngle = Math.atan2(parentY, parentX);
const spread = Math.PI / 3; // 60 graus de arco
const leafAngle = parentAngle - spread / 2 + (leafIndex / (totalLeaves - 1)) * spread;

Quando o numero de categorias muda (por expand/collapse), o layout e recalculado com uma animacao de transicao de 300ms usando requestAnimationFrame.

Expand/Collapse

Clicar em um no de categoria alterna a visibilidade dos seus nos folha.

O comportamento de expand/collapse funciona da seguinte forma:

  • Clicar em um no de categoria (.mind-map__node--category) alterna o estado collapsed no state interno do React.
  • Quando colapsado, o no recebe o modificador --collapsed (borda tracejada) e seus nos folha e arestas de nivel 2 sao removidos do canvas.
  • O badge (.mind-map__node-badge) exibe a contagem de filhos ocultos quando colapsado (ex: "3").
  • O layout radial e recalculado apos cada toggle para redistribuir o espaco entre os nos visiveis.
  • A transicao usa opacity e transform: scale nos nos folha com transition: all 200ms ease-out.

Por padrao, todos os nos iniciam expandidos. Para iniciar com nos colapsados, passe collapsed: true na propriedade data de cada categoria no JSON.

Eventos

CustomEvents disparados pelo componente para integracao com o monolito PHP.

O Mind Map emite CustomEvents no elemento container para que o codigo do monolito possa reagir a acoes do usuario sem acoplar-se ao React.

EventodetailQuando dispara
arenito:mindmap:node-click{ nodeId: string, level: number, data: object }Usuario clica em qualquer no do mapa
js
document.querySelector('[data-island="MindMap"]')
  .addEventListener('arenito:mindmap:node-click', (e) => {
    console.log('Node:', e.detail.nodeId);
    console.log('Level:', e.detail.level);
  });

Propriedades

Classes CSS disponiveis para configurar o mapa mental.

PropriedadeTipoDefaultDescrição
.mind-mapclasseContainer principal do mapa mental.
.mind-map__canvasclasseArea do ReactFlow com zoom, pan e drag interativo.
.mind-map__nodeclasseNo base do mapa com border-radius 50% e padding 12px.
.mind-map__node--rootclasseNo central (raiz) com tamanho maior (80x80) e background brand.
.mind-map__node--categoryclasseNo de categoria (nivel 1) com 60x60 e cor configuravel.
.mind-map__node--leafclasseNo folha (nivel 2) com 44x44 e estilo mais sutil.
.mind-map__node--collapsedclasseModificador para no com filhos colapsados (borda tracejada).
.mind-map__node-labelclasseLabel de texto do no (12px w500, text-align center).
.mind-map__node-iconclasseIcone opcional dentro do no (16x16, centralizado).
.mind-map__node-badgeclasseBadge numerico no canto do no de categoria (contagem de filhos).
.mind-map__edgeclasseAresta curva (bezier) conectando nos com stroke 2px.
.mind-map__edge--level-1classeAresta do root para categoria (stroke 2.5px, cor do no destino).
.mind-map__edge--level-2classeAresta de categoria para folha (stroke 1.5px, opacity 0.6).
.mind-map__controlsclasseControles de zoom posicionados no canto inferior esquerdo.
.mind-map__emptyclasseEstado vazio com borda tracejada e texto centralizado.

Codigo

Snippets prontos para copiar e usar no seu projeto.

HTML com data-island e data-props

html
<div data-island="MindMap"
     data-props='{
       "rootLabel": "Gestao Ambiental",
       "categories": [
         {
           "id": "cat-1",
           "label": "Monitoramento",
           "color": "#3B82F6",
           "items": [
             { "id": "leaf-1", "label": "Qualidade da Agua" },
             { "id": "leaf-2", "label": "Emissoes Atmosfericas" },
             { "id": "leaf-3", "label": "Residuos Solidos" }
           ]
         },
         {
           "id": "cat-2",
           "label": "Licenciamento",
           "color": "#10B981",
           "items": [
             { "id": "leaf-4", "label": "LP" },
             { "id": "leaf-5", "label": "LI" },
             { "id": "leaf-6", "label": "LO" }
           ]
         },
         {
           "id": "cat-3",
           "label": "Fiscalizacao",
           "color": "#F59E0B",
           "items": [
             { "id": "leaf-7", "label": "Vistorias" },
             { "id": "leaf-8", "label": "Autuacoes" }
           ]
         }
       ]
     }'>
</div>

Montagem programatica via ArenitoIslands.mount()

js
const container = document.getElementById('mindmap-container');

ArenitoIslands.mount(container, 'MindMap', {
  rootLabel: 'Planejamento Estrategico',
  categories: [
    {
      id: 'cat-1',
      label: 'Objetivos',
      color: '#8B5CF6',
      items: [
        { id: 'leaf-1', label: 'Reducao de custos' },
        { id: 'leaf-2', label: 'Expansao de cobertura' }
      ]
    },
    {
      id: 'cat-2',
      label: 'Indicadores',
      color: '#3B82F6',
      items: [
        { id: 'leaf-3', label: 'OKR Q1' },
        { id: 'leaf-4', label: 'KPI Operacional' }
      ]
    },
    {
      id: 'cat-3',
      label: 'Riscos',
      color: '#EF4444',
      collapsed: true,
      items: [
        { id: 'leaf-5', label: 'Orcamentario' },
        { id: 'leaf-6', label: 'Regulatorio' },
        { id: 'leaf-7', label: 'Operacional' }
      ]
    }
  ]
});

Integracao com Factory (PHP)

php
<div id="mindmap-areas"
     data-island="MindMap"
     data-endpoint="/api/mindmap/areas/<?= $organizacao_id ?>">
</div>

<script src="/assets/arenito-islands.js"></script>

Fonte de Dados

O componente aceita dados em JSON com um label raiz e array de categorias contendo itens.

JSON (formato recomendado)

O objeto JSON raiz contem o rootLabel para o no central e um array categories com as ramificacoes de nivel 1, cada uma contendo um array items para os nos folha.

json
{
  "rootLabel": "Gestao Ambiental",
  "categories": [
    {
      "id": "cat-1",
      "label": "Monitoramento",
      "color": "#3B82F6",
      "icon": "chart-line",
      "items": [
        { "id": "leaf-1", "label": "Qualidade da Agua" },
        { "id": "leaf-2", "label": "Emissoes Atmosfericas" },
        { "id": "leaf-3", "label": "Residuos Solidos" }
      ]
    },
    {
      "id": "cat-2",
      "label": "Licenciamento",
      "color": "#10B981",
      "icon": "file-check",
      "collapsed": false,
      "items": [
        { "id": "leaf-4", "label": "Licenca Previa" },
        { "id": "leaf-5", "label": "Licenca de Instalacao" },
        { "id": "leaf-6", "label": "Licenca de Operacao" }
      ]
    }
  ]
}

Mapeamento de campos

CampoTipoObrigatorioDescricao
rootLabelstringSimTexto exibido no no central (raiz) do mapa
categoriesarraySimArray de categorias (nos de nivel 1)
idstringSimIdentificador unico da categoria ou item
labelstringSimTexto exibido no no
colorstringSim (cat)Cor hexadecimal do no de categoria e suas arestas
iconstringNaoNome do icone exibido dentro do no de categoria
collapsedbooleanNaoSe true, inicia com filhos ocultos (padrao: false)
itemsarraySim (cat)Array de nos folha pertencentes a categoria

Dark Mode

O mapa mental adapta automaticamente ao tema escuro via classe .dark.

No dark mode, o canvas recebe um fundo escuro e os nos ajustam suas cores de borda e fill para manter contraste. As arestas reduzem levemente a opacidade para evitar excesso de destaque.

PropriedadeLightDark
Canvas background--surface-base-neutral-container-default--surface-base-neutral-container-default
Root node background--accent-500--accent-400
Root node text#FFFFFF#FFFFFF
Category node borderrgba(0,0,0,0.12)rgba(255,255,255,0.12)
Category node backgroundCor da categoria com opacity 0.15Cor da categoria com opacity 0.20
Leaf node background--surface-base-neutral-container-emphasis--surface-base-neutral-container-emphasis
Leaf node text--text-base-neutral-default--text-base-neutral-default
Edge level 1 strokeCor da categoria destinoCor da categoria destino com opacity 0.8
Edge level 2 stroke--border-base-neutral-default--border-base-neutral-default
Controls background--surface-base-neutral-container-default--surface-base-neutral-container-default
Badge background--surface-base-neutral-container-emphasis--surface-base-neutral-container-emphasis

Os tokens semanticos de superficie e texto herdam automaticamente os valores do dark mode. As cores das categorias sao mantidas em ambos os temas, com leve ajuste de opacidade no fundo dos nos para garantir contraste adequado.

Design System interno do GALES