Collections: an ordered list of Blocks
A collection is a Typed block of the Block type collection that lists other Blocks, in an order the user sets. It points at its Blocks and holds none of them: removing an entry, or the collection, never touches the Block. A reading list, the Blocks of a chapter that live in several Documents, or the hits of a query a person curated are collections.
defaultBlockTypes(t) lists the type (label „Sammlung“ / "Collection", icon i-lucide-list-checks, capabilities container with 'all', the colour basket, properties and export), so a host that spreads the built-in list has it.
What a collection stores
Two Block properties in the Typed block's meta:
| Key | Type | Meaning |
|---|---|---|
items | reference-list | the entries, in order: BlockAddress[] ({ documentId, blockId }; documentId '' is a Block of this Document) |
query | text, hidden | an optional DataQuery as JSON text, for „Aus Abfrage füllen“; the properties form never shows it |
The framework-free functions in @skaile/forge-editor/composites read and build these values:
import { collectionItemsOf, collectionQueryOf, withCollectionItems } from '@skaile/forge-editor/composites'
collectionItemsOf(node) // BlockAddress[]; entries that are no valid address are dropped
collectionQueryOf(node) // DataQuery | null; null for no query, bad JSON or a query that names no Block type
withCollectionItems(node, items) // a copy of node's meta with `items` as its list; an empty list removes the key
collectionQueryOf checks only the shape (v: 1, a blocks source that names a Block type). Which fields exist is the job of validateQuery; the route answers 422 with the issues when the stored query is wrong.
Write the list through setBlockProperty (or setCollectionItems(editor, blockId, items), which does that): it writes the one key, so two collaborators who edit different keys of the same block keep both changes.
The view
Both bindings supply a node view for the collection (CompositeNodeViews.collection): the usual Typed block view with the entries below its content.
- Every entry is a reference pill with the title of its Block (an entry whose Block is gone shows „Block gelöscht“). A click reveals the Block in the open Document, or opens it in its own Document through the host's
openBlock. - Move up, move down and remove buttons appear beside an entry while the editor is editable.
- „Aus Abfrage füllen“ shows when the collection has a stored query and
backend.index.queryexists. It runs the query, appends the hits that are not listed yet in the order of the answer, and tells the user how many it added (a collection never lists itself). A failed query leaves the list as it is and says why.
The view is features/composites/collection-model.ts (collectionViewOf, moveCollectionItem, removeCollectionItem, fillCollectionFromQuery), shared by CollectionView.vue and CollectionView.tsx. A binding that renders no live content leaves collection out of nodeViews; the collection then renders as any Typed block and its entries stay in meta.items.
Both bindings pass typedBlockViewOptions (@skaile/forge-editor/composites) to the renderer of typedBlock and collection, so a block whose Block type changes gets the view of its new type. A host that builds its own nodeViews does the same:
compositeNodes({
typedBlock: VueNodeViewRenderer(CompositeView, typedBlockViewOptions),
collection: VueNodeViewRenderer(CollectionView, typedBlockViewOptions),
reference: VueNodeViewRenderer(ReferenceView),
})
Add a Block from anywhere
The block action collection-add („Zu Sammlung hinzufügen“, Ability composite.meta) is on every Typed block. Its menu lists the collections of the open Document by title (one that holds the Block already is disabled; the action is hidden when the Document has no other collection), and choosing one appends the Block's address.
Markdown export
The Markdown form of a collection lists its entries as reference lines, one chip per paragraph, in the stored order:
# Lesestoff
[@3f0c…]
[@doc-9#a41b…]
An entry in another Document carries the Document first ([@<documentId>#<blockId>]), the same form as a reference chip. documentCompositeMarkdown, headingsMarkdown and the markdown render target all go through compositeChromeBlocks, which appends collectionItemBlocks(schema, node) for a collection. The meta line leaves out hidden properties (the stored query) and reference-list properties, so neither shows as [object Object].
EditorPorts: auth and storage
The single seam between the package and the host: ports for identity, documents, Yjs state, assets, settings, themes and AI models.
Data views: a query shown as table, list or board
The dataView Block type: its stored query and layout, the three layouts and what a board needs, how the view asks and refreshes, /ansicht, and the Typed blocks table as the table layout.