` component using a motion preset on a `` element:
+
+```vue
+
+ Text in Motion!
+
+```
+
+
+
+## ``
+
+The `` can be used to apply motion configuration to all of its child elements, this component is renderless by default and can be used as a wrapper by passing a value to the `:is` prop.
+
+```vue
+
+
+
+ Product 1
+ Description text
+
+
+ Product 2
+ Description text
+
+
+ Product 3
+ Description text
+
+
+
+```
+
+
+
+
+## Props
+
+The `` and `` components allow you to define animation properties (variants) as props.
+
+- **`is`**: What element should rendered (`div` by default for ``).
+- **`preset`**: Motion preset to use (expects camel-case string), see [Presets](/features/presets).
+
+### Variant props
+
+- **`initial`**: Properties the element will have before it is mounted.
+- **`enter`**: Properties the element will have after it is mounted.
+- **`visible`**: Properties the element will have whenever it is within view. Once out of view, the `initial` properties are reapplied.
+- **`visible-once`**: Properties the element will have once it enters the view.
+- **`hovered`**: Properties the element will have when hovered.
+- **`focused`**: Properties the element will have when it receives focus.
+- **`tapped`**: Properties the element will have upon being clicked or tapped.
+
+Variants can be passed as an object using the `:variants` prop.
+
+The `:variants` prop combines with other variant properties, allowing for the definition of custom variants from this object.
+
+Additional variant properties can be explored on the [Variants](/features/variants) page.
+
+### Shorthand Props
+
+We support shorthand props for quickly setting transition properties:
+
+- **`delay`**
+- **`duration`**
+
+These properties apply to `visible`, `visible-once`, or `enter` variants if specified; otherwise, they default to the `initial` variant.
+
+```vue
+
+
+ Content to animate!
+
+
+```
+
+### Group props
+
+These props are specific to the `` component:
+
+- **`config-fn`**
+ - Type: `(index: number) => MotionComponentConfig`
+ A function that takes an `` child element index and returns a `MotionComponentConfig`, the returned value will be applied and merged to each element individually.
diff --git a/docs/nuxt.config.ts b/docs/nuxt.config.ts
index 420f12fe..6acd32d0 100644
--- a/docs/nuxt.config.ts
+++ b/docs/nuxt.config.ts
@@ -7,6 +7,9 @@ export default defineNuxtConfig({
'@vueuse/motion/nuxt': resolve(__dirname, '../src/nuxt/module.ts'),
},
modules: ['@vueuse/motion/nuxt'],
+ features: {
+ devLogs: false,
+ },
typescript: {
includeWorkspace: true,
},
diff --git a/src/components/Motion.ts b/src/components/Motion.ts
index 487e0e99..46bcd594 100644
--- a/src/components/Motion.ts
+++ b/src/components/Motion.ts
@@ -6,6 +6,7 @@ import { variantToStyle } from '../utils/transform'
import { MotionComponentProps, setupMotionComponent } from '../utils/component'
export default defineComponent({
+ name: 'Motion',
props: {
...MotionComponentProps,
is: {
diff --git a/src/components/MotionGroup.ts b/src/components/MotionGroup.ts
index 97930391..bdc30de3 100644
--- a/src/components/MotionGroup.ts
+++ b/src/components/MotionGroup.ts
@@ -3,15 +3,17 @@ import type { Component } from '@nuxt/schema'
import { defineComponent, h, useSlots } from 'vue'
import { variantToStyle } from '../utils/transform'
-import { MotionComponentProps, setupMotionComponent } from '../utils/component'
+import { MotionComponentProps, MotionGroupComponentProps, setupMotionComponent } from '../utils/component'
export default defineComponent({
+ name: 'MotionGroup',
props: {
...MotionComponentProps,
is: {
type: [String, Object] as PropType,
required: false,
},
+ ...MotionGroupComponentProps,
},
setup(props) {
const slots = useSlots()
diff --git a/src/nuxt/module.ts b/src/nuxt/module.ts
index aff51804..ea86fa67 100644
--- a/src/nuxt/module.ts
+++ b/src/nuxt/module.ts
@@ -1,5 +1,5 @@
import { defu } from 'defu'
-import { addImportsDir, addPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'
+import { addComponent, addImportsDir, addPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'
import type { NuxtModule } from '@nuxt/schema'
import type { ModuleOptions as MotionModuleOpts } from '../types'
@@ -20,6 +20,18 @@ export default defineNuxtModule({
// Add templates (options and directives)
addPlugin(resolve('./runtime/templates/motion'))
+ addComponent({
+ name: 'Motion',
+ export: 'MotionComponent',
+ filePath: resolve('./runtime/components'),
+ })
+
+ addComponent({
+ name: 'MotionGroup',
+ export: 'MotionGroupComponent',
+ filePath: resolve('./runtime/components'),
+ })
+
// Add auto imports
addImportsDir(resolve('./runtime/composables'))
diff --git a/src/nuxt/runtime/components/index.ts b/src/nuxt/runtime/components/index.ts
new file mode 100644
index 00000000..31473667
--- /dev/null
+++ b/src/nuxt/runtime/components/index.ts
@@ -0,0 +1,2 @@
+export { default as MotionComponent } from '../../../components/Motion'
+export { default as MotionGroupComponent } from '../../../components/MotionGroup'
diff --git a/src/utils/component.ts b/src/utils/component.ts
index aac0324d..0ada58a3 100644
--- a/src/utils/component.ts
+++ b/src/utils/component.ts
@@ -78,10 +78,28 @@ export const MotionComponentProps = {
},
}
+/**
+ * Partial `` config props
+ */
+export type MotionComponentConfig = Partial>>
+
+/**
+ * Component props specific to
+ */
+export const MotionGroupComponentProps = {
+ configFn: {
+ type: Function as PropType<(index: number) => MotionComponentConfig>,
+ required: false,
+ },
+}
+
/**
* Shared logic for and
*/
-export function setupMotionComponent(props: LooseRequired>) {
+export function setupMotionComponent(
+ // prettier-ignore
+ props: LooseRequired>,
+) {
// Motion instance map
const instances = reactive<{ [key: number]: MotionInstance> }>({})
@@ -100,16 +118,12 @@ export function setupMotionComponent(props: LooseRequired {
- const config = defu({}, propsConfig.value, preset.value, props.variants || {})
-
+ function applyTransitionHelpers(config: typeof propsConfig.value, values: Partial>) {
for (const transitionKey of ['delay', 'duration'] as const) {
- if (!props[transitionKey]) continue
+ if (!values[transitionKey]) continue
- const transitionValueParsed = Number.parseInt(props[transitionKey] as string)
+ const transitionValueParsed = Number.parseInt(values[transitionKey] as string)
- // TODO: extract to utility function
// Apply transition property to existing variants where applicable
for (const variantKey of ['enter', 'visible', 'visibleOnce'] as const) {
const variantConfig = config[variantKey]
@@ -123,6 +137,13 @@ export function setupMotionComponent(props: LooseRequired {
+ const config = defu({}, { ...propsConfig.value }, preset.value, props.variants || {})
+
+ return applyTransitionHelpers({ ...config }, props)
})
// Replay animations on component update Vue
@@ -156,7 +177,13 @@ export function setupMotionComponent(props: LooseRequired {
- instances[index] = useMotion>(el as any, motionConfig.value)
+ if (props.configFn) {
+ const derivedConfig = defu({}, structuredClone({ ...motionConfig.value }), props.configFn?.(index) ?? {})
+ applyTransitionHelpers(derivedConfig, props.configFn(index))
+ instances[index] = useMotion>(el as any, derivedConfig)
+ } else {
+ instances[index] = useMotion>(el as any, motionConfig.value)
+ }
}
return node