@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

PropTypeRequiredDefaultDescription
srcstringno''Icon URL. Empty or errored → fallback.
altstringyesAlt text. Pass '' for decorative mode (sets role="presentation").
sizenumberno48Square dimension in px.

Events

EventPayloadWhen
errorEventEmitted 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

PropTypeRequiredDefaultDescription
displayElementDisplayyesPre-derived display data from deriveElementDisplay.
iconSizenumberno48Icon 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

PropTypeRequiredDefaultDescription
displayElementDisplayyesPre-derived display data.
localestringno'en'BCP-47 locale for Intl.NumberFormat / Intl.DateTimeFormat.
yesNoLabels{yes: string, no: string}no{yes:'yes', no:'no'}Localized boolean labels.
descriptionHtmlstringnoPre-sanitized HTML for the description. When omitted, the plain-text path renders via interpolateSegments.
iconSizenumberno64Icon 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

SlotDefault contentPurpose
overlayIcon + titleReplace the entire header.
after-descriptionInserted after the description paragraph.
after-variablesInserted after the variables list.
after-citationInserted 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

PropTypeRequiredDefaultDescription
idstringyesStable section id (used in aria-controls).
titlestringyesHeading text.
expandedbooleannoControlled expanded state. Omit for uncontrolled.
disableAccordionbooleannofalseWhen true, renders as a non-interactive heading — always expanded.

Events

EventPayloadWhen
update:expandedbooleanControlled-mode updates (v-model compatible).
togglebooleanEither mode — fires on each toggle.

Slots

SlotDescription
defaultBody 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

PropTypeRequiredDefaultDescription
sectionsreadonly {id, title}[]yesOrdered section descriptors.
openSectionIdstring | nullnoControlled open-section id. Omit for uncontrolled.
disableAccordionbooleannofalseCascades to all sections — every section always expanded.

Events

EventPayload
update:openSectionIdstring | null

Slots

SlotDescription
section-<id>Content for the section with matching id. Slot props include section: {id, title}.
emptyRendered 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

SlotDescription
defaultCard content (e.g. DtprElement instances).

Example

<DtprElementGrid>
  <DtprElement v-for="d in displays" :key="d.title" :display="d" />
</DtprElementGrid>

See also

Copyright © 2026