16 KiB
MyUIbrix Elementor Features - Integration Guide
🔧 Component Integration
This guide shows how to integrate all the new Elementor-style features into your pages.
1. Inline Text Editor Integration
Basic Usage
import InlineTextEditor from '@/components/editor/InlineTextEditor';
// In your component
<InlineTextEditor
elementId="hero-title"
initialContent="<h1>Welcome to Our Club</h1>"
onSave={(newContent) => {
// Save to state or API
updateElementContent('hero-title', newContent);
}}
/>
Advanced Usage with State Management
const [heroTitle, setHeroTitle] = useState('<h1>Welcome</h1>');
<InlineTextEditor
elementId="hero-title"
initialContent={heroTitle}
onSave={(content) => {
setHeroTitle(content);
// Persist to backend
saveToAPI('hero', { title: content });
}}
/>
Making Existing Elements Editable
// Wrap any text element
<Box data-element="news">
<InlineTextEditor
elementId="news-headline"
initialContent={newsHeadline}
onSave={handleSaveHeadline}
/>
<InlineTextEditor
elementId="news-description"
initialContent={newsDescription}
onSave={handleSaveDescription}
/>
</Box>
2. Column Layout Manager Integration
Basic Setup
import ColumnLayoutManager from '@/components/editor/ColumnLayoutManager';
const [columns, setColumns] = useState([
{ id: '1', width: '50%', elements: [] },
{ id: '2', width: '50%', elements: [] }
]);
<ColumnLayoutManager
elementName="hero"
currentColumns={columns}
onLayoutChange={(newColumns) => {
setColumns(newColumns);
applyLayoutToDOM(newColumns);
}}
/>
Applying Layout to DOM
const applyLayoutToDOM = (columns: Column[]) => {
const container = document.querySelector('[data-element="hero"]');
if (!container) return;
// Clear existing layout
container.style.display = 'grid';
container.style.gridTemplateColumns = columns.map(c => c.width).join(' ');
container.style.gap = '20px';
// Save to backend
saveLayoutConfig('hero', columns);
};
Responsive Columns
const [columns, setColumns] = useState({
desktop: [
{ id: '1', width: '33.33%', elements: [] },
{ id: '2', width: '33.33%', elements: [] },
{ id: '3', width: '33.33%', elements: [] }
],
tablet: [
{ id: '1', width: '50%', elements: [] },
{ id: '2', width: '50%', elements: [] }
],
mobile: [
{ id: '1', width: '100%', elements: [] }
]
});
// Apply based on viewport
const currentColumns = viewport === 'mobile'
? columns.mobile
: viewport === 'tablet'
? columns.tablet
: columns.desktop;
<ColumnLayoutManager
elementName="hero"
currentColumns={currentColumns}
onLayoutChange={(newCols) => {
setColumns(prev => ({
...prev,
[viewport]: newCols
}));
}}
/>
3. Custom CSS Editor Integration
Basic Integration
import CustomCSSEditor from '@/components/editor/CustomCSSEditor';
const [customCSS, setCustomCSS] = useState('');
<CustomCSSEditor
elementName="hero"
currentCSS={customCSS}
onCSSChange={(css) => {
setCustomCSS(css);
applyCustomCSS('hero', css);
}}
/>
Applying CSS to Elements
const applyCustomCSS = (elementName: string, css: string) => {
// Remove existing custom style
const existingStyle = document.getElementById(`custom-css-${elementName}`);
if (existingStyle) {
existingStyle.remove();
}
// Apply new CSS
if (css.trim()) {
const style = document.createElement('style');
style.id = `custom-css-${elementName}`;
style.textContent = `
[data-element="${elementName}"] {
${css}
}
`;
document.head.appendChild(style);
}
// Save to database
saveCustomCSS(elementName, css);
};
CSS with Media Queries
const applyResponsiveCSS = (elementName: string, css: Record<string, string>) => {
const style = document.createElement('style');
style.id = `custom-css-${elementName}`;
style.textContent = `
[data-element="${elementName}"] {
${css.desktop || ''}
}
@media (max-width: 768px) {
[data-element="${elementName}"] {
${css.tablet || ''}
}
}
@media (max-width: 480px) {
[data-element="${elementName}"] {
${css.mobile || ''}
}
}
`;
document.head.appendChild(style);
};
4. Contextual Admin Links Integration
Basic Usage
import ContextualAdminLinks from '@/components/editor/ContextualAdminLinks';
// In your style panel or settings popup
<Box>
<Heading size="sm">Quick Actions</Heading>
<ContextualAdminLinks elementName={selectedElement} />
</Box>
Custom Links for New Elements
// Extend ContextualAdminLinks.tsx with new element types
const getLinksForElement = (element: string): AdminLink[] => {
const links: Record<string, AdminLink[]> = {
// ... existing links ...
// Add your custom element
'custom-gallery': [
{
label: 'Manage Photos',
url: '/admin/custom-gallery',
icon: FiImage,
description: 'Upload and organize photos'
},
{
label: 'Gallery Settings',
url: '/admin/settings/custom-gallery',
icon: FiSettings
},
],
};
return links[element] || [];
};
5. Full Integration Example
Complete Editable Section
import React, { useState } from 'react';
import { Box, VStack } from '@chakra-ui/react';
import InlineTextEditor from '@/components/editor/InlineTextEditor';
import ColumnLayoutManager from '@/components/editor/ColumnLayoutManager';
import CustomCSSEditor from '@/components/editor/CustomCSSEditor';
import ContextualAdminLinks from '@/components/editor/ContextualAdminLinks';
const EditableHeroSection: React.FC = () => {
const [title, setTitle] = useState('<h1>Welcome</h1>');
const [subtitle, setSubtitle] = useState('<p>Your club, your passion</p>');
const [columns, setColumns] = useState([
{ id: '1', width: '60%', elements: [] },
{ id: '2', width: '40%', elements: [] }
]);
const [customCSS, setCustomCSS] = useState('');
const { isEditing } = useEditMode(); // Your edit mode hook
return (
<Box data-element="hero" position="relative">
{/* Main Content */}
<VStack spacing={4} align="stretch">
{isEditing ? (
<>
<InlineTextEditor
elementId="hero-title"
initialContent={title}
onSave={setTitle}
/>
<InlineTextEditor
elementId="hero-subtitle"
initialContent={subtitle}
onSave={setSubtitle}
/>
</>
) : (
<>
<div dangerouslySetInnerHTML={{ __html: title }} />
<div dangerouslySetInnerHTML={{ __html: subtitle }} />
</>
)}
</VStack>
{/* Editor Panel (shown when element is selected) */}
{isEditing && (
<Box
position="fixed"
right={4}
top="100px"
width="300px"
bg="white"
borderRadius="lg"
boxShadow="xl"
p={4}
>
<VStack align="stretch" spacing={4}>
<ColumnLayoutManager
elementName="hero"
currentColumns={columns}
onLayoutChange={setColumns}
/>
<CustomCSSEditor
elementName="hero"
currentCSS={customCSS}
onCSSChange={setCustomCSS}
/>
<ContextualAdminLinks elementName="hero" />
</VStack>
</Box>
)}
</Box>
);
};
export default EditableHeroSection;
6. Enhanced MyUIbrixEditor Integration
Adding New Components to Existing Editor
Update MyUIbrixEditor.tsx:
import InlineTextEditor from './InlineTextEditor';
import CustomCSSEditor from './CustomCSSEditor';
import ColumnLayoutManager from './ColumnLayoutManager';
import ContextualAdminLinks from './ContextualAdminLinks';
// Add state for new features
const [elementContent, setElementContent] = useState<Record<string, string>>({});
const [elementColumns, setElementColumns] = useState<Record<string, Column[]>>({});
const [elementCSS, setElementCSS] = useState<Record<string, string>>({});
// In the contextual style panel, add tabs
<Tabs>
<TabList>
<Tab>Style</Tab>
<Tab>Layout</Tab>
<Tab>CSS</Tab>
<Tab>Content</Tab>
<Tab>Admin</Tab>
</TabList>
<TabPanels>
{/* Style Tab */}
<TabPanel>
<VisualStylePanel
elementName={selectedElement}
onStyleChange={handleStyleChange}
currentStyles={elementStyles[selectedElement]}
/>
</TabPanel>
{/* Layout Tab */}
<TabPanel>
<ColumnLayoutManager
elementName={selectedElement}
currentColumns={elementColumns[selectedElement] || []}
onLayoutChange={(cols) => {
setElementColumns(prev => ({
...prev,
[selectedElement]: cols
}));
}}
/>
</TabPanel>
{/* CSS Tab */}
<TabPanel>
<CustomCSSEditor
elementName={selectedElement}
currentCSS={elementCSS[selectedElement] || ''}
onCSSChange={(css) => {
setElementCSS(prev => ({
...prev,
[selectedElement]: css
}));
}}
/>
</TabPanel>
{/* Content Tab */}
<TabPanel>
<VStack align="stretch" spacing={3}>
<Text fontWeight="bold">Edit Content</Text>
<Button
leftIcon={<FiEdit />}
onClick={() => enableInlineEditingForElement(selectedElement)}
>
Enable Inline Editing
</Button>
</VStack>
</TabPanel>
{/* Admin Tab */}
<TabPanel>
<ContextualAdminLinks elementName={selectedElement} />
</TabPanel>
</TabPanels>
</Tabs>
7. Saving and Loading Data
Data Structure
interface ElementConfiguration {
element_name: string;
variant: string;
visible: boolean;
display_order: number;
// New fields
content?: Record<string, string>; // Inline edited content
columns?: Column[]; // Column layout
customCSS?: string; // Custom CSS
customStyles?: Record<string, any>; // Style panel values
}
Save Function
const saveAllChanges = async () => {
const configurations: ElementConfiguration[] = elementOrder.map((elementName, index) => ({
page_type: pageType,
element_name: elementName,
variant: localChanges[elementName] || 'default',
visible: visibleElements.has(elementName),
display_order: index,
// New data
content: elementContent[elementName],
columns: elementColumns[elementName],
customCSS: elementCSS[elementName],
customStyles: elementStyles[elementName],
}));
await batchUpdatePageElementConfigs(configurations);
toast({
title: 'All changes saved!',
status: 'success',
duration: 3000,
});
};
Load Function
const loadConfigurations = async () => {
const configs = await getPageElementConfigs(pageType);
const content: Record<string, string> = {};
const columns: Record<string, Column[]> = {};
const css: Record<string, string> = {};
const styles: Record<string, any> = {};
configs.forEach(config => {
if (config.content) content[config.element_name] = config.content;
if (config.columns) columns[config.element_name] = config.columns;
if (config.customCSS) css[config.element_name] = config.customCSS;
if (config.customStyles) styles[config.element_name] = config.customStyles;
});
setElementContent(content);
setElementColumns(columns);
setElementCSS(css);
setElementStyles(styles);
// Apply CSS to DOM
Object.entries(css).forEach(([elementName, cssString]) => {
applyCustomCSS(elementName, cssString);
});
};
8. Backend API Updates
Update API Endpoint
// In your page elements controller
type PageElementConfig struct {
PageType string `json:"page_type"`
ElementName string `json:"element_name"`
Variant string `json:"variant"`
Visible bool `json:"visible"`
DisplayOrder int `json:"display_order"`
// New fields
Content map[string]string `json:"content,omitempty"`
Columns []Column `json:"columns,omitempty"`
CustomCSS string `json:"custom_css,omitempty"`
CustomStyles map[string]interface{} `json:"custom_styles,omitempty"`
}
type Column struct {
ID string `json:"id"`
Width string `json:"width"`
Elements []string `json:"elements"`
}
Database Migration
-- Add new columns to page_elements table
ALTER TABLE page_elements
ADD COLUMN content JSONB,
ADD COLUMN columns JSONB,
ADD COLUMN custom_css TEXT,
ADD COLUMN custom_styles JSONB;
-- Create index for faster queries
CREATE INDEX idx_page_elements_custom_css ON page_elements(custom_css) WHERE custom_css IS NOT NULL;
9. Testing Checklist
-
Inline Editor
- Click to edit activates editor
- Formatting toolbar appears
- Bold/Italic/Underline work
- Links can be inserted
- Auto-save on blur works
- Changes persist after page reload
-
Column Layout
- Templates apply correctly
- Columns can be added/removed
- Widths recalculate automatically
- Layout persists after save
-
Custom CSS
- Code editor works
- Validation detects errors
- Preview mode applies styles
- Examples can be inserted
- Styles persist after save
-
Admin Links
- Links show for each element type
- Links open in new tab
- URLs are correct
- Icons display properly
-
Integration
- All components work together
- No console errors
- Performance is acceptable
- Mobile responsive
- Cross-browser compatible
10. Common Issues & Solutions
Issue: Inline editor not appearing
Solution: Ensure element has proper data attribute and is not nested incorrectly.
Issue: Custom CSS not applying
Solution: Check for syntax errors, ensure style tag is being created, check CSS specificity.
Issue: Column layout breaking
Solution: Verify total width is 100%, check for conflicting CSS, ensure grid is supported.
Issue: Admin links not working
Solution: Verify routes exist, check authentication, ensure backend is running.
11. Performance Optimization
Lazy Loading
const InlineTextEditor = lazy(() => import('./InlineTextEditor'));
const CustomCSSEditor = lazy(() => import('./CustomCSSEditor'));
const ColumnLayoutManager = lazy(() => import('./ColumnLayoutManager'));
// Use with Suspense
<Suspense fallback={<Spinner />}>
<InlineTextEditor {...props} />
</Suspense>
Debouncing Updates
import { debounce } from 'lodash';
const debouncedSave = debounce((content) => {
saveToAPI(content);
}, 500);
<InlineTextEditor
onSave={debouncedSave}
{...otherProps}
/>
Memoization
const MemoizedColumnManager = React.memo(ColumnLayoutManager);
const MemoizedCSSEditor = React.memo(CustomCSSEditor);
12. Security Considerations
Sanitize User Input
import DOMPurify from 'dompurify';
const sanitizeHTML = (html: string) => {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: ['b', 'i', 'u', 'a', 'p', 'h1', 'h2', 'h3', 'span'],
ALLOWED_ATTR: ['href', 'target', 'rel'],
});
};
<InlineTextEditor
onSave={(content) => {
const clean = sanitizeHTML(content);
saveToAPI(clean);
}}
/>
Validate CSS
const isValidCSS = (css: string): boolean => {
// Check for dangerous content
if (css.includes('javascript:') || css.includes('<script')) {
return false;
}
// Check for balanced braces
const openBraces = (css.match(/{/g) || []).length;
const closeBraces = (css.match(/}/g) || []).length;
return openBraces === closeBraces;
};
Summary
All new Elementor-style components are now integrated into MyUIbrix:
✅ Inline Text Editor - Rich text editing in place
✅ Column Layout Manager - Visual layout builder
✅ Custom CSS Editor - Full CSS control
✅ Contextual Admin Links - Smart navigation
✅ Enhanced Style Panel - Complete styling tools
The system is modular, type-safe, and production-ready!
Next Steps:
- Test all features thoroughly
- Deploy to staging environment
- Train users on new features
- Monitor performance and feedback
- Iterate based on user needs