Pular para o conteúdo

Resizable

templates/components/Resizable

O Resizable divide espaço entre painéis vizinhos por meio de um divisor (handle) arrastável e acessível por teclado; sua natureza é realocar proporção (flex-grow) entre um painel e o seguinte. Por isso os enriquecedores atuam sobre COMO o arrasto se comporta — limites, colapso, ancoragem, persistência e feedback — sem tocar na aparência do divisor nem no modelo de layout.

Base congelada

One
Two
Three

Conteúdo

Persistir layout

Salva as proporções escolhidas pelo usuário…

Salva as proporções escolhidas pelo usuário (localStorage, por id do grupo) e as restaura no próximo carregamento.

Comportamento

Limites de tamanho (mín/máx)

Define tamanho mínimo e máximo por…

Define tamanho mínimo e máximo por painel (px ou %); o arrasto e as setas respeitam os limites em vez do mínimo fixo de 20px.

Painel colapsável (snap)

Ao arrastar abaixo de um limiar,…

Ao arrastar abaixo de um limiar, o painel encolhe até 0 e fecha; arrastar de volta o reabre no tamanho anterior (padrão sidebar).

Botão colapsar/expandir no handle

Adiciona um chevron no divisor para…

Adiciona um chevron no divisor para colapsar/expandir o painel vizinho com um clique, além do arrasto.

Reset por duplo-clique

Duplo-clique no divisor devolve os painéis…

Duplo-clique no divisor devolve os painéis vizinhos às proporções iniciais (o size declarado).

Medida ao arrastar

Exibe o tamanho ao vivo dos…

Exibe o tamanho ao vivo dos painéis vizinhos enquanto o handle é arrastado; some ao soltar. posicionável

Pontos de ancoragem (snap)

Durante o arrasto o divisor 'gruda'…

Durante o arrasto o divisor 'gruda' em proporções predefinidas, gerando layouts alinhados sem mira fina.

Travar divisor

Fixa um handle específico como não-redimensionável…

Fixa um handle específico como não-redimensionável (divisor rígido), mantendo os demais arrastáveis.
Código gerado

Cole no Claude Code — ele acerta de primeira.

Resizable (UX) — Design System (Symfony UX Toolkit / shadcn)

BASE CONGELADA (não mude por instância): Congelado: o modelo de layout flex proporcional (`flex: size 1 0%`), o divisor de 1px (`bg-border`, `ring-offset-background`, `focus-visible:ring-ring`) com sua área de clique (`after:w-1`), o grip opcional (pílula `rounded-lg bg-border` via `withHandle`), os cursores `col-resize`/`row-resize`, os painéis com `overflow-auto`, a orientação horizontal/vertical (flex-direction) e o espelhamento RTL. A11y imutável: `role="separator"`, `tabindex="0"`, `aria-orientation`, redimensionar por setas (passo 8px / Shift 40px). Fonte, cores, cantos e espaçamento vêm dos tokens — nenhum enricher os altera.

USO: <twig:Resizable />

CAPACIDADES OPT-IN (ligue sem alterar o visual):
- Limites de tamanho (mín/máx): Define tamanho mínimo e máximo por painel (px ou %); o arrasto e as setas respeitam os limites em vez do mínimo fixo de 20px. [number]- Painel colapsável (snap): Ao arrastar abaixo de um limiar, o painel encolhe até 0 e fecha; arrastar de volta o reabre no tamanho anterior (padrão sidebar). [toggle]- Botão colapsar/expandir no handle: Adiciona um chevron no divisor para colapsar/expandir o painel vizinho com um clique, além do arrasto. [select → Colapsa painel anterior / Colapsa painel seguinte / Dois botões (ambos)]- Reset por duplo-clique: Duplo-clique no divisor devolve os painéis vizinhos às proporções iniciais (o size declarado). [toggle]- Persistir layout: Salva as proporções escolhidas pelo usuário (localStorage, por id do grupo) e as restaura no próximo carregamento. [toggle]- Medida ao arrastar: Exibe o tamanho ao vivo dos painéis vizinhos enquanto o handle é arrastado; some ao soltar. [select → Porcentagem / Pixels / Porcentagem + pixels]- Pontos de ancoragem (snap): Durante o arrasto o divisor 'gruda' em proporções predefinidas, gerando layouts alinhados sem mira fina. [multi → 25% / 33% / 50% / 67% / 75%]- Travar divisor: Fixa um handle específico como não-redimensionável (divisor rígido), mantendo os demais arrastáveis. [toggle]FAÇA:
- Use <twig:Nome> — a base é congelada, você é dono do template em templates/components/.
- Ligue apenas capacidades opt-in listadas (e_funcao=true); a aparência não muda ao ligá-las.
- Passe atributos extras (id, aria-*, name, data-*) via {{ attributes }}.
NÃO FAÇA:
- Não mude cor/fonte/cantos/espaçamento por instância — é decisão de BASE, na fonte única assets/styles/app.css.
- Não crie variante/fork para a mesma coisa — existe UMA base por componente.
- Não reimplemente o componente nem adicione toolchain Node.
TESTE ANTES DE MUDAR: "é função ou é base?" — função = capacidade opt-in; base = mude o token na fonte única (assets/styles/app.css), para todos.

A11Y (herdada da base): A11y imutável: `role="separator"`, `tabindex="0"`, `aria-orientation`, redimensionar por setas (passo 8px / Shift 40px)

Via MCP: tool get_component com {"id": "resizable"} · list_capabilities("resizable").

Spec crua: config/ds-specs/resizable.json · Conectar o MCP.

Spec machine-readable (JSON)

{
    "$schema_version": "1.0",
    "id": "resizable",
    "component": "Resizable",
    "eixo": "ux",
    "particularidade": "O Resizable divide espaço entre painéis vizinhos por meio de um divisor (handle) arrastável e acessível por teclado; sua natureza é realocar proporção (flex-grow) entre um painel e o seguinte. Por isso os enriquecedores atuam sobre COMO o arrasto se comporta — limites, colapso, ancoragem, persistência e feedback — sem tocar na aparência do divisor nem no modelo de layout.",
    "base_congelada": "Congelado: o modelo de layout flex proporcional (`flex: size 1 0%`), o divisor de 1px (`bg-border`, `ring-offset-background`, `focus-visible:ring-ring`) com sua área de clique (`after:w-1`), o grip opcional (pílula `rounded-lg bg-border` via `withHandle`), os cursores `col-resize`/`row-resize`, os painéis com `overflow-auto`, a orientação horizontal/vertical (flex-direction) e o espelhamento RTL. A11y imutável: `role=\"separator\"`, `tabindex=\"0\"`, `aria-orientation`, redimensionar por setas (passo 8px / Shift 40px). Fonte, cores, cantos e espaçamento vêm dos tokens — nenhum enricher os altera.",
    "props": [
        {
            "name": "orientation",
            "type": "'horizontal'|'vertical'",
            "default": "horizontal",
            "description": "Layout direction. Defaults to `horizontal` #}"
        }
    ],
    "capacidades": [
        {
            "id": "limites-de-tamanho-m-n-m-x",
            "nome": "Limites de tamanho (mín/máx)",
            "descricao": "Define tamanho mínimo e máximo por painel (px ou %); o arrasto e as setas respeitam os limites em vez do mínimo fixo de 20px.",
            "controle": "number",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (number). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "painel-colaps-vel-snap",
            "nome": "Painel colapsável (snap)",
            "descricao": "Ao arrastar abaixo de um limiar, o painel encolhe até 0 e fecha; arrastar de volta o reabre no tamanho anterior (padrão sidebar).",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "bot-o-colapsar-expandir-no-handle",
            "nome": "Botão colapsar/expandir no handle",
            "descricao": "Adiciona um chevron no divisor para colapsar/expandir o painel vizinho com um clique, além do arrasto.",
            "controle": "select",
            "opcoes": [
                "Colapsa painel anterior",
                "Colapsa painel seguinte",
                "Dois botões (ambos)"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "reset-por-duplo-clique",
            "nome": "Reset por duplo-clique",
            "descricao": "Duplo-clique no divisor devolve os painéis vizinhos às proporções iniciais (o size declarado).",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "persistir-layout",
            "nome": "Persistir layout",
            "descricao": "Salva as proporções escolhidas pelo usuário (localStorage, por id do grupo) e as restaura no próximo carregamento.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "medida-ao-arrastar",
            "nome": "Medida ao arrastar",
            "descricao": "Exibe o tamanho ao vivo dos painéis vizinhos enquanto o handle é arrastado; some ao soltar.",
            "controle": "select",
            "opcoes": [
                "Porcentagem",
                "Pixels",
                "Porcentagem + pixels"
            ],
            "posicionavel": true,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (select). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "pontos-de-ancoragem-snap",
            "nome": "Pontos de ancoragem (snap)",
            "descricao": "Durante o arrasto o divisor 'gruda' em proporções predefinidas, gerando layouts alinhados sem mira fina.",
            "controle": "multi",
            "opcoes": [
                "25%",
                "33%",
                "50%",
                "67%",
                "75%"
            ],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (multi). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
        },
        {
            "id": "travar-divisor",
            "nome": "Travar divisor",
            "descricao": "Fixa um handle específico como não-redimensionável (divisor rígido), mantendo os demais arrastáveis.",
            "controle": "toggle",
            "opcoes": [],
            "posicionavel": false,
            "e_funcao": true,
            "como_plugar": "Capacidade opt-in (toggle). Ligue no configurador em /vitrine/ux/resizable; comportamento via controller Stimulus. Não altera a base."
        }
    ],
    "snippet_uso": "<twig:Resizable />",
    "exemplo_demo": "<twig:Resizable orientation=\"horizontal\" class=\"max-w-sm rounded-lg border h-[200px]\">\n    <twig:Resizable:Panel size=\"50\">\n        <div class=\"flex h-full items-center justify-center p-6\">\n            <span class=\"font-semibold\">One</span>\n        </div>\n    </twig:Resizable:Panel>\n    <twig:Resizable:Handle withHandle />\n    <twig:Resizable:Panel size=\"50\">\n        <twig:Resizable orientation=\"vertical\">\n            <twig:Resizable:Panel size=\"25\">\n                <div class=\"flex h-full items-center justify-center p-6\">\n                    <span class=\"font-semibold\">Two</span>\n                </div>\n            </twig:Resizable:Panel>\n            <twig:Resizable:Handle withHandle />\n            <twig:Resizable:Panel size=\"75\">\n                <div class=\"flex h-full items-center justify-center p-6\">\n                    <span class=\"font-semibold\">Three</span>\n                </div>\n            </twig:Resizable:Panel>\n        </twig:Resizable>\n    </twig:Resizable:Panel>\n</twig:Resizable>",
    "regras": {
        "faca": [
            "Use <twig:Nome> — a base é congelada, você é dono do template em templates/components/.",
            "Ligue apenas capacidades opt-in listadas (e_funcao=true); a aparência não muda ao ligá-las.",
            "Passe atributos extras (id, aria-*, name, data-*) via {{ attributes }}."
        ],
        "nao_faca": [
            "Não mude cor/fonte/cantos/espaçamento por instância — é decisão de BASE, na fonte única assets/styles/app.css.",
            "Não crie variante/fork para a mesma coisa — existe UMA base por componente.",
            "Não reimplemente o componente nem adicione toolchain Node."
        ]
    },
    "a11y": "A11y imutável: `role=\"separator\"`, `tabindex=\"0\"`, `aria-orientation`, redimensionar por setas (passo 8px / Shift 40px)",
    "mcp": {
        "tool": "get_component",
        "args": {
            "id": "resizable"
        }
    }
}