Aller au contenu

Adaptateur Vue

Le package @presage-kit/vue fournit des composables Vue 3 et un plugin pour construire des interfaces adaptatives.

Fenêtre de terminal
pnpm add @presage-kit/core @presage-kit/vue

Installez le plugin pour fournir le AdaptiveClient à tous les composants via l’injection de dépendances 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')

Options :

OptionTypeDescription
clientAdaptiveClientL’instance du client issue de createAdaptiveClient()

Le composable principal pour résoudre les points d’adaptation. Retourne des getters réactifs pour la variante sélectionnée, la résolution et le contexte.

<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>

Paramètres :

ParamTypeDescription
adaptationIdstringIdentifiant unique du point d’adaptation
config.variantsreadonly string[]IDs de variantes disponibles
config.defaultVariantstringVariante de repli
config.strategyStrategyOptionnel. Par défaut { type: 'rules' }

Type de retour :

L’objet retourné utilise des getters réactifs, les valeurs se mettent donc à jour automatiquement quand le contexte utilisateur change.

ChampTypeDescription
selectedVariantV (getter)La chaîne de la variante sélectionnée
resolutionResolvedAdaptation (getter)Détails complets de résolution
contextUserContext (getter)Contexte utilisateur courant
track(event: string, properties?: Record<string, unknown>) => voidFonction de suivi scopée

Suivi automatique des impressions : Comme le hook React, useAdaptive() surveille les changements de variante et suit automatiquement les événements presage:impression.

Un composable plus léger qui retourne uniquement la chaîne de la variante sélectionnée.

<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>

Paramètres : Identiques à useAdaptive().

Type de retour : V — La chaîne de la variante sélectionnée (getter réactif).

Retourne l’instance AdaptiveClient depuis l’injection du plugin. Utilisez-le pour les opérations impératives.

<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>

Lance une erreur si le AdaptivePlugin n’a pas été installé.

Contrairement à l’adaptateur React, le package Vue ne fournit pas de composant AdaptiveNav dédié. Utilisez plutôt evaluateAction() directement pour réordonner ou masquer les éléments de navigation.

<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>