8.0 KiB
8.0 KiB
Extension Slots System - Canvas Insertion Design
Overview
Simple system for adding widget canvases to existing pages using dedicated extension slot components. When in edit mode, users can add a canvas before or after known extension points.
Concept
Extension Slot Component
A dedicated component that marks insertion points on any page:
<ExtensionSlot
id="coils-page-header"
position="after-header"
pageId="coils-page"
/>
Edit Mode Behavior
When edit mode is enabled:
- Extension slots become visible with "Add Canvas" buttons
- User clicks "Add Canvas Before" or "Add Canvas After"
- New canvas is inserted at that position
- Canvas uses existing layout system (containers + widgets)
Architecture
Core Components
ExtensionSlot Component
// src/components/layout/ExtensionSlot.tsx
interface ExtensionSlotProps {
id: string; // Unique identifier for this slot
pageId: string; // Page this slot belongs to
position: string; // Descriptive position (e.g., "after-header")
title?: string; // Display name for the slot
allowMultiple?: boolean; // Allow multiple canvases at this slot
canvasConfig?: {
defaultColumns?: number;
defaultGap?: number;
showTitle?: boolean;
};
}
const ExtensionSlot: React.FC<ExtensionSlotProps> = ({
id,
pageId,
position,
title,
allowMultiple = false,
canvasConfig
}) => {
const { isEditMode } = useLayout();
const { getSlotCanvases, addCanvasToSlot } = useExtensionSlots();
const canvases = getSlotCanvases(pageId, id);
const canAddMore = allowMultiple || canvases.length === 0;
return (
<div className="extension-slot" data-slot-id={id}>
{/* Render existing canvases */}
{canvases.map(canvas => (
<GenericCanvas
key={canvas.id}
pageId={pageId}
pageName={canvas.name}
isEditMode={isEditMode}
showControls={isEditMode}
canvasId={canvas.id}
slotId={id}
/>
))}
{/* Add Canvas Button (Edit Mode Only) */}
{isEditMode && canAddMore && (
<div className="extension-slot-controls border-2 border-dashed border-blue-300 rounded-lg p-4 text-center">
<p className="text-sm text-slate-600 mb-2">
Extension Slot: {title || position}
</p>
<Button
onClick={() => addCanvasToSlot(pageId, id, canvasConfig)}
size="sm"
className="bg-blue-500 text-white"
>
<Plus className="h-4 w-4 mr-2" />
Add Canvas Here
</Button>
</div>
)}
</div>
);
};
Extension Slots Context
// src/contexts/ExtensionSlotsContext.tsx
interface ExtensionSlotsContextType {
getSlotCanvases: (pageId: string, slotId: string) => SlotCanvas[];
addCanvasToSlot: (pageId: string, slotId: string, config?: CanvasConfig) => Promise<SlotCanvas>;
removeCanvasFromSlot: (pageId: string, canvasId: string) => Promise<void>;
updateCanvasConfig: (pageId: string, canvasId: string, config: Partial<CanvasConfig>) => Promise<void>;
}
interface SlotCanvas {
id: string;
name: string;
slotId: string;
pageId: string;
config: CanvasConfig;
createdAt: number;
updatedAt: number;
}
interface CanvasConfig {
columns: number;
gap: number;
showTitle: boolean;
title?: string;
collapsible?: boolean;
}
Storage Schema
Extended Layout Structure
// Storage in layout.json
{
"pages": {
"coils-page": {
"id": "coils-page",
"name": "Coils Page",
"containers": [], // Empty for non-canvas pages
"extensionSlots": { // New field for extension slot canvases
"coils-page-header": [
{
"id": "canvas-123",
"name": "Header Dashboard",
"slotId": "coils-page-header",
"pageId": "coils-page",
"config": { "columns": 2, "gap": 16, "showTitle": true },
"layout": { // Standard layout structure
"containers": [
{
"id": "container-456",
"widgets": [
{ "id": "widget-789", "widgetId": "status-chart" }
]
}
]
}
}
]
}
}
}
}
Implementation Plan
Phase 1: Core Infrastructure (3 days)
Day 1: Extension Slot Component
□ Create ExtensionSlot component with add canvas functionality
□ Implement basic slot registration and canvas creation
□ Add edit mode integration with show/hide controls
□ Test with simple slot placement
Day 2: Storage Integration
□ Extend UnifiedLayoutManager for extension slot canvases
□ Add ExtensionSlotsContext for state management
□ Implement slot canvas CRUD operations
□ Add persistence to layout.json structure
Day 3: Canvas Integration
□ Modify GenericCanvas to work within extension slots
□ Add slot-aware canvas identification
□ Implement canvas removal from slots
□ Add canvas configuration options
Phase 2: Page Integration (2 days)
Day 4: Page Enhancement
□ Add ExtensionSlot components to CoilsPage
□ Add ExtensionSlot components to RegistersPage
□ Add ExtensionSlot components to SettingsDisplay
□ Test canvas addition/removal on each page
Day 5: Polish & Testing
□ Add visual feedback for slot boundaries
□ Implement canvas title editing
□ Add canvas reordering within slots
□ Performance testing and optimization
Usage Examples
CoilsPage with Extension Slots
// src/pages/CoilsPage.tsx
const CoilsPage = () => {
return (
<div className="coils-page">
{/* Header Extension Slot */}
<ExtensionSlot
id="coils-header"
pageId="coils-page"
position="page-header"
title="Header Widgets"
canvasConfig={{ defaultColumns: 3, showTitle: true }}
/>
{/* Existing Coils Content */}
<CoilsTable />
<CoilsControls />
{/* Footer Extension Slot */}
<ExtensionSlot
id="coils-footer"
pageId="coils-page"
position="page-footer"
title="Footer Widgets"
allowMultiple={true}
/>
</div>
);
};
RegistersPage with Extension Slots
// src/pages/RegistersPage.tsx
const RegistersPage = () => {
return (
<div className="registers-page">
<ExtensionSlot
id="registers-sidebar"
pageId="registers-page"
position="sidebar"
title="Sidebar Dashboard"
canvasConfig={{ defaultColumns: 1, collapsible: true }}
/>
<div className="main-content">
<RegistersTable />
<ExtensionSlot
id="registers-inline"
pageId="registers-page"
position="after-table"
title="Analysis Widgets"
/>
</div>
</div>
);
};
Benefits
✅ Advantages
- Simple Implementation - Leverages existing layout system
- Predictable UX - Clear insertion points, no guessing
- Maintainable - Standard React components, no DOM manipulation
- Flexible - Can add multiple slots per page with different configs
- Compatible - Works with existing canvas and widget systems
🎯 User Experience
- View Mode - Extension slots are invisible, page looks normal
- Edit Mode - Slots show "Add Canvas Here" buttons with clear labels
- Canvas Added - Full canvas functionality within the slot
- Persistent - Canvases save to API with rest of layout data
This approach gives you 80% of the functionality with 20% of the complexity compared to universal DOM insertion! 🚀