# Composition

Change the rendered element with as and asChild, wrap a part once, and extend the props interfaces it exports.

Every part takes `as` and `asChild`, and every props interface is exported, so
you can put a Mirror part inside a component of your own without restating what
it already accepts.

## Import a part

Every part is exported twice, as a flat component and as a member of a
namespace object. Templates use the dotted form, because it reads as the
anatomy and it survives a rename.

```vue
<script setup lang="ts">
import { Select } from '@maas/mirror/vue'
</script>

<template>
  <Select.Root>
    <Select.Trigger />
  </Select.Root>
</template>
```

Single-part components are the component itself: `<Button>`, never
`<Button.Root>`. A namespace therefore means the component has more than one
part, so an import tells you how many pieces to expect before you read the
template. `Field.Label` is the one member that is not its own component, since
it is `Label` re-exported so that a field’s anatomy reads in one piece.

If you are using Nuxt, the module registers every export globally with an `M`
prefix, so the same parts are `<m-select-root>` and `<m-select-trigger>` with
nothing to import.

## Swap the element with `as`

`as` changes the element or component a part renders, keeping the behaviour and
the state attributes.

```vue
<template>
  <Button as="a" href="/docs">Read the docs</Button>
</template>
```

## Hand the behaviour to a child with `asChild`

`asChild` renders no element of its own. It merges everything it would have
rendered, attributes, listeners and `data-*` state, onto the single child you
provide.

```vue
<template>
  <Button as-child>
    <nuxt-link to="/settings">Settings</nuxt-link>
  </Button>
</template>
```

Give it exactly one element child, because everything merges onto the first
element it finds and any further siblings render without attributes, listeners
or state.

## Configure a part through `options`

A component that owns state takes a bundled `options` object on its root rather
than a long prop list, so positioning, dismissal, focus and transition settings
sit in one place instead of being repeated across the parts that use them.

```vue
<template>
  <Popover.Root
    :options="{
      floating: { side: 'bottom', sideOffset: 8 },
      dismiss: { escape: false },
    }"
  />
</template>
```

A direct prop on a nested part is the escape hatch and wins over the matching
key in the object. Reach for one where a single instance needs a different
value, and leave the rest on the root. Each component page lists which of its
props override which option.

## Controlled and uncontrolled

Every part that owns a value takes the same pair. `modelValue` makes it
controlled, `defaultValue` seeds it and leaves it uncontrolled, and `v-model`
is the controlled form written short.

```vue
<template>
  <Switch.Root :default-value="true" />

  <Switch.Root v-model="notifications" />
</template>
```

Open state follows the same rule where a component has one: `open` with
`update:open`, or `defaultOpen`.

Which of the two applies is decided once, during setup, by whether `modelValue`
is `undefined` at that moment. A ref that starts out `undefined` and is filled
in later leaves the part uncontrolled, and later writes to that ref do not move
it.

```ts
// wrong: undefined at mount, so the part never becomes controlled
const value = ref<string | undefined>()

// right: give it an initial value, or key the component so it remounts
const value = ref('')
```

## Wrap a part once

Once a class list repeats at every call site, it belongs in a wrapper. Put it
in one component per control, such as `app/components/AppButton.vue`, and give
that component the props your project wants to talk in, such as the `intent`
below.

```vue
<script setup lang="ts">
import { Button } from '@maas/mirror/vue'

interface AppButtonProps {
  intent?: 'primary' | 'danger'
}

const { intent = 'primary' } = defineProps<AppButtonProps>()
</script>

<template>
  <Button class="app-button" :class="`-${intent}`">
    <slot />
  </Button>
</template>
```

The wrapper is where a prop matrix belongs. Mirror ships no `variant`, `mode` or
`size` prop and will not add one, so declare yours here, where the names can be
the ones your project already uses. Changing one is then an edit to your own
code rather than a request to ours, and it reaches every call site at once.

## Extend the exported props

Every part exports a props interface named after it, so a wrapper extends
rather than duplicates.

```ts
import type { ButtonProps, SelectTriggerProps } from '@maas/mirror/vue'

interface AppButtonProps extends ButtonProps {
  intent?: 'primary' | 'danger'
}

interface AppSelectTriggerProps extends SelectTriggerProps {
  tone?: 'neutral' | 'danger'
}
```

Forward the rest with `v-bind`, so a prop the part gains in a later release
reaches it through the wrapper with no edit to the wrapper.

```vue
<script setup lang="ts">
import { Button, type ButtonProps } from '@maas/mirror/vue'

const { intent = 'primary', ...rest } = defineProps<AppButtonProps>()
</script>

<template>
  <Button v-bind="rest" class="app-button" :class="`-${intent}`">
    <slot />
  </Button>
</template>
```

## Render your own element polymorphically

`Primitive` and `PrimitiveSlot` are re-exported from `@maas/mirror/vue`, so a
wrapper of your own can take `as` and `asChild` the same way a Mirror part
does, without a second package.

```vue
<script setup lang="ts">
import { Primitive, type PrimitiveProps } from '@maas/mirror/vue'

const { as = 'div', asChild } = defineProps<PrimitiveProps>()
</script>

<template>
  <primitive :as="as" :as-child="asChild" class="app-panel">
    <slot />
  </primitive>
</template>
```

## Reach a module from outside its subtree

A module keeps its state in an app-scoped store keyed by `id`, so anything in
the app can drive it. Pass an `id` and read it back with the matching
composable.

```vue
<script setup lang="ts">
import { useMirrorSelect } from '@maas/mirror/vue'

const { isOpen, open, close, toggle, value, setValue } =
  useMirrorSelect('framework')
</script>
```

Give a module an `id` only when something outside its subtree has to address
it. See [TypeScript](/components/typescript) for the full list of composables and
the types they return.

## Text direction

Every component that reacts to the horizontal arrow keys takes a `dir` option,
but you rarely want to repeat it on each of them. Wrap the app, or the part of
it that runs right to left, in a `DirectionProvider` instead and they all
follow.

```vue
<script setup lang="ts">
import { DirectionProvider, Tabs } from '@maas/mirror/vue'
</script>

<template>
  <DirectionProvider direction="rtl">
    <Tabs.Root>…</Tabs.Root>
  </DirectionProvider>
</template>
```

The provider renders nothing and sets no attribute, so `dir="rtl"` on the
`<html>` element or on whichever wrapper you own is still yours to set. If you
already set it, you can skip the provider altogether: a component without a
`dir` option reads the closest `dir` attribute above it once it is mounted. In
full, a component takes its own `dir` option first, then the nearest
`DirectionProvider`, then the closest `dir` attribute, and defaults to `ltr`.

The attribute has three blind spots the provider covers, so reach for the
provider when any of them applies to you. An attribute lives in the DOM and can
only be read after mount, so a server render and the first client paint fall
back to left to right; the provider answers while the markup is being
rendered. A portalled popup moves to the end of `<body>`, out of whichever
wrapper carries your attribute, while the provider follows the component tree
and reaches it anyway. And the attribute is read once on mount, while the
provider is reactive, so changing its `direction` at runtime turns every
component below it around.

The same resolution is available on its own through `useDirection`, which
returns a computed `'ltr' | 'rtl'` for a component of yours.

## Further reading

- [Styling](/components/styling): the state a part writes, and the four ways to
  read it.
- [TypeScript](/components/typescript): the props interfaces, the options types
  and the composables, listed by name.
