Blocks

DocumentView

Document shell — Forehead, scrollable section tabs, and each section's shell, 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).

API subject to change. This block has a single consumer so far (Quotation.vue) — it won't be considered stable until a second document type (Quotation Response) validates the contract.
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 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.

  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...).
sectionsDocumentViewSection[]
The sections. Each item has id, label, shell? ('card' | 'bare', defaults to 'card'), 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.
foreheadActions[]Pick<ButtonProps, 'icon' | 'onClick'>[]
Forehead icon buttons (e.g. Favorite, Comments).
foreheadOptionsDropdownMenuItem[]
Items for the Forehead's "Mais opções" dropdown.

Slots

SlotType
alerts{}
before-sections{}
section-{id}{}
One per item in sections, e.g. section-atributos.

Emits

EventPayload
copyvalue: string

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.