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.
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)
}
}
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
- Composition: what to do with the interfaces once you have them.
- Form integration:
useMirrorFieldand the field context in practice.