@spin-studio/vue
v0.1.1
Published
Vue SDK for Spin Studio - embeddable prize wheel component
Maintainers
Readme
@spin-studio/vue
Vue 3 SDK for Spin Studio - embeddable prize wheel components with full TypeScript support.
Installation
npm install @spin-studio/vueRequirements: Vue 3.3.0 or higher
Quick Start
1. Get Your API Key & Create a Wheel
- Sign up at spin-studio.weez.boo
- Create an organization
- Go to Settings → API Keys → Create API Key
- Go to Dashboard → Wheels → Create Wheel
- Add segments, customize, and Publish
2. Add Provider
Wrap your app with SpinStudioProvider:
<!-- App.vue -->
<script setup lang="ts">
import { SpinStudioProvider } from '@spin-studio/vue';
const config = {
apiKey: 'sk_live_your_api_key_here',
baseUrl: 'https://spin-studio.weez.boo'
};
</script>
<template>
<SpinStudioProvider :config="config">
<RouterView />
</SpinStudioProvider>
</template>3. Use the SpinWheel Component
<script setup lang="ts">
import { SpinWheel } from '@spin-studio/vue';
const handlePrizeWon = (event) => {
console.log('Won:', event.prize);
console.log('Code:', event.redemptionCode);
// Apply discount to cart
applyDiscount(event.prize.value);
};
</script>
<template>
<SpinWheel
wheel-id="your-wheel-id"
@prize-won="handlePrizeWon"
/>
</template>Usage Examples
Basic Inline Wheel
<script setup lang="ts">
import { SpinStudioProvider, SpinWheel } from '@spin-studio/vue';
const config = { apiKey: 'sk_live_xxx' };
const handlePrizeWon = (event) => {
console.log('Won:', event.prize);
};
</script>
<template>
<SpinStudioProvider :config="config">
<div style="width: 400px; height: 400px;">
<SpinWheel
wheel-id="wheel-id"
:size="400"
@prize-won="handlePrizeWon"
/>
</div>
</SpinStudioProvider>
</template>With All Event Handlers
<script setup lang="ts">
import { SpinWheel } from '@spin-studio/vue';
const onReady = (e) => console.log('Wheel ready');
const onSpinStart = (e) => console.log('Spinning...');
const onSpinEnd = (e) => console.log('Spin ended');
const onPrizeWon = (e) => {
const { prize, redemptionCode } = e;
// prize.type: 'discount_percentage' | 'discount_fixed' | 'free_shipping' | ...
// prize.value: 15 (for 15% off)
// redemptionCode: 'SPIN-ABC123'
applyToCart(prize, redemptionCode);
};
const onLevelUp = (e) => console.log(`Level up! ${e.fromLevel} → ${e.toLevel}`);
const onError = (e) => console.error('Error:', e.message);
</script>
<template>
<SpinWheel
wheel-id="wheel-id"
:size="400"
@ready="onReady"
@spin-start="onSpinStart"
@spin-end="onSpinEnd"
@prize-won="onPrizeWon"
@level-up="onLevelUp"
@error="onError"
/>
</template>Using the Composable
<script setup lang="ts">
import { useSpinStudio } from '@spin-studio/vue';
const { openWheel, closeWheel, isReady } = useSpinStudio();
const handleClick = async () => {
await openWheel('wheel-id', {
mode: 'modal',
onPrizeWon: (e) => console.log('Won:', e.prize)
});
};
</script>
<template>
<button @click="handleClick" :disabled="!isReady">
🎡 Spin to Win!
</button>
</template>With Template Ref for Programmatic Control
<script setup lang="ts">
import { ref } from 'vue';
import { SpinWheel } from '@spin-studio/vue';
import type { SpinWheelRef } from '@spin-studio/vue';
const wheelRef = ref<SpinWheelRef | null>(null);
const handleSpin = async () => {
if (wheelRef.value) {
const result = await wheelRef.value.spin();
console.log('Result:', result);
}
};
const handleReset = () => {
wheelRef.value?.reset();
};
</script>
<template>
<div>
<SpinWheel ref="wheelRef" wheel-id="wheel-id" hide-spin-button />
<button @click="handleSpin">Spin!</button>
<button @click="handleReset">Reset</button>
</div>
</template>Custom Spin Button (Slot)
<template>
<SpinWheel wheel-id="wheel-id">
<template #spin-button>
<button class="my-custom-button">
🎰 Try Your Luck!
</button>
</template>
</SpinWheel>
</template>API Reference
SpinStudioProvider
Context provider for SDK configuration.
<SpinStudioProvider :config="config">
<!-- Your app -->
</SpinStudioProvider>interface Config {
apiKey: string; // Required
baseUrl?: string; // Default: 'https://spin-studio.weez.boo'
debug?: boolean; // Default: false
}SpinWheel
The wheel component.
Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| wheel-id | string | Required | The wheel ID |
| wheel-data | WheelData | - | Pre-loaded wheel data |
| size | number | 400 | Wheel size in pixels |
| spinnable | boolean | true | Enable spinning |
| initial-level | number | 1 | Starting level |
| hide-spin-button | boolean | false | Hide default button |
Events
| Event | Payload | Description |
|-------|---------|-------------|
| @ready | ReadyEvent | Wheel loaded |
| @spin-start | SpinStartEvent | Spin started |
| @spin-end | SpinEndEvent | Spin ended |
| @prize-won | PrizeWonEvent | Prize won |
| @level-up | LevelUpEvent | Level increased |
| @level-down | LevelDownEvent | Level decreased |
| @error | ErrorEvent | Error occurred |
Slots
| Slot | Description |
|------|-------------|
| spin-button | Custom spin button |
useSpinStudio
Composable for SDK interaction.
const {
openWheel, // (wheelId, options?) => Promise<WheelInstance>
closeWheel, // () => void
preloadWheel, // (wheelId) => Promise<void>
isReady, // Ref<boolean>
isLoading, // Ref<boolean>
} = useSpinStudio();Events
PrizeWonEvent
interface PrizeWonEvent {
wheelId: string;
sessionId: string;
timestamp: number;
prize: {
id: string;
name: string;
type: 'discount_percentage' | 'discount_fixed' | 'free_shipping' |
'free_product' | 'points' | 'coupon' | 'custom';
value?: number;
code?: string;
imageUrl?: string;
};
redemptionCode?: string; // e.g., "SPIN-ABC123"
}Storing Discounts (Recommended)
For production use, add discount fields to your Order model:
model Order {
// ... your existing fields
discountCode String? // "SPIN-ABC123"
discountType String? // "percentage", "fixed", "free_shipping"
discountPercent Float? // 15
discountAmount Float? // 15.00
originalTotal Float? // Total before discount
}Without these fields, discounts work visually but won't be persisted in your database.
TypeScript
Full TypeScript support included:
import type {
SpinWheelProps,
SpinWheelRef,
WheelData,
Prize,
PrizeWonEvent,
SpinResult,
} from '@spin-studio/vue';Nuxt 3
For Nuxt 3, create a plugin:
// plugins/spin-studio.client.ts
import { SpinStudioProvider } from '@spin-studio/vue';
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.component('SpinStudioProvider', SpinStudioProvider);
});Then use in your app:
<!-- app.vue -->
<template>
<SpinStudioProvider :config="{ apiKey: 'sk_live_xxx' }">
<NuxtPage />
</SpinStudioProvider>
</template>Related Packages
- Vanilla JS:
@spin-studio/sdk- For any JavaScript project - React:
@spin-studio/react- For React applications
License
MIT
