@dtpr/ui
@dtpr/ui/vue
Six Vue 3 components for rendering DTPR datachains.
@dtpr/ui/vue
Vue 3 Single-File Components. Pair with
@dtpr/ui/vue/styles.css for the default tokens and layout.Install & import
pnpm add @dtpr/ui
import {
DtprIcon,
DtprElement,
DtprElementDetail,
DtprCategorySection,
DtprDatachain,
DtprElementGrid,
} from '@dtpr/ui/vue'
import '@dtpr/ui/vue/styles.css'
Peer dependency: vue ^3.5.
DtprIcon
Renders a 36×36 <img> tag with a hexagon fallback when the source is missing or errors.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
src | string | no | '' | Icon URL. Empty or errored → fallback. |
alt | string | yes | — | Alt text. Pass '' for decorative mode (sets role="presentation"). |
size | number | no | 48 | Square dimension in px. |
Events
| Event | Payload | When |
|---|---|---|
error | Event | Emitted when the image fails to load. Component then swaps to HEXAGON_FALLBACK_DATA_URI. |
Example
<DtprIcon
:src="iconUrl"
alt="Camera icon"
:size="64"
@error="onIconError"
/>
DtprElement
Compact element card — icon + title.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
display | ElementDisplay | yes | — | Pre-derived display data from deriveElementDisplay. |
iconSize | number | no | 48 | Icon size in px. |
Example
<script setup lang="ts">
import { deriveElementDisplay } from '@dtpr/ui/core'
import { DtprElement } from '@dtpr/ui/vue'
const display = deriveElementDisplay(element, placement, 'en')
</script>
<template>
<DtprElement :display="display" />
</template>
DtprElementDetail
Rich element detail — icon, title, interpolated description, variables (with typed rendering for url / boolean / number / date), missing-required warnings, and optional citation.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
display | ElementDisplay | yes | — | Pre-derived display data. |
locale | string | no | 'en' | BCP-47 locale for Intl.NumberFormat / Intl.DateTimeFormat. |
yesNoLabels | {yes: string, no: string} | no | {yes:'yes', no:'no'} | Localized boolean labels. |
descriptionHtml | string | no | — | Pre-sanitized HTML for the description. When omitted, the plain-text path renders via interpolateSegments. |
iconSize | number | no | 64 | Icon size in px. |
descriptionHtml is injected via v-html without sanitization. Pass only HTML you have sanitized (DOMPurify, etc.) or content from a trusted source.Slots
| Slot | Default content | Purpose |
|---|---|---|
overlay | Icon + title | Replace the entire header. |
after-description | — | Inserted after the description paragraph. |
after-variables | — | Inserted after the variables list. |
after-citation | — | Inserted after the citation paragraph. |
Example
<DtprElementDetail
:display="display"
:locale="'en'"
:icon-size="96"
>
<template #after-variables>
<a :href="helpUrl">Learn more</a>
</template>
</DtprElementDetail>
DtprCategorySection
Accessible accordion section with an aria-expanded header button.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | yes | — | Stable section id (used in aria-controls). |
title | string | yes | — | Heading text. |
expanded | boolean | no | — | Controlled expanded state. Omit for uncontrolled. |
disableAccordion | boolean | no | false | When true, renders as a non-interactive heading — always expanded. |
Events
| Event | Payload | When |
|---|---|---|
update:expanded | boolean | Controlled-mode updates (v-model compatible). |
toggle | boolean | Either mode — fires on each toggle. |
Slots
| Slot | Description |
|---|---|
| default | Body content rendered inside the panel. |
Example
<DtprCategorySection id="purpose" title="Purpose" v-model:expanded="isOpen">
<DtprElement v-for="d in displays" :key="d.title" :display="d" />
</DtprCategorySection>
DtprDatachain
Top-level composer. Coordinates an accordion of DtprCategorySections and exposes per-section slots.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
sections | readonly {id, title}[] | yes | — | Ordered section descriptors. |
openSectionId | string | null | no | — | Controlled open-section id. Omit for uncontrolled. |
disableAccordion | boolean | no | false | Cascades to all sections — every section always expanded. |
Events
| Event | Payload |
|---|---|
update:openSectionId | string | null |
Slots
| Slot | Description |
|---|---|
section-<id> | Content for the section with matching id. Slot props include section: {id, title}. |
empty | Rendered when sections.length === 0. |
Example
<script setup lang="ts">
import { DtprDatachain, DtprElementDetail, DtprElementGrid } from '@dtpr/ui/vue'
import '@dtpr/ui/vue/styles.css'
import { deriveElementDisplay } from '@dtpr/ui/core'
const sections = [
{ id: 'purpose', title: 'Purpose' },
{ id: 'data', title: 'Data collected' },
]
const byCategory: Record<string, Element[]> = {
purpose: [/* ... */],
data: [/* ... */],
}
</script>
<template>
<DtprDatachain :sections="sections">
<template
v-for="section in sections"
:key="section.id"
#[`section-${section.id}`]
>
<DtprElementGrid>
<DtprElementDetail
v-for="el in byCategory[section.id]"
:key="el.id"
:display="deriveElementDisplay(el, undefined, 'en')"
/>
</DtprElementGrid>
</template>
<template #empty>
<p>No data collected.</p>
</template>
</DtprDatachain>
</template>
DtprElementGrid
Layout-only responsive grid using CSS @container queries (columns adapt to the wrapper's inline-size, not the viewport). Consumers place their own cards in the default slot.
Props
None.
Slots
| Slot | Description |
|---|---|
| default | Card content (e.g. DtprElement instances). |
Example
<DtprElementGrid>
<DtprElement v-for="d in displays" :key="d.title" :display="d" />
</DtprElementGrid>