Skip to main content

Components

ChamevoJS provides a set of StencilJS web components that work with any framework (React, Vue, Angular, Svelte) or vanilla HTML.

Available Components

Main Components

ComponentTagDescription
CvCustomizer<cv-customizer>Root wrapper - orchestrates canvas, events, keyboard
CvCanvas<cv-canvas>Canvas container with resize observer
CvSizeTooltip<cv-size-tooltip>Displays W × H dimensions above selected image elements
CvActionButton<cv-action-button>Standalone action button for use outside the customizer

Shared Components

ComponentTagDescription
CvButton<cv-button>Button with variant, size, disabled
CvTabs<cv-tabs>Tab navigation with panels
CvModal<cv-modal>Modal dialog with backdrop
CvTooltip<cv-tooltip>Tooltip positioned around a trigger

Registration

Register all components once in your entry point:

import { defineCustomElements } from '@chamevo/customizer';

// Register all cv-* custom elements
defineCustomElements();

Or from the components package directly:

import { defineCustomElements } from '@chamevo/components/loader';

defineCustomElements();

cv-customizer

The root component that creates a ChamevoCanvas and ChamevoJS orchestrator, wires events to Stencil Store, and sets up keyboard controls.

Properties

PropertyTypeDefaultDescription
optionsstring | Partial<ChamevoOptions>'{}'Customizer configuration (JSON string or object)
proxyFileServerstring''Proxy URL for images and fonts

Events

EventDetailDescription
cvReady{ chamevo: ChamevoJS, canvas: ChamevoCanvas }Customizer initialized
cvProductCreate{ product: CVProduct }Product loaded
cvViewSelect{ view: CVView, index: number }Active view changed
cvElementSelect{ element: unknown }Element selected/deselected
cvPriceChange{ price: number, singleProductPrice: number }Price updated

Methods

MethodReturnsDescription
loadProduct(product)Promise<void>Load a product (CVProduct or flat CVView[])
selectView(index)Promise<void>Switch active view
getProduct()Promise<CVProduct>Get serialized product
getOrder()Promise<unknown>Get full order data
getElements(viewIndex?, type?)Promise<unknown[]>Get serialized elements
undo()Promise<void>Undo last action
redo()Promise<void>Redo last undone action
reset()Promise<void>Clear all views

Slots

cv-customizer uses named slots for layout customization:

<cv-customizer options='{"stageWidth": 900, "stageHeight": 600}'>
<div slot="toolbar-top">Custom top toolbar</div>
<div slot="sidebar">Custom sidebar</div>
<div slot="panel">Custom right panel</div>
<div slot="toolbar-bottom">Custom bottom toolbar</div>
</cv-customizer>
SlotPosition
toolbar-topAbove the canvas area
sidebarLeft of the canvas
panelRight of the canvas
toolbar-bottomBelow the canvas area

Keyboard Controls

When an element is selected on the canvas, cv-customizer handles:

KeyAction
Arrow keysMove element by 1px
Shift+ArrowMove element by 10px
DeleteRemove selected element
BackspaceRemove selected element

Keyboard input is ignored when focus is inside <input>, <textarea>, or contentEditable elements.

Usage Example

import { defineCustomElements } from '@chamevo/customizer';

defineCustomElements();

const customizer = document.querySelector('cv-customizer');

customizer.addEventListener('cvReady', async (e) => {
const { chamevo, canvas } = e.detail;

await customizer.loadProduct({
title: 'T-Shirt',
views: [
{
title: 'Front',
elements: [
{
type: 'image',
source: '/images/tshirt.png',
title: 'T-Shirt',
parameters: { left: 450, top: 300, draggable: false },
},
],
},
],
});
});

cv-canvas

Container component that provides the <canvas> element and monitors container resize.

Events

EventDetailDescription
cvCanvasResize{ width: number, height: number }Container resized

Methods

MethodReturnsDescription
getCanvasElement()Promise<HTMLCanvasElement>Returns the internal canvas element

cv-button

Reusable button component with variants and sizes.

Properties

PropertyTypeDefaultDescription
variant'primary' | 'secondary' | 'ghost''primary'Visual style
size'sm' | 'md' | 'lg''md'Button size
disabledbooleanfalseDisabled state
<cv-button variant="primary" size="md">Click me</cv-button>
<cv-button variant="ghost" size="sm">Cancel</cv-button>

CSS Theming

All components use CSS custom properties (--cv-*) for theming. Variables are defined on :root and cascade through shadow DOM boundaries.

Global Tokens

:root {
/* Primary palette */
--cv-primary: #2185d0;
--cv-secondary: #6c757d;
--cv-success: #21ba45;
--cv-error: #db2828;
--cv-warning: #fbbd08;

/* Surfaces */
--cv-bg: #ffffff;
--cv-bg-secondary: #f8f9fa;

/* Text */
--cv-text: #212529;
--cv-text-secondary: #6c757d;

/* Borders */
--cv-border: #dee2e6;

/* Border radius */
--cv-radius: 0.375rem;
--cv-radius-sm: 0.25rem;
--cv-radius-lg: 0.5rem;

/* Layout */
--cv-toolbar-height: 48px;
--cv-sidebar-width: 280px;
--cv-panel-width: 260px;
}

Customizing

Override variables on the cv-customizer element or any ancestor:

cv-customizer {
--cv-primary: #e11d48;
--cv-radius: 0.5rem;
--cv-sidebar-width: 300px;
}

Dark Mode

Add the cv-dark class to <cv-customizer> or any ancestor:

<cv-customizer class="cv-dark" options='{"stageWidth": 900}'>
</cv-customizer>

Dark mode overrides color tokens while keeping layout, radius, and transitions unchanged:

.cv-dark {
--cv-primary: #3b9eed;
--cv-bg: #0f172a;
--cv-bg-secondary: #1e293b;
--cv-text: #f1f5f9;
--cv-text-secondary: #94a3b8;
--cv-border: #334155;
}

Stencil Store

Components share reactive UI state via @stencil/store:

interface UIState {
chamevo: ChamevoJS | null; // Core orchestrator reference
canvas: ChamevoCanvas | null; // Canvas instance reference
currentModule: string; // Active module in mainbar
sidebarOpen: boolean; // Whether sidebar is open
selectedElementId: string | null; // Currently selected element
isLoading: boolean; // Loading state
modalOpen: string | null; // Which modal is open
currentViewIndex: number; // Active view index
viewCount: number; // Total views in product
canUndo: boolean; // Undo available
canRedo: boolean; // Redo available
currentPrice: number; // Current total price
}

The cv-customizer component populates the store on initialization. Other components read from the store to stay in sync without prop-drilling.