Integration Examples
Real-world code examples for integrating Plaros embeds into popular frameworks and platforms.
React Integration
Basic React Component
import React, { useMemo } from 'react';
const PlarosEmbed = ({
tenantSlug,
zonePath,
playbookId,
userId,
customColors = {},
themeMode,
size = 'medium',
className,
onLoad
}) => {
const embedUrl = useMemo(() => {
const url = new URL(`https://play.plaros.com/v1/embed/${tenantSlug}/playbooks`);
// Required parameter
url.searchParams.set('zonePath', zonePath);
// Optional parameters
if (playbookId) {
url.searchParams.set('playbookId', playbookId);
}
if (userId) {
url.searchParams.set('userId', userId);
}
if (customColors.primary) {
url.searchParams.set('primaryColor', customColors.primary.replace('#', ''));
}
if (customColors.secondary) {
url.searchParams.set('secondaryColor', customColors.secondary.replace('#', ''));
}
if (themeMode) {
url.searchParams.set('themeMode', themeMode);
}
return url.toString();
}, [tenantSlug, zonePath, playbookId, userId, customColors, themeMode]);
const sizeConfig = {
small: { maxWidth: '400px', aspectRatio: '177.78%' },
medium: { maxWidth: '600px', aspectRatio: '133.33%' },
large: { maxWidth: '800px', aspectRatio: '75%' }
};
const config = sizeConfig[size] || sizeConfig.medium;
return (
<div
className={`playbook-embed-container ${className || ''}`}
style={{
position: 'relative',
width: '100%',
maxWidth: config.maxWidth,
margin: '0 auto'
}}
>
<div style={{
position: 'relative',
paddingBottom: config.aspectRatio,
height: 0,
overflow: 'hidden'
}}>
<iframe
src={embedUrl}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
border: 0,
borderRadius: '8px'
}}
allow="fullscreen"
title="Interactive Playbook"
onLoad={onLoad}
/>
</div>
</div>
);
};
export default PlarosEmbed;React with Hooks and Context
import React, { createContext, useContext, useState } from 'react';
// Theme context for app-wide embed theming
const EmbedThemeContext = createContext();
export const EmbedThemeProvider = ({ children }) => {
const [theme, setTheme] = useState({
primaryColor: '#2196F3',
secondaryColor: '#9C27B0',
mode: 'light'
});
return (
<EmbedThemeContext.Provider value={{ theme, setTheme }}>
{children}
</EmbedThemeContext.Provider>
);
};
// Custom hook for embed configuration
const useEmbedConfig = (baseConfig) => {
const { theme } = useContext(EmbedThemeContext);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const embedUrl = useMemo(() => {
const url = new URL(`https://play.plaros.com/v1/embed/${baseConfig.tenantSlug}/playbooks`);
url.searchParams.set('zonePath', baseConfig.zonePath);
if (baseConfig.playbookId) {
url.searchParams.set('playbookId', baseConfig.playbookId);
}
if (baseConfig.userId) {
url.searchParams.set('userId', baseConfig.userId);
}
// Apply theme from context
url.searchParams.set('primaryColor', theme.primaryColor.replace('#', ''));
url.searchParams.set('secondaryColor', theme.secondaryColor.replace('#', ''));
url.searchParams.set('themeMode', theme.mode);
return url.toString();
}, [baseConfig, theme]);
const handleLoad = () => {
setLoading(false);
setError(null);
};
const handleError = (err) => {
setLoading(false);
setError(err.message || 'Failed to load embed');
};
return { embedUrl, loading, error, handleLoad, handleError };
};
// Enhanced component using the hook
const ThemedPlarosEmbed = (props) => {
const { embedUrl, loading, error, handleLoad, handleError } = useEmbedConfig(props);
if (error) {
return (
<div className="embed-error" style={{
padding: '20px',
background: '#fee',
border: '1px solid #fcc',
borderRadius: '8px',
textAlign: 'center'
}}>
<p>Failed to load playbook: {error}</p>
<button onClick={() => window.location.reload()}>
Retry
</button>
</div>
);
}
return (
<div className="themed-embed-container">
{loading && (
<div className="embed-loading" style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
zIndex: 1
}}>
Loading interactive content...
</div>
)}
<PlarosEmbed
{...props}
onLoad={handleLoad}
onError={handleError}
/>
</div>
);
};Vue.js Integration
Vue 3 Composition API
<template>
<div class="playbook-embed-container" :class="containerClass">
<div class="playbook-embed-wrapper" :style="wrapperStyle">
<iframe
:src="embedUrl"
:style="iframeStyle"
:title="embedTitle"
allow="fullscreen"
@load="handleLoad"
@error="handleError"
/>
</div>
<div v-if="loading" class="embed-loading">
<slot name="loading">
<div class="loading-spinner">Loading...</div>
</slot>
</div>
</div>
</template>
<script>
import { computed, ref, watch } from 'vue';
export default {
name: 'PlarosEmbed',
props: {
tenantSlug: { type: String, required: true },
zonePath: { type: String, required: true },
playbookId: { type: String, default: null },
userId: { type: String, default: null },
primaryColor: { type: String, default: null },
secondaryColor: { type: String, default: null },
themeMode: { type: String, default: null },
size: { type: String, default: 'medium' },
analytics: { type: Boolean, default: true },
debug: { type: Boolean, default: false }
},
emits: ['load', 'error'],
setup(props, { emit }) {
const loading = ref(true);
const embedUrl = computed(() => {
const url = new URL(`https://play.plaros.com/v1/embed/${props.tenantSlug}/playbooks`);
url.searchParams.set('zonePath', props.zonePath);
if (props.playbookId) {
url.searchParams.set('playbookId', props.playbookId);
}
if (props.userId) {
url.searchParams.set('userId', props.userId);
}
if (props.primaryColor) {
url.searchParams.set('primaryColor', props.primaryColor.replace('#', ''));
}
if (props.secondaryColor) {
url.searchParams.set('secondaryColor', props.secondaryColor.replace('#', ''));
}
if (props.themeMode) {
url.searchParams.set('themeMode', props.themeMode);
}
url.searchParams.set('analytics', props.analytics.toString());
if (props.debug) {
url.searchParams.set('debug', 'true');
}
return url.toString();
});
const sizeConfig = {
small: { maxWidth: '400px', paddingBottom: '177.78%' },
medium: { maxWidth: '600px', paddingBottom: '133.33%' },
large: { maxWidth: '800px', paddingBottom: '75%' }
};
const containerClass = computed(() => `embed-size-${props.size}`);
const wrapperStyle = computed(() => ({
position: 'relative',
paddingBottom: sizeConfig[props.size]?.paddingBottom || '133.33%',
height: 0,
overflow: 'hidden'
}));
const iframeStyle = {
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
border: 0,
borderRadius: '8px'
};
const embedTitle = computed(() =>
`Interactive Playbook${props.playbookId ? ` - ${props.playbookId}` : ''}`
);
const handleLoad = () => {
loading.value = false;
emit('load');
};
const handleError = (error) => {
loading.value = false;
emit('error', error);
};
// Reset loading state when URL changes
watch(embedUrl, () => {
loading.value = true;
});
return {
loading,
embedUrl,
containerClass,
wrapperStyle,
iframeStyle,
embedTitle,
handleLoad,
handleError
};
}
};
</script>
<style scoped>
.playbook-embed-container {
position: relative;
width: 100%;
margin: 0 auto;
}
.embed-size-small { max-width: 400px; }
.embed-size-medium { max-width: 600px; }
.embed-size-large { max-width: 800px; }
.embed-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
background: rgba(255, 255, 255, 0.9);
padding: 20px;
border-radius: 8px;
}
.loading-spinner {
text-align: center;
color: #666;
}
@media (max-width: 600px) {
.playbook-embed-container {
max-width: 100% !important;
padding: 0 16px;
}
}
</style>WordPress Integration
Plugin Development
<?php
/**
* Plugin Name: Plaros Embed
* Description: Easily embed Plaros interactive playbooks in WordPress
* Version: 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class PlarosEmbedPlugin {
public function __construct() {
add_action('init', array($this, 'init'));
add_shortcode('plaros_embed', array($this, 'embed_shortcode'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_styles'));
}
public function init() {
// Plugin initialization
}
public function enqueue_styles() {
wp_enqueue_style(
'plaros-embed-styles',
plugin_dir_url(__FILE__) . 'assets/plaros-embed.css',
array(),
'1.0.0'
);
}
public function embed_shortcode($atts) {
$atts = shortcode_atts(array(
'tenant' => '',
'zone' => '',
'playbook' => '',
'user' => '',
'width' => '600px',
'height' => '800px',
'primary_color' => '',
'secondary_color' => '',
'theme_mode' => '',
'analytics' => 'true',
'debug' => 'false',
'class' => ''
), $atts, 'plaros_embed');
// Validate required parameters
if (empty($atts['tenant']) || empty($atts['zone'])) {
return '<div class="plaros-embed-error">Error: tenant and zone parameters are required.</div>';
}
return $this->generate_embed_html($atts);
}
private function generate_embed_html($atts) {
$embed_url = $this->build_embed_url($atts);
$aspect_ratio = $this->calculate_aspect_ratio($atts['width'], $atts['height']);
$html = sprintf(
'<div class="plaros-embed-container %s" style="max-width: %s;">
<div class="plaros-embed-wrapper" style="padding-bottom: %.2f%%;">
<iframe
src="%s"
class="plaros-embed-iframe"
allow="fullscreen"
title="Interactive Playbook">
</iframe>
</div>
</div>',
esc_attr($atts['class']),
esc_attr($atts['width']),
$aspect_ratio,
esc_url($embed_url)
);
return $html;
}
private function build_embed_url($atts) {
$base_url = sprintf(
'https://play.plaros.com/v1/embed/%s/playbooks',
urlencode($atts['tenant'])
);
$params = array(
'zonePath' => $atts['zone']
);
if (!empty($atts['playbook'])) {
$params['playbookId'] = $atts['playbook'];
}
if (!empty($atts['user'])) {
$params['userId'] = $atts['user'];
}
if (!empty($atts['primary_color'])) {
$params['primaryColor'] = ltrim($atts['primary_color'], '#');
}
if (!empty($atts['secondary_color'])) {
$params['secondaryColor'] = ltrim($atts['secondary_color'], '#');
}
if (!empty($atts['theme_mode'])) {
$params['themeMode'] = $atts['theme_mode'];
}
$params['analytics'] = $atts['analytics'];
if ($atts['debug'] === 'true') {
$params['debug'] = 'true';
}
return $base_url . '?' . http_build_query($params);
}
private function calculate_aspect_ratio($width, $height) {
$w = (int) filter_var($width, FILTER_SANITIZE_NUMBER_INT);
$h = (int) filter_var($height, FILTER_SANITIZE_NUMBER_INT);
if ($w > 0 && $h > 0) {
return ($h / $w) * 100;
}
return 133.33; // Default 3:4 ratio
}
}
// Initialize the plugin
new PlarosEmbedPlugin();
// Admin page for configuration
class PlarosEmbedAdmin {
public function __construct() {
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'settings_init'));
}
public function add_admin_menu() {
add_options_page(
'Plaros Embed Settings',
'Plaros Embed',
'manage_options',
'plaros_embed',
array($this, 'options_page')
);
}
public function settings_init() {
register_setting('plaros_embed', 'plaros_embed_settings');
add_settings_section(
'plaros_embed_section',
'Default Configuration',
array($this, 'settings_section_callback'),
'plaros_embed'
);
add_settings_field(
'default_tenant',
'Default Tenant Slug',
array($this, 'text_field_callback'),
'plaros_embed',
'plaros_embed_section',
array('field' => 'default_tenant')
);
}
public function options_page() {
?>
<div class="wrap">
<h1>Plaros Embed Settings</h1>
<form action="options.php" method="post">
<?php
settings_fields('plaros_embed');
do_settings_sections('plaros_embed');
submit_button();
?>
</form>
<div class="plaros-usage-examples">
<h2>Usage Examples</h2>
<h3>Basic Embed</h3>
<code>[plaros_embed tenant="your-tenant" zone="your-zone"]</code>
<h3>With Custom Styling</h3>
<code>[plaros_embed tenant="your-tenant" zone="your-zone" primary_color="#FF6B35" theme_mode="dark"]</code>
<h3>Specific Playbook</h3>
<code>[plaros_embed tenant="your-tenant" zone="your-zone" playbook="pb_123456" user="current_user"]</code>
</div>
</div>
<?php
}
public function settings_section_callback() {
echo 'Configure default settings for Plaros embeds.';
}
public function text_field_callback($args) {
$options = get_option('plaros_embed_settings');
$value = isset($options[$args['field']]) ? $options[$args['field']] : '';
printf(
'<input type="text" id="%s" name="plaros_embed_settings[%s]" value="%s" class="regular-text" />',
$args['field'],
$args['field'],
esc_attr($value)
);
}
}
if (is_admin()) {
new PlarosEmbedAdmin();
}
?>WordPress CSS File
/* assets/plaros-embed.css */
.plaros-embed-container {
position: relative;
width: 100%;
margin: 20px auto;
background: #f9f9f9;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.plaros-embed-wrapper {
position: relative;
height: 0;
overflow: hidden;
}
.plaros-embed-iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
}
.plaros-embed-error {
background: #fee;
border: 1px solid #fcc;
border-radius: 4px;
padding: 15px;
margin: 20px 0;
color: #c33;
text-align: center;
}
/* Responsive design */
@media (max-width: 768px) {
.plaros-embed-container {
margin: 15px 0;
border-radius: 8px;
}
}
/* Theme integration */
.wp-block-group .plaros-embed-container {
background: var(--wp--preset--color--background, #ffffff);
}
.has-dark-background .plaros-embed-container {
background: var(--wp--preset--color--foreground, #000000);
box-shadow: 0 4px 20px rgba(255, 255, 255, 0.1);
}Learning Management System (LMS)
Moodle Integration
<?php
// mod/plarosembed/lib.php
function plarosembed_add_instance($plarosembed) {
global $DB;
$plarosembed->timecreated = time();
$plarosembed->timemodified = time();
return $DB->insert_record('plarosembed', $plarosembed);
}
function plarosembed_view($plarosembed, $course, $cm, $context) {
global $PAGE, $OUTPUT;
$PAGE->set_url('/mod/plarosembed/view.php', array('id' => $cm->id));
$PAGE->set_title($plarosembed->name);
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
echo $OUTPUT->heading($plarosembed->name);
// Generate embed HTML
$embed_url = build_plaros_embed_url($plarosembed);
echo html_writer::start_div('plaros-embed-container');
echo html_writer::start_div('plaros-embed-wrapper');
echo html_writer::empty_tag('iframe', array(
'src' => $embed_url,
'class' => 'plaros-embed-iframe',
'allow' => 'fullscreen',
'title' => 'Interactive Learning Module'
));
echo html_writer::end_div();
echo html_writer::end_div();
echo $OUTPUT->footer();
}
function build_plaros_embed_url($plarosembed) {
global $USER;
$base_url = "https://play.plaros.com/v1/embed/{$plarosembed->tenant_slug}/playbooks";
$params = array(
'zonePath' => $plarosembed->zone_path,
'userId' => $USER->id,
'analytics' => 'true'
);
if (!empty($plarosembed->playbook_id)) {
$params['playbookId'] = $plarosembed->playbook_id;
}
return $base_url . '?' . http_build_query($params);
}
?>Canvas LTI Integration
// Canvas LTI Tool for Plaros Embeds
class PlarosLTITool {
constructor(config) {
this.config = config;
this.initializeTool();
}
initializeTool() {
// LTI launch handling
document.addEventListener('DOMContentLoaded', () => {
this.renderEmbed();
});
}
renderEmbed() {
const container = document.getElementById('plaros-embed-container');
if (!container) return;
const embedUrl = this.buildEmbedUrl();
container.innerHTML = `
<div class="lti-embed-wrapper" style="
position: relative;
padding-bottom: 75%;
height: 0;
overflow: hidden;
background: #f5f5f5;
border-radius: 8px;
">
<iframe
src="${embedUrl}"
style="
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
border: 0;
"
allow="fullscreen"
title="Interactive Learning Module">
</iframe>
</div>
`;
}
buildEmbedUrl() {
const url = new URL(`https://play.plaros.com/v1/embed/${this.config.tenantSlug}/playbooks`);
url.searchParams.set('zonePath', this.config.zonePath);
url.searchParams.set('userId', this.config.userId);
url.searchParams.set('analytics', 'true');
if (this.config.playbookId) {
url.searchParams.set('playbookId', this.config.playbookId);
}
return url.toString();
}
// Grade passback integration
async sendGrade(score, maxScore = 100) {
const grade = (score / maxScore) * 1.0; // LTI expects 0-1 range
try {
await fetch('/lti/grade-passback', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: this.config.userId,
grade: grade,
activityId: this.config.activityId
})
});
} catch (error) {
console.error('Grade passback failed:', error);
}
}
}
// Initialize tool
const ltiTool = new PlarosLTITool({
tenantSlug: LTI_CONFIG.tenant_slug,
zonePath: LTI_CONFIG.zone_path,
playbookId: LTI_CONFIG.playbook_id,
userId: LTI_CONFIG.user_id,
activityId: LTI_CONFIG.activity_id
});Next Steps
Learn about Custom Theming to style your embeds
Check Configuration Options for parameter details
See Troubleshooting for common integration issues