mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
688 lines
16 KiB
Markdown
688 lines
16 KiB
Markdown
# 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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
// 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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
// 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
|
|
```tsx
|
|
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`:
|
|
|
|
```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
|
|
```typescript
|
|
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
|
|
```typescript
|
|
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
|
|
```typescript
|
|
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
|
|
```go
|
|
// 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
|
|
```sql
|
|
-- 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
|
|
```tsx
|
|
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
|
|
```tsx
|
|
import { debounce } from 'lodash';
|
|
|
|
const debouncedSave = debounce((content) => {
|
|
saveToAPI(content);
|
|
}, 500);
|
|
|
|
<InlineTextEditor
|
|
onSave={debouncedSave}
|
|
{...otherProps}
|
|
/>
|
|
```
|
|
|
|
### Memoization
|
|
```tsx
|
|
const MemoizedColumnManager = React.memo(ColumnLayoutManager);
|
|
const MemoizedCSSEditor = React.memo(CustomCSSEditor);
|
|
```
|
|
|
|
---
|
|
|
|
## 12. Security Considerations
|
|
|
|
### Sanitize User Input
|
|
```tsx
|
|
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
|
|
```tsx
|
|
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**:
|
|
1. Test all features thoroughly
|
|
2. Deploy to staging environment
|
|
3. Train users on new features
|
|
4. Monitor performance and feedback
|
|
5. Iterate based on user needs
|