Blocks

Document

The document chassis — one agnostic component that renders any document from a payload (meta, sections, toolbar) on top of MeDocumentView. Read is the mode in use, summary is its condensed form; create and edit exist and are not a priority.

Usage

MeDocument composes MeLayout, MeHeader, MeDocumentToolbar and MeDocumentView into a full document screen, driven entirely by mode and the v-model payload. No branch of markup differs between create and edit, only the initial data does. read renders every field as plain text. summary is read with no app shell, for a preview pane, a modal or a page of its own; see Summary mode.

The chassis knows nothing about any document type. Sections, labels, cards and per-mode visibility all come from the payload.

<template>
  <MeDocument
    v-model="formData"
    :document="document"
    mode="create"
  />
</template>

<script setup lang="ts">
import { DocumentFieldType, type DocumentFormData, type DocumentMeta } from '@mercadoeletronico/eds-next'

const document: DocumentMeta = { type: 'request', number: '', title: 'Novo documento' }

const formData = ref<DocumentFormData>({
  sections: [
    {
      id: 'campos-de-texto',
      label: 'Campos de texto',
      fields: [
        { label: 'Campo de texto', value: '', type: DocumentFieldType.text }
      ]
    }
  ]
})
</script>

Field schema

Every field is a DocumentField: a value alone renders as plain text; adding type (+ config) renders the matching editable input (MeInput, UTextarea, UInputNumber, MeInputDate, USelect, UCheckbox). Fields group into DocumentFormSection/DocumentFormSubsection. A disabled field in config automatically gets variant: 'soft' (unless it already sets its own variant), and help renders as a tooltip after a longer-than-default hover.

A link renders the value as a hyperlink. It is orthogonal to type on purpose: type picks an input, and a link is how the value is displayed, so a field only renders as a link where it renders as plain text. That includes every field in read, since the read transform strips type.

Sections

A section is identified by id and titled by label. Two things beyond fields:

  • modes limits the section to some of the three full-page screens. Omitted, it appears in all of them. A section holding decisions made while writing the document typically declares modes: ['create', 'edit'].
  • summary keeps the section in the condensed read. Opt-in and independent of modes; see Summary mode.
  • component + componentProps replace the field grid with a component of your own, for a section the chassis has no renderer for (a table, an attachment grid, a timeline). Forwarded straight to MeDocumentView, which takes it over the slot. Wrap it in markRaw, or Vue proxies it once it lands in reactive data.
  • kind picks the body renderer; see Section kinds for the table of kinds and their data.

Summary mode

mode="summary" renders the same payload as the condensed read of the document: what a preview pane, a modal or a page of its own shows before the reader opens the full document. Three things change, nothing else:

  • No app shell. No UApp, no MeLayout, no MeHeader: the pane or modal that holds the summary already has them. The toolbar, when the consumer passes one, is drawn as the document's own band above the Forehead. There is no default toolbar in this mode: a condensed document with nothing to press draws no band, rather than a Voltar that leads nowhere inside a pane.
  • Only the sections that opted in. A section stays by declaring summary: true, whatever its modes say. Nothing opts in by default: a summary is a choice of what stays, and a default of everything would be no summary. Party cards still render.
  • Container-driven spacing. MeDocumentView gets compact: the gutter and the gap between sections follow the width of the box the summary sits in, not the viewport, and the view has no scroll box of its own because the container scrolls.

The Forehead needs no extra data: identity, statuses, total, trailing facts, progress and workflow steps are the same DocumentMeta the full read carries.

<template>
  <MeDocument
    v-model="payload"
    :document="document"
    mode="summary"
    :toolbar="toolbar"
  />
</template>

Party cards

cards renders above the sections, in edit and read only: a document being created has no parties resolved yet. A banner card renders full width through MeForehead; a tile card, the default, joins a row that fills the width whatever the card count. Each card is a role eyebrow, a title and a list of { label, value } rows.

API

Props

PropDefaultType
documentDocumentMeta
Passed to MeDocumentView, see DocumentView. The number is blanked while mode is 'create'. Also takes identity, trailingFacts, progressLabel, steps and alerts (alerts above the Forehead, as data), see DocumentView.
mode'create' | 'edit' | 'read' | 'summary'
summary is the condensed read with no app shell, see Summary mode.
actionsper modeButtonBarAction[]
Overrides the default toolbar. Defaults: Criar/Cancelar, Salvar/Cancelar, Voltar.
sectionNavfalseboolean
Turns on the anchor strip. Forwarded to MeDocumentView, see DocumentView.
headerbuilt-in mockDocumentHeaderConfig
User, brand, navigation and profile items for MeHeader.

Model

ModelType
v-modelDocumentFormData
{ sections: DocumentFormSection[], cards?: DocumentPartyCard[] }.

A section's kind and its data are described on the template page and below, under Payload reference.

Slots

SlotDescription
alertsMarkup above the Forehead, after the document.alerts drawn from data. Escape hatch: a modal that must live inside the chassis's UApp goes here too.
railA column beside the scroll box, e.g. MePhasesPanel. The rail stays put while the sections scroll.
section-<id>-actionsControls in a section's header row, beside the approval state when the section carries one.
section-<id>-footerReplaces the section's footer. Only hand it on when filled: the shell draws a footer box on the slot's presence.

Events

EventPayloadDescription
copyvalue: stringThe Forehead copied the document number. The screen may raise a toast; the chassis does not.
copyErrorvalue: stringThe copy failed.
itemActionsectionId: string, name: string, item: DocumentItemA control inside an item row was pressed. What it opens is the document's business.
identityActionname: 'edit' | 'chat' | 'issuedByChat'A control in the Forehead's identity block was pressed: the edit pencil or one of the two chat controls. Same rule: the chassis names the control, the page decides what it opens.

Payload reference

Three objects describe a document in read mode. All of them are plain data: a page generator can produce them and nothing in them names a document type.

document: DocumentMeta

{
  type: 'preorder',                       // picks the badge label and icon from documentTypes.ts
  number: '15392701',
  subNumber: '#ME 23088952',              // a second identifier under the number in the badge (`Série 33`, `ME# 570844`)
  title: 'Demonstração',
  alerts: [{ id: 'offer', title: '...', description: '...', color: 'primary', link: { label: '...', href: '#' } }],
  identity: {
    title: '...',
    editAction: true,                     // the edit pencil beside the title; set by the page, since access is its rule
    issuedBy: { label: 'Requisitante', value: '...', chatAction: true, note: 'Who issued the document...' },
    facts: [{ label: 'Fornecedor', value: '...' }]
  },
  total: { currency: 'BRL', amount: '1.320,375' },
  statuses: [{ kind: 'Status do processo', label: 'APROVAÇÃO NÍVEL 1', color: 'warning' }],
  trailingFacts: [{ label: 'Criado em', value: '04/09/2026 03:04:42' }, { label: 'Atualizado em', value: '04/09/2026 23:32:30' }],
  progress: 32, progressLabel: 'Utilizado: BRL 0,0000 (0,00%)',
  steps: [{ label: 'Condições Gerais', done: true }, { label: 'Confirmação', current: true }]
}

A document with two trailing dates (a Pedido prints Criado em ... - Atualizado em ...) carries two trailingFacts; there is no field for the second date. The pencil and the chat controls emit identityAction with their name and nothing else: the legacy opens an edit page from the pencil and Genius Orchestration edits the title inline from the same control, so the chassis cannot know which one a page wants. issuedBy.note is a tooltip, not a control, and emits nothing.

modelValue: DocumentFormData

{ sections: DocumentFormSection[], cards?: DocumentPartyCard[] }. A party card (role, title, variant: 'banner' | 'tile', rows or lines (each line { text, icon?, link? }), codeChip, chatAction, mapHref, statuses) renders above the sections. Every section has id, label, an optional kind (the field grid when omitted), description, footer (a sentence or { text, legend }), empty (starts collapsed) and count (derived from the rows when omitted). The data a section carries depends on its kind:

kindDataShape
fieldsfields, subsectionsDocumentField[]: { label, value }, or lines: string[], link: { label, href }, attachments: { count, href }, span: 'full', tone, icon.
tabletable, selectable{ title, columns: [{ accessorKey, header }], rows: [{ id, ...cells }] }. A cell is a string, a number, { label, href }, { badge }, { statusIcon }, { attachments }, { actions } or an array of those.
itemsitems, totals{ columns, items: DocumentItem[], selectable, search, emptyCell, pinActions, actions, expandAll, toggle: { toDetail, toSummary }, massActions: [{ label, icon, requiresSelection }] }. An item is { id, title, values, badges?, status?, detail?: { fields, tables, attachments } }.
approvalhistory, currentState{ steps: [{ title, description, pendingNote, icon, done }], columns: [{ key, header }], rows: [{ id, ...cells, statusVariant, hasLog }], legend }; currentState is a sentence or { title, badge, at } beside the section title.
attachmentsattachments[{ label, count, href, files? }], one slot per attachment group.
totalstotals[{ label, value, emphasis? }].
cardscardsInfoCardData[], a wrapping row of cards.
emptymessageThe empty-state sentence. A list kind with no rows prints its message the same way.
tabstabs[{ id, label, kind, ...the kind's data }]: link tabs under the section header, one body per tab (a Cotação's approval history has a "Cotação Criada" and a "Cotação Fechada" tab).

toolbar: DocumentToolbarProps

{ actions: ButtonBarProps, options?: DropdownMenuItem[][], views?: DocumentToolbarView[] }. The bar is ghost primary by default; actions beyond maxVisibleActions go to the bar's own overflow menu, options is the Opções dropdown on the right and views the view switch beside it.

A complete example per document type lives in packages/ui/app/review/documents/, rendered by the review route /document-read?doc=<slug> of the packages/ui dev server.

Notes

  • Consumed by pages that hand it a payload (see Documentos), not routed at a fixed path itself, which is why it's documented and ejected as a block rather than a registry:page template.
  • Fields are mutated in place: the chassis writes edits straight into field.value. A payload built by mapping from another shape must pass the same DocumentField instances through rather than cloning them, or the form will silently stop writing back.
  • A document type is a payload, not a component: its field shape is whatever its page generator sends, and nothing of it lives here.