Skip to content

ExpandableContainer

Resumo

O LxExpandableContainer é um container que pode ser expandido para ocupar toda a tela (ou um container pai específico). Ao expandir, o conteúdo é movido via Teleport para o elemento alvo (padrão: body), travando o scroll da página. Suporta fechamento por tecla Esc, botão do navegador (popstate) e controle programático.

Expansão padrão (teleport para body)

O container ocupa toda a viewport. Feche com Esc, com o botão de voltar do navegador ou com o botão de recolher.

Conteúdo expandido para tela inteira via Teleport para o body.

Expansão sobre um container específico

Quando teleportTo aponta para um elemento específico, o container expandido assume exatamente as dimensões e posição desse elemento na viewport.

Este é o container alvo (#scoped-expand-target). O conteúdo expandido cobrirá exatamente esta área.

Cobrindo exatamente #scoped-expand-target.

Controle programático via ref

Usando os métodos expostos expand(), collapse() e toggle() via ref do componente.

Este container foi aberto programaticamente via containerRef.value.expand().

Clique para visualizar o código
vue
<script setup lang="ts">
import { ref } from 'vue';
import { LxExpandableContainer, LxButton } from '@lde/lxcomponents';

const isExpanded = ref(false);
const isExpandedScoped = ref(false);
const isExpandedProgrammatic = ref(false);
const containerRef = ref<InstanceType<typeof LxExpandableContainer> | null>(null);

const expandViaRef = (): void => containerRef.value?.expand();
const collapseViaRef = (): void => containerRef.value?.collapse();
const toggleViaRef = (): void => containerRef.value?.toggle();
</script>

<template>
	<div class="example-wrapper">
		<section class="example-section">
			<h4>Expansão padrão (teleport para body)</h4>
			<p>O container ocupa toda a viewport. Feche com <kbd>Esc</kbd>, com o botão de voltar do navegador ou com o botão de recolher.</p>
			<LxButton id="btn-expand" label="Expandir conteúdo" icon="fas fa-expand-alt" @click="isExpanded = true" />

			<LxExpandableContainer id="example-expandable" v-model="isExpanded" title="Visualização expandida" close-label="Recolher">
				<div class="expandable-content">
					<p>Conteúdo expandido para tela inteira via <code>Teleport</code> para o <code>body</code>.</p>
					<LxButton id="btn-collapse" label="Recolher" severity="secondary" icon="fas fa-compress-alt" @click="isExpanded = false" />
				</div>
			</LxExpandableContainer>
		</section>

		<section class="example-section">
			<h4>Expansão sobre um container específico</h4>
			<p>Quando <code>teleportTo</code> aponta para um elemento específico, o container expandido assume exatamente as dimensões e posição desse elemento na viewport.</p>
			<div id="scoped-expand-target" class="scoped-container">
				<p>Este é o container alvo (<code>#scoped-expand-target</code>). O conteúdo expandido cobrirá exatamente esta área.</p>
				<LxButton id="btn-expand-scoped" label="Expandir neste container" icon="fas fa-expand-alt" size="sm" @click="isExpandedScoped = true" />

				<LxExpandableContainer
					id="example-expandable-scoped"
					v-model="isExpandedScoped"
					title="Expandido sobre o container"
					close-label="Fechar"
					teleport-to="#scoped-expand-target"
				>
					<div class="expandable-content">
						<p>Cobrindo exatamente <code>#scoped-expand-target</code>.</p>
						<LxButton id="btn-collapse-scoped" label="Fechar" severity="secondary" icon="fas fa-compress-alt" size="sm" @click="isExpandedScoped = false" />
					</div>
				</LxExpandableContainer>
			</div>
		</section>

		<section class="example-section">
			<h4>Controle programático via ref</h4>
			<p>Usando os métodos expostos <code>expand()</code>, <code>collapse()</code> e <code>toggle()</code> via <code>ref</code> do componente.</p>
			<div class="btn-row">
				<LxButton id="btn-ref-expand" label="expand()" icon="fas fa-expand-alt" size="sm" @click="expandViaRef" />
				<LxButton id="btn-ref-toggle" label="toggle()" icon="fas fa-random" size="sm" severity="secondary" @click="toggleViaRef" />
				<LxButton id="btn-ref-collapse" label="collapse()" icon="fas fa-compress-alt" size="sm" severity="secondary" @click="collapseViaRef" />
			</div>

			<LxExpandableContainer id="example-expandable-ref" ref="containerRef" v-model="isExpandedProgrammatic" title="Controlado por ref" close-label="Fechar">
				<div class="expandable-content">
					<p>Este container foi aberto programaticamente via <code>containerRef.value.expand()</code>.</p>
					<LxButton id="btn-ref-close" label="Fechar via collapse()" severity="secondary" icon="fas fa-compress-alt" @click="collapseViaRef" />
				</div>
			</LxExpandableContainer>
		</section>
	</div>
</template>

<style scoped>
.example-wrapper {
	display: flex;
	flex-direction: column;
	gap: 2rem;
}

.example-section {
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.example-section h4 {
	font-weight: 600;
	margin: 0;
}

.example-section p {
	margin: 0;
}

.scoped-container {
	position: relative;
	border: 1px dashed var(--lx-border-color, #ccc);
	border-radius: 0.5rem;
	padding: 1rem;
	min-height: 140px;
	display: flex;
	flex-direction: column;
	gap: 0.75rem;
}

.btn-row {
	display: flex;
	gap: 0.5rem;
	flex-wrap: wrap;
}

.expandable-content {
	display: flex;
	flex-direction: column;
	gap: 1rem;
	padding: 1rem;
}
</style>

Propriedades

NomeDescriçãoTipoPadrão
idIdentificador base do containerString'lx-expandable-container'
modelValueControla o estado expandido/recolhido via v-modelBooleanfalse
titleTítulo exibido no cabeçalho quando expandido. Quando vazio, o cabeçalho não é renderizadoString''
closeLabelTexto do botão de recolher exibido no cabeçalhoString'Recolher'
teleportToSeletor CSS do elemento alvo do Teleport quando expandidoString'body'
disabledDesabilita a alternância de estadoBooleanfalse

Eventos

NomeDescriçãoPayload
update:modelValueEmitido ao alterar o estado expandido/recolhidoBoolean
expand:regionEmitido ao expandir o container{ targetId: string | null }
collapse:regionEmitido ao recolher o container{ targetId: string | null }

Slots

NomeDescrição
defaultConteúdo exibido dentro do container, tanto no estado recolhido quanto no expandido

Métodos expostos

O componente expõe os seguintes métodos via ref (usando defineExpose):

NomeDescrição
toggle()Alterna entre expandido e recolhido
expand()Expande o container
collapse()Recolhe o container

Comportamento de expansão

  • Quando expandido com teleportTo="body" (padrão), o container é posicionado como fixed cobrindo toda a viewport (inset: 0) e o scroll do body é travado.
  • Quando teleportTo aponta para um seletor CSS específico, o container mede o getBoundingClientRect() do elemento alvo ao expandir e aplica top/left/width/height como inline style — cobrindo exatamente a área daquele elemento na viewport. A medição é atualizada automaticamente via ResizeObserver e listeners de resize/scroll enquanto o container estiver expandido.
  • O Teleport é ativado somente quando o container está expandido; no estado recolhido, o conteúdo permanece no lugar original do DOM.
  • O fechamento via Esc e o botão de voltar do navegador (popstate) são capturados automaticamente enquanto o container estiver expandido.

Desenvolvido pelo time Linx Microvix