Ir al contenido

Adaptador Vue

El paquete @presage-kit/vue proporciona composables de Vue 3 y un plugin para construir interfaces adaptativas.

Ventana de terminal
pnpm add @presage-kit/core @presage-kit/vue

Instale el plugin para proporcionar el AdaptiveClient a todos los componentes mediante la inyeccion de dependencias de Vue.

src/main.ts
import { createApp } from 'vue'
import { AdaptivePlugin } from '@presage-kit/vue'
import { createAdaptiveClient, createLocalStorageDriver } from '@presage-kit/core'
import App from './App.vue'
const client = createAdaptiveClient({
rules: [/* ... */],
persistence: { driver: createLocalStorageDriver('my-app') },
})
const app = createApp(App)
app.use(AdaptivePlugin, { client })
app.mount('#app')

Opciones:

OpcionTipoDescripcion
clientAdaptiveClientLa instancia del cliente creada con createAdaptiveClient()

El composable principal para resolver adaptation points. Devuelve getters reactivos para la variante seleccionada, la resolucion y el contexto.

<script setup lang="ts">
import { useAdaptive } from '@presage-kit/vue'
const { selectedVariant, resolution, context, track } = useAdaptive('onboarding', {
variants: ['guided-tour', 'standard', 'minimal'] as const,
defaultVariant: 'standard',
})
function handleComplete() {
track('onboarding_completed')
}
</script>
<template>
<div v-if="selectedVariant === 'guided-tour'">
<h2>Welcome! Let us show you around.</h2>
<button @click="handleComplete">Complete tour</button>
</div>
<div v-else-if="selectedVariant === 'standard'">
<h2>Welcome</h2>
</div>
<div v-else>
<p>Good to see you again.</p>
</div>
</template>

Parametros:

ParametroTipoDescripcion
adaptationIdstringIdentificador unico del adaptation point
config.variantsreadonly string[]IDs de variantes disponibles
config.defaultVariantstringVariante de respaldo
config.strategyStrategyOpcional. Por defecto { type: 'rules' }

Tipo de retorno:

El objeto de retorno utiliza getters reactivos, por lo que los valores se actualizan automaticamente cuando el contexto del usuario cambia.

CampoTipoDescripcion
selectedVariantV (getter)La cadena de la variante seleccionada
resolutionResolvedAdaptation (getter)Detalles completos de resolucion
contextUserContext (getter)Contexto actual del usuario
track(event: string, properties?: Record<string, unknown>) => voidFuncion de rastreo con scope

Rastreo automatico de impresiones: Al igual que el hook de React, useAdaptive() observa los cambios de variante y rastrea automaticamente eventos presage:impression.

Un composable mas ligero que devuelve unicamente la cadena de la variante seleccionada.

<script setup lang="ts">
import { useVariant } from '@presage-kit/vue'
const variant = useVariant('cta-style', {
variants: ['primary', 'secondary', 'subtle'] as const,
defaultVariant: 'primary',
})
</script>
<template>
<button :class="`cta cta--${variant}`">Get Started</button>
</template>

Parametros: Los mismos que useAdaptive().

Tipo de retorno: V — La cadena de la variante seleccionada (getter reactivo).

Devuelve la instancia de AdaptiveClient desde la inyeccion del plugin. Uselo para operaciones imperativas.

<script setup lang="ts">
import { useAdaptiveClient } from '@presage-kit/vue'
const client = useAdaptiveClient()
function handleExport() {
client.track('feature_used', { featureId: 'export' })
}
</script>
<template>
<button @click="handleExport">Export Data</button>
</template>

Lanza un error si el AdaptivePlugin no ha sido instalado.

A diferencia del adaptador React, el paquete Vue no incluye un componente AdaptiveNav dedicado. En su lugar, use evaluateAction() directamente para reordenar u ocultar elementos de navegacion.

<script setup lang="ts">
import { computed } from 'vue'
import { useAdaptiveClient } from '@presage-kit/vue'
interface NavItem {
id: string
label: string
href: string
}
const client = useAdaptiveClient()
const baseItems: NavItem[] = [
{ id: 'home', label: 'Home', href: '/' },
{ id: 'analytics', label: 'Analytics', href: '/analytics' },
{ id: 'settings', label: 'Settings', href: '/settings' },
]
const navItems = computed(() => {
const action = client.evaluateAction('sidebar-nav')
if (action?.type === 'hide') return []
if (action?.type === 'reorder') {
return reorder(baseItems, action.order)
}
return baseItems
})
function reorder(items: NavItem[], order: string[]): NavItem[] {
const map = new Map(items.map((item) => [item.id, item]))
const ordered: NavItem[] = []
for (const id of order) {
const item = map.get(id)
if (item) {
ordered.push(item)
map.delete(id)
}
}
// Append items not in the order list
for (const item of map.values()) {
ordered.push(item)
}
return ordered
}
</script>
<template>
<nav>
<a v-for="item in navItems" :key="item.id" :href="item.href">
{{ item.label }}
</a>
</nav>
</template>
src/lib/adaptive-client.ts
import { createAdaptiveClient, createLocalStorageDriver } from '@presage-kit/core'
export const client = createAdaptiveClient({
rules: [
{
id: 'power-user-sidebar',
adaptationId: 'sidebar-nav',
priority: 10,
conditions: {
all: [{ field: 'maturity', operator: 'eq', value: 'power' }],
},
action: { type: 'reorder', order: ['analytics', 'dashboards', 'settings', 'home'] },
},
{
id: 'new-user-tour',
adaptationId: 'onboarding',
priority: 10,
conditions: {
all: [{ field: 'maturity', operator: 'eq', value: 'new' }],
},
action: { type: 'show', variantId: 'guided-tour' },
},
{
id: 'experienced-skip',
adaptationId: 'onboarding',
priority: 5,
conditions: {
any: [
{ field: 'maturity', operator: 'eq', value: 'active' },
{ field: 'maturity', operator: 'eq', value: 'power' },
],
},
action: { type: 'show', variantId: 'minimal' },
},
],
persistence: {
driver: createLocalStorageDriver('vue-demo'),
},
})
src/main.ts
import { createApp } from 'vue'
import { AdaptivePlugin } from '@presage-kit/vue'
import { client } from './lib/adaptive-client'
import App from './App.vue'
const app = createApp(App)
app.use(AdaptivePlugin, { client })
app.mount('#app')
src/App.vue
<script setup lang="ts">
import SidebarNav from './components/SidebarNav.vue'
import Onboarding from './components/Onboarding.vue'
</script>
<template>
<div class="layout">
<SidebarNav />
<main>
<Onboarding />
<router-view />
</main>
</div>
</template>
src/components/Onboarding.vue
<script setup lang="ts">
import { useAdaptive } from '@presage-kit/vue'
const { selectedVariant, track } = useAdaptive('onboarding', {
variants: ['guided-tour', 'standard', 'minimal'] as const,
defaultVariant: 'standard',
})
function handleComplete() {
track('onboarding_completed')
}
</script>
<template>
<div v-if="selectedVariant === 'guided-tour'" class="onboarding">
<h2>Welcome! Let us show you around.</h2>
<button @click="handleComplete">Complete Tour</button>
</div>
<div v-else-if="selectedVariant === 'standard'" class="onboarding">
<h2>What is new</h2>
<p>Check out the latest features.</p>
</div>
<div v-else class="onboarding">
<p>Welcome back!</p>
</div>
</template>
src/components/SidebarNav.vue
<script setup lang="ts">
import { computed } from 'vue'
import { useAdaptiveClient } from '@presage-kit/vue'
const client = useAdaptiveClient()
const items = [
{ id: 'home', label: 'Home', href: '/' },
{ id: 'dashboards', label: 'Dashboards', href: '/dashboards' },
{ id: 'analytics', label: 'Analytics', href: '/analytics' },
{ id: 'settings', label: 'Settings', href: '/settings' },
]
const orderedItems = computed(() => {
const action = client.evaluateAction('sidebar-nav')
if (action?.type === 'hide') return []
if (action?.type === 'reorder') {
const map = new Map(items.map((i) => [i.id, i]))
const result = action.order
.filter((id) => map.has(id))
.map((id) => map.get(id)!)
const remaining = items.filter((i) => !action.order.includes(i.id))
return [...result, ...remaining]
}
return items
})
</script>
<template>
<nav class="sidebar">
<a v-for="item in orderedItems" :key="item.id" :href="item.href">
{{ item.label }}
</a>
</nav>
</template>