Blocks

DocumentView

Document shell — Forehead, each section's shell and an optional anchor strip, for document read screens (Quotation, Order, Request...).

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).

Every document template now consumes this block through the Documentos chassis (MeDocument): Pré-Pedido, Requisição, Pedido, Contrato, Cotação, Nota Fiscal and Registro de Serviço (FRS). Change its contract with all of them in mind.
Loading preview...
<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 by MeDocumentView when sectionNav is true (off by default, see the API below) — 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.

  1. Add a key to documentTypes.ts — declares the Forehead's label, icon and metadata labels for the new type.
  2. Compose MeLayout + MeDocumentToolbar (actions/views/options) + MeDocumentView (document + sections), the same shape as Quotation.vue.
  3. Fill each section's slot with a piece the suite already covers:
Section needsUse
Section shell (header + border)MeDocumentSection — automatic, one per sections[] item
Attribute/data tableMeTableView
Status history / timelineMeStepper
Side panel of phasesMePhasesPanel, in MeLayout's #nav-area
Card-style content blockUCard (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

PropDefaultType
documentDocumentMeta
Document metadata (type, number, title, status, deadlines, indicators...). The Forehead is data too: identity replaces the leading column with an identity block (role, title, labelled facts, the issuing user, the address), trailingFacts adds muted lines under the status badges, progressLabel captions the progress bar, and steps draws the workflow stages as a breadcrumb in the drawer, which then starts open. subNumber prints a second identifier under the number in the badge (Série 33, #ME 23088952); the copy control copies number alone. In the identity block, editAction draws the edit pencil beside the title (a boolean, since whether the reader may edit is an access rule only the page knows; editLabel names it) and issuedBy.note an info icon beside the issuing user with the text as its tooltip. alerts (DocumentAlert[]) draws the document's alerts above the Forehead as data: title and description on the left, an optional link and the close control on the right, 8px apart when there are several; the alerts slot stays for markup the data cannot carry.
sectionsDocumentViewSection[]
The sections. Each item has id, label, shell? ('card' | 'bare', defaults to 'card'), description? (a muted line under the header), empty? (starts collapsed), and optionally component/componentProps (takes precedence over the section-<id> slot).
sectionNavfalseboolean
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.
compactfalseboolean
Embedded layout: the box becomes a container-query root, the gutter and the gap between sections grow with the box's own width (16px, then 24px from @2xl), and the view has no height or scroll of its own because whoever holds it scrolls. What MeDocument turns on in summary mode.
foreheadActions[]Pick<ButtonProps, 'icon' | 'onClick'>[]
Forehead icon buttons (e.g. Favorite, Comments).
foreheadOptionsDropdownMenuItem[]
Items for the Forehead's "Mais opções" dropdown.

Slots

SlotType
rail{}
A column beside the scroll box, e.g. a phases panel.
alerts{}
before-sections{}
section-{id}{}
One per item in sections, e.g. section-atributos.

Emits

EventPayload
copyvalue: string
identityActionname: 'edit' | 'chat' | 'issuedByChat'
A control in the identity block was pressed. The view names the control and nothing else: what it opens (a page, an inline field) is the consuming page's business.

DocumentSection

PropDefaultType
idstring
labelstring
shell'card''card' | 'bare'

DocumentToolbar

PropDefaultType
actionsButtonBarProps
viewsDocumentToolbarView[]
Each item has key, label, icon and active?.
optionsDropdownMenuItem[][]
Groups separated by a divider.

Emits

EventPayload
view-changekey: string

DocumentTabs

PropDefaultType
sectionsDocumentViewSection[]
getContainer() => HTMLElement | null
Injected by MeDocumentView.
getSectionEl(id: string) => HTMLElement | null
Injected by MeDocumentView.

Emits

EventPayload
selectid: string

Notes

  • MeDocumentToolbar and MeDocumentTabs work alongside MeDocumentView, 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 UChip directly in the template — see Quotation.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 by chart/ components. Use Nuxt UI's UCard for 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.