DocumentView
Usage
MeDocumentView assembles the document's Forehead, the section navigation tabs (MeDocumentTabs), and each section's shell (MeDocumentSection) from document and sections. Differences between document types (label, icon, metadata labels) live in documentTypes.ts — a new type is added by adding a key, without touching this block.
Renders as content, not shell: compose MeLayout around it, in the template that consumes it (see Layout).
Quotation.vue) — it won't be considered stable until a second document type (Quotation Response) validates the contract.<template>
<MeLayout>
<template #default>
<MeDocumentView
:document="document"
:sections="sections"
>
<template #section-atributos>
<p class="px-5 py-4">
Section content goes here.
</p>
</template>
</MeDocumentView>
</template>
</MeLayout>
</template>
<script setup lang="ts">
import type { DocumentMeta, DocumentViewSection } from '@mercadoeletronico/eds-next'
const document: DocumentMeta = {
type: 'quotation',
number: '1234567891',
title: '1234567891 - Cotação (RFQ)',
category: 'Categoria: RFQ',
status: 'Em análise da Negociação',
metaValues: ['Administrador FAST1', '24/02/2026']
}
const sections: DocumentViewSection[] = [
{ id: 'atributos', label: 'Atributos da cotação' }
]
</script>
Sections
Each item in sections renders as a MeDocumentSection, which picks the shell via shell: card (default) wraps the slot with a gray header and border; bare renders only the title as its own card and leaves the slot free, for sections whose internal structure is already its own shell (e.g. the Quotation's history cards, each with its own header).
MeDocumentSection is also exported standalone, to compose a section shell outside MeDocumentView without duplicating markup.
A section fills its content one of two ways: the section-<id> slot (for custom markup), or component/componentProps when the section is just a single Nuxt UI or EDSnext component with no surrounding markup (e.g. MeTableView) — component takes precedence over the slot when both are given.
Wrap the component reference with Vue's markRaw() when building sections (e.g. component: markRaw(MeTableView)) — otherwise it ends up inside sections' reactive prop and Vue makes the component definition itself reactive, which it warns against.
Atributos da cotação
#code
<template>
<MeDocumentSection
id="example"
label="Atributos da cotação"
>
Content goes here.
</MeDocumentSection>
</template>
::
Toolbar
MeLayout's #toolbar slot takes MeDocumentToolbar: a MeButtonBar of actions on the left, and on the right a view switch (UFieldGroup + UButton, one per views[]) plus a MeButtonBar of options.
The view switch is a segmented control — no dedicated Me* component exists (or needs to exist) for this, UFieldGroup + UButton already handles it, see DocumentToolbar.vue.
<template>
<MeLayout>
<template #toolbar>
<MeDocumentToolbar
:actions="actions"
:views="views"
@view-change="(key) => console.log('switched to', key)"
/>
</template>
</MeLayout>
</template>
<script setup lang="ts">
import type { DocumentToolbarView } from '@mercadoeletronico/eds-next'
const actions = { actions: [{ label: 'Finalizar negociação', icon: 'i-lucide-x' }] }
const views: DocumentToolbarView[] = [
{ key: 'doc', label: 'Documento', icon: 'i-lucide-file-text', active: true },
{ key: 'map', label: 'Mapa', icon: 'i-lucide-map' }
]
</script>
Tabs
MeDocumentTabs is rendered automatically by MeDocumentView — it doesn't need to be imported separately for common usage. It's sticky, scrolls the container (not the window) when switching tabs, and preserves the selected tab when sections changes to a new array but the id still exists (switching document type without remounting the component).
Building other document templates
MeDocumentView was built against Quotation, but the contract is meant to hold for any read-only document screen (Order, Request, Contract, Quotation Response...): add a key to documentTypes.ts and fill each section's slot, without editing DocumentView.vue itself.
- Add a key to
documentTypes.ts— declares the Forehead's label, icon and metadata labels for the new type. - Compose
MeLayout+MeDocumentToolbar(actions/views/options) +MeDocumentView(document+sections), the same shape asQuotation.vue. - Fill each section's slot with a piece the suite already covers:
| Section needs | Use |
|---|---|
| Section shell (header + border) | MeDocumentSection — automatic, one per sections[] item |
| Attribute/data table | MeTableView |
| Status history / timeline | MeStepper |
| Side panel of phases | MePhasesPanel, in MeLayout's #nav-area |
| Card-style content block | UCard (Nuxt UI) |
| Status legend (colored dots) | UChip standalone, composed inline — see Quotation.vue |
| View switch (segmented control) | UFieldGroup + UButton, composed inline — see DocumentToolbar.vue |
If a new region shows up in two or more document types and isn't in the table above, it's a candidate to graduate from inline composition to a real Me* component — see the document-suite inventory (docs/superpowers/references/2026-08-12-MW-86233-inventario-suite.md) for the two-occurrence rule and the current state of every region.
API
DocumentView
Props
| Prop | Default | Type |
|---|---|---|
document | DocumentMeta Document metadata (type, number, title, status, deadlines, indicators...). | |
sections | DocumentViewSection[] The sections. Each item has id, label, shell? ('card' | 'bare', defaults to 'card'), and optionally component/componentProps (takes precedence over the section-<id> slot). | |
sectionNav | false | boolean Shows the anchor strip. This is an anchor strip, not tabs: every section stays rendered on the page, so leaving it off never hides content. Off by default because a document knows whether it is long enough to want one and the shell does not; counting sections is not measuring length. |
foreheadActions | [] | Pick<ButtonProps, 'icon' | 'onClick'>[] Forehead icon buttons (e.g. Favorite, Comments). |
foreheadOptions | DropdownMenuItem[] Items for the Forehead's "Mais opções" dropdown. |
Slots
| Slot | Type |
|---|---|
alerts | {} |
before-sections | {} |
section-{id} | {} One per item in sections, e.g. section-atributos. |
Emits
| Event | Payload |
|---|---|
copy | value: string |
DocumentSection
| Prop | Default | Type |
|---|---|---|
id | string | |
label | string | |
shell | 'card' | 'card' | 'bare' |
DocumentToolbar
| Prop | Default | Type |
|---|---|---|
actions | ButtonBarProps | |
views | DocumentToolbarView[] Each item has key, label, icon and active?. | |
options | DropdownMenuItem[][] Groups separated by a divider. |
Emits
| Event | Payload |
|---|---|
view-change | key: string |
DocumentTabs
| Prop | Default | Type |
|---|---|---|
sections | DocumentViewSection[] | |
getContainer | () => HTMLElement | null Injected by MeDocumentView. | |
getSectionEl | (id: string) => HTMLElement | null Injected by MeDocumentView. |
Emits
| Event | Payload |
|---|---|
select | id: string |
Notes
MeDocumentToolbarandMeDocumentTabswork alongsideMeDocumentView, but are exported standalone and documented here rather than as their own components/blocks — no evidence of reuse outside this shell yet.- The state legend under the Suppliers table (in Quotation) is another no-component pattern case: composed with
UChipdirectly in the template — seeQuotation.vue. MeCard(packages/ui/app/components/card/) exists in the repo but isn't publicly exported and uses shadcn conventions (bg-card,cn()) instead of EDSnext tokens — it stays internal-only, used bychart/components. Use Nuxt UI'sUCardfor cards in the document shell.- Presentation mode (modal-over-list vs full page) with a route-based breadcrumb, replacing today's plain "Voltar" link, is a finding from the MW-86233 journey research (see
docs/superpowers/specs/2026-08-11-mw-86233-sondagem-figma-jornadas-design.md) — deliberately left out of this delivery, since there's no modal consumer yet to validate the shape against.