Skip to content

TypeScript

Mirror exports a props interface for every part, plus options types, composables and context keys. Read this for how they are named and where to import them.

View source View as Markdown

Everything public is exported from @maas/mirror/vue, types and values from the same entry point.

import { Button, type ButtonProps } from '@maas/mirror/vue'

Props interfaces

Every part exports an interface named after it. Multi-part components use <Component><Part>Props, single-part components use <Component>Props.

import type {
  ButtonProps,
  FieldRootProps,
  RadioGroupProps,
  SelectContentProps,
  SliderThumbProps,
  TabsContentProps,
} from '@maas/mirror/vue'

Extend one to add your own props without restating what the part already accepts. See Composition.

Every interface that renders an element extends PrimitiveProps, which holds as and asChild, and which is re-exported alongside Primitive and PrimitiveSlot.

import { Primitive, type PrimitiveProps } from '@maas/mirror/vue'

Options types

Components that take a bundled options object export its type, plus a Resolved form with every key filled in from the defaults.

import type { SelectOptions, ResolvedSelectOptions } from '@maas/mirror/vue'

AvatarOptions, CheckboxOptions, ComboboxOptions, FieldOptions, ProgressOptions, RadioOptions, SelectOptions, SliderOptions, SwitchOptions and TabsOptions each have one.

Nested groups are exported separately, so you can type a fragment of the object you build elsewhere.

import type { SelectFloatingOptions } from '@maas/mirror/vue'

const floating: SelectFloatingOptions = { side: 'bottom', sideOffset: 8 }

Select and Combobox also export *DismissOptions, *FocusOptions and *TransitionOptions, and Select adds SelectTypeaheadOptions.

Module composables

A module keeps its state in an app-scoped store keyed by id and exposes one composable as its public API. Each takes a MaybeRefOrGetter<string> and returns a UseMirror<Component>Return.

import { useMirrorSelect, type UseMirrorSelectReturn } from '@maas/mirror/vue'

const select = useMirrorSelect('framework')

Every module exports one composable named after it, useMirrorSelect for Select and useMirrorTabs for Tabs. Checkbox exports a second, useMirrorCheckboxGroup, for the group store. Toggle is the one exception to the naming: a lone Toggle.Root holds its state locally, so its only composable is useMirrorToggleGroup.

Button, Input, Label, Separator and Textarea have no store and therefore no composable; they take props and nothing else.

Instance IDs

An id is an ordinary string. Mirror generates one when you leave it out and declares no enum of its own, so keep your instance IDs in a const object or an enum your app owns.

export const FieldId = {
  Email: 'email',
  Password: 'password',
} as const

Pass one wherever something outside the subtree has to address the instance.

<template>
  <Field.Root :id="FieldId.Email" :options="{ name: 'email' }" />
</template>

Context keys

Where a part needs to reach its parent’s context directly, the injection key is exported.

import { inject } from 'vue'
import { FieldContextKey, SelectItemContextKey } from '@maas/mirror/vue'

const field = inject(FieldContextKey, undefined)

AvatarContextKey, ComboboxContextKey, ComboboxItemContextKey, FieldContextKey, ProgressContextKey, RadioItemContextKey, SelectContextKey, SelectGroupContextKey, SelectItemContextKey, SliderContextKey and TabsContextKey are all available, along with the CheckboxInstanceId, RadioInstanceId and SwitchInstanceId id keys.

Prefer the composable where one exists, and reach for a context key when you are building a part of your own that has to sit inside a Mirror subtree.

Errors

Mirror fails loudly rather than rendering something inert. Every assertion throws a MagicError, which is re-exported from @maas/mirror/vue so you can narrow on it directly.

import { MagicError } from '@maas/mirror/vue'

try {
  render()
} catch (error) {
  if (error instanceof MagicError) {
    console.error(error.errorCode, error.source)
  }
}
PropertyType
errorCode
string | number
source
string
timestamp
number

The message is prefixed [Mirror]: and is also written to console.error before the throw, so catch by code rather than by message.

MagicErrorCode types the codes shared across every part.

import type { MagicErrorCode } from '@maas/mirror/vue'

// 'missing_context' | 'missing_instance_id' | 'missing_element'
function isRecoverable(code: MagicErrorCode) {
  return code === 'missing_element'
}

missing_context is the most common one, since any part whose only requirement is an immediate parent raises it, such as Select.ItemText outside a Select.Item. Component-specific codes are plain strings, documented one for one with the assertion that raises them on each component page.

Further reading