Elements
In Ydesign, "elements" are Fabric.js FabricObject instances. We don't wrap them in another class (as Polotno's Element does), so every capability is available either through store helpers or directly via Fabric's own API.
All store.editor.xxxHandler.xxx() calls below are real methods exposed from @ydesign/core handlers (ObjectsHandler, LayerHandler, AlignmentHandler, etc.).
Element types
Common type values (identical to Fabric's):
| type | Description |
|---|---|
textbox | Rich text box (multi-line, resizable) |
image | Bitmap image |
path | SVG path (from the Shapes panel or an imported SVG) |
rect / circle / ellipse / triangle | Basic geometric shapes |
group | A group containing objects: FabricObject[] |
workarea | The workarea (background board), auto-created and unselectable |
⚠️
workareais a specialrectwith a fixed id'workarea', managed byWorkareaHandler. It is not a user element and won't show up in selection results.
Adding elements
From a known JSON shape
// Text
store.addElement({
type: 'textbox',
text: 'Hello Ydesign',
left: 100,
top: 100,
width: 400,
fontSize: 48,
fontFamily: 'Inter',
fill: '#111',
});
// Image
store.addElement({
type: 'image',
src: 'https://cdn.example.com/photo.jpg',
left: 50,
top: 50,
scaleX: 0.5,
scaleY: 0.5,
});
// Shape
store.addElement({
type: 'rect',
left: 200,
top: 200,
width: 300,
height: 200,
fill: '#2253eb',
rx: 12,
ry: 12,
});
addElement(options, isHistory?, autoSelect?):
options— the element JSONisHistory— push to the undo stack, defaults totrueautoSelect— select after adding, defaults totrue
Path (custom shape)
// A five-pointed star
store.editor?.objectsHandler.createPathElement(
'M 100,10 L 125,85 L 200,85 L 140,130 L 165,205 L 100,160 L 35,205 L 60,130 L 0,85 L 75,85 Z',
100, // left
100, // top
);
Preset shapes (used by the Shapes panel)
store.editor?.objectsHandler.createShapeElement('roundRect', 50, 50);
store.editor?.objectsHandler.createShapeElement('triangle', 200, 200);
// See `ShapePathFormulasKeys` in `@ydesign/react-editor`'s `config.ts` for the full list.
Updating properties
Any Fabric FabricObject property can be updated:
// Apply to the current selection
store.set({ opacity: 0.8 });
store.set({ angle: 45 });
store.set({ fill: '#ff6a00' });
// Apply to a specific element
store.set({ stroke: '#000', strokeWidth: 2 }, element);
// Or by id via the handler directly
store.editor?.objectsHandler.update({ left: 200, top: 300 }, 'element-id');
Common properties
| Group | Properties |
|---|---|
| Position | left, top |
| Size | width, height, scaleX, scaleY |
| Rotation | angle |
| Flip | flipX, flipY |
| Opacity | opacity |
| Fill | fill (color or gradient object) |
| Stroke | stroke, strokeWidth, strokeDashArray |
| Corner radius (rect) | rx, ry |
| Text | text, fontSize, fontFamily, fontWeight, fontStyle, lineHeight, letterSpacing, textAlign |
| Image | src, cropX, cropY, filters |
Text-specific helpers
// Apply a style to selection / the whole textbox
store.editor?.objectsHandler.setTextStyle({
fontWeight: 'bold',
fontStyle: 'italic',
underline: true,
}, 'textbox-id');
Shadow
store.editor?.objectsHandler.setShadow({
color: 'rgba(0,0,0,0.3)',
blur: 10,
offsetX: 4,
offsetY: 4,
});
Finding elements
// By id
store.getElementById('abc123');
store.editor?.objectsHandler.findOneById('abc123');
// By predicate (recurses into groups)
const firstText = store.find(e => e.type === 'textbox');
// By name (your custom field)
store.editor?.objectsHandler.findByName('watermark');
store.editor?.objectsHandler.removeByName('watermark');
// All canvas objects (excluding workarea)
const all = store.editor?.layerHandler.getEffectiveLayers();
Selection
// Select one
store.editor?.objectsHandler.select('element-id');
// Select many (creates an ActiveSelection)
store.editor?.objectsHandler.selectElements([el1, el2, el3]);
// Or by id list via the store
store.selectElements(['id-1', 'id-2']);
// Deselect
store.editor?.objectsHandler.deselect();
// Read current selection
store.selectedElements; // FabricObject[] (may include groups)
store.selectedShapes; // FabricObject[] (flattened, groups excluded)
Deleting & cloning
// Delete current selection
store.editor?.objectsHandler.remove();
// Delete by id
store.editor?.objectsHandler.removeById('element-id');
// Batch delete
store.deleteElements(['id-1', 'id-2']);
// Clone current selection
store.clone();
// Nudge position with arrow keys
store.editor?.objectsHandler.arrowMoving({
direction: 'up',
amount: 10,
id: 'element-id', // defaults to the active selection
});
Layer operations
Layers in Ydesign are simply the order of objects in Fabric's internal array — later items render on top.
store.moveElementsUp(['id-1']);
store.moveElementsDown(['id-1']);
store.moveElementsTop(['id-1']);
store.moveElementsBottom(['id-1']);
// Button-state helpers
store.canMoveElementsUp(['id-1']);
store.canMoveElementsTop(['id-1']);
store.canMoveElementsDown(['id-1']);
store.canMoveElementsBottom(['id-1']);
// Or via the handler
store.editor?.layerHandler.bringForward('id-1');
store.editor?.layerHandler.bringToFront('id-1');
store.editor?.layerHandler.sendBackwards('id-1');
store.editor?.layerHandler.sendToBack('id-1');
// List effective layers (workarea filtered out)
const layers = store.editor?.layerHandler.getEffectiveLayers();
Grouping
// Group the current selection
store.editor?.objectsHandler.group();
// Ungroup the selected group
store.editor?.objectsHandler.ungroup();
Groups are Fabric Group instances; use group.getObjects() to access children.
Alignment & distribution
Provided by AlignmentHandler, operates on the current selection:
const align = store.editor?.alignmentHandler;
align?.alignLeft();
align?.alignCenter(); // horizontal center
align?.alignRight();
align?.alignTop();
align?.alignMiddle(); // vertical middle
align?.alignBottom();
align?.distributeHorizontal();
align?.distributeVertical();
Locking
store.editor?.lockHandler.lock();
store.editor?.lockHandler.unlock();
store.editor?.lockHandler.isLocked(element);
Image-specific: crop / filters / stroke / eraser
// Crop (double-click on an image also triggers this)
store.editor?.imageCropHandler.cropImg.onEnterCrop(imageElement);
// Filters (grayscale, sepia, blur, hue rotation, …)
store.editor?.imageFiltersHandler.apply(imageElement, 'grayscale');
store.editor?.imageFiltersHandler.apply(imageElement, 'brightness', { value: 0.2 });
// Image stroke (dashed / inner / outer)
store.editor?.imageStrokeHandler.setStroke(imageElement, {
color: '#000',
width: 4,
style: 'dashed',
position: 'outer',
});
// AI eraser (brush / rect / lasso)
store.editor?.inpaintHandler.activate('brush');
store.editor?.inpaintHandler.setBrushSize(20);
// Export mask + base image so your backend can inpaint
const { maskBlob, imageBlob } = await store.editor!.inpaintHandler.exportMaskAndImage();
See Customizations.
Listening to changes
Canvas object changes
const dispose = store.on('change', objects => {
console.log(`${objects.length} objects on canvas`);
});
dispose();
Core-layer events
Editor itself is an event bus (EventManager). You can subscribe to finer-grained events:
store.editor?.on('object:modified', obj => {
console.log('object modified', obj);
});
store.editor?.on('textbox:modified', obj => {
console.log('textbox modified', obj);
});
store.editor?.on('workarea:changed', ({ target }) => {
console.log('workarea size changed', target.width, target.height);
});
store.editor?.on('history:changed', data => {
console.log('history stack changed', data);
});
Event names come from handlers inside core — search for this.editor.emit( in @ydesign/core for the complete list.
Relationship with Fabric.js
Ydesign doesn't hide Fabric's low-level API — if a handler doesn't expose what you need, reach straight for the Fabric Canvas:
const fabricCanvas = store.editor!.customCanvas.canvas;
fabricCanvas.forEachObject(obj => {
console.log(obj.type, obj.id);
});
fabricCanvas.setActiveObject(someObj);
fabricCanvas.requestRenderAll();
But prefer handler methods whenever possible, because handlers also:
- Keep the store's reactive state in sync
- Push history entries
- Handle guidelines / snapping / selection state
- Preserve undo/redo semantics
Mutating Fabric objects directly tends to drop these.