Migration from FPD-JS
This guide covers breaking changes when migrating from the legacy FPD-JS (Fancy Product Designer) to ChamevoJS.
Package Changes
| FPD-JS | ChamevoJS |
|---|---|
fancy-product-designer | @chamevo/customizer (or @chamevo/core for headless) |
Global FancyProductDesigner | createCustomizer() factory or ChamevoJS class |
| jQuery dependency | No jQuery — pure Web Components |
Data Format Changes
Product Structure
Legacy (FPD-JS): Products were flat arrays. Product metadata lived on the first view.
[
{
"productTitle": "Hoodie",
"productThumbnail": "hoodie.png",
"product_id": "42",
"title": "Front",
"elements": []
},
{ "title": "Back", "elements": [] }
]
ChamevoJS: Products are CVProduct objects with views as a nested array. Products can carry product-level option overrides via options.
{
"id": "42",
"title": "Hoodie",
"thumbnail": "hoodie.png",
"options": {},
"views": [
{ "id": "front", "title": "Front", "elements": [] },
{ "id": "back", "title": "Back", "elements": [] }
]
}
Backward compatible: loadProduct() accepts both formats. Legacy arrays are auto-normalized at load time.
IDs on Products and Views
Products and views now carry stable id fields. When id is missing, it is auto-generated from title via generateSlugId() (lowercase, spaces to hyphens).
Data Model Changes
CVView new fields:
| Field | Type | Description |
|---|---|---|
locked | boolean | Whether the view is locked (optional view that user hasn't unlocked yet). |
CVProduct new fields:
| Field | Type | Description |
|---|---|---|
namesNumbers | NamesNumbersEntry[] | Names & Numbers roster shared across all views. Stored at product level and serialized in getProduct() output. |
Note: In earlier builds,
namesNumberswas stored per view inview.namesNumbers. It is now stored at the product level inproduct.namesNumbers. Legacy view-level data is auto-migrated to the product level onloadProduct().
CVProduct.options: Products can carry product-level option overrides (options?: Partial<ChamevoOptions>) that are merged at load time.
getProduct() / getOrder() Return Types
| Legacy | ChamevoJS | |
|---|---|---|
getProduct() return | CVView[] | null | CVProduct | null |
| Includes product metadata | No (on first view) | Yes (id, title, thumbnail, options) |
| View IDs | No | Yes (auto-generated if missing) |
getOrder() returns a richer object:
{
product: CVProduct | null;
usedFonts: FontInfo[]; // { name, url?, type? }
usedColors: ColorInfo[]; // { hex, name? } — name derived from hexNames option
}
Catalog Data Types
Product catalog (setProducts()):
CVProductCatalogInput supports flat product lists, categorized products, and both new and legacy formats:
// Flat (no categories)
customizer.setProducts([
{ title: 'T-Shirt', views: [...] },
{ title: 'Hoodie', views: [...] },
]);
// Categorized
customizer.setProducts([
{ category: 'Shirts', products: [{ title: 'T-Shirt', views: [...] }] },
]);
// Legacy format (auto-normalized)
customizer.setProducts([
[{ productTitle: 'T-Shirt', title: 'Front', elements: [...] }, { title: 'Back', ... }],
]);
Graphics catalog (setGraphics()):
CVGraphicsCatalogInput supports flat lists and recursive categories. The designs field name on categories is kept for legacy FPD-JS JSON compatibility:
customizer.setGraphics([
{
title: 'Nature',
category: [{ title: 'Animals', designs: [{ source: 'cat.svg', title: 'Cat', parameters: {} }] }],
},
]);
Event Changes
| FPD-JS Event | ChamevoJS Core Event | Web Component Event |
|---|---|---|
productCreate | productCreate | cvProductCreate |
viewSelect | viewSelect | cvViewSelect |
elementAdd | elementAdd | — |
elementRemove | elementRemove | — |
elementSelect | elementSelect | cvElementSelect |
priceChange | priceChange | cvPriceChange |
Event Listening
// FPD-JS (jQuery)
$('#fpd').on('productCreate', function(event, product) { ... });
// ChamevoJS (native events)
chamevo.on('productCreate', ({ product }) => { ... });
// Or on the web component
customizer.addEventListener('cvProductCreate', (e) => {
const { product } = e.detail;
});
Event Payload Changes
| Event | Legacy Payload | ChamevoJS Payload |
|---|---|---|
productCreate | { product: CVView[] } | { product: CVProduct } |
productChange | { product: CVView[] } | { product: CVProduct } |
Web component events (cv-customizer) forward core events with a cv prefix. See the Events guide for the complete event reference.
Configuration Changes
Options Cascade
Legacy: Options passed as a flat object, no scope awareness.
ChamevoJS: Options cascade with scope annotations (global, view, printArea):
OPTION_DEFAULTS -> mainOptions -> viewOptions -> printAreaProfile
Use chamevo.currentOptions to read the fully cascaded options for the active view/print area. See Options Cascade for details.
disabledModules vs mainBarModules:
mainBarModules— global only, controls which modules exist in the main bardisabledModules— participates in full cascade (global → view → print area), temporarily hides modules for specific contexts. Uses the same module IDs asmainBarModules.
Removed Options
| Option | Legacy Purpose | Reason Removed |
|---|---|---|
facebookAppId | Facebook login for image imports | Facebook API deprecated |
instagramClientId | Instagram API integration | Instagram Basic Display API sunset |
instagramRedirectUri | Instagram OAuth redirect | Removed with Instagram integration |
instagramTokenUri | Instagram OAuth token endpoint | Removed with Instagram integration |
designsJSON | URL to load designs JSON at init | Products/designs loaded via loadProduct() API |
productsJSON | URL to load products JSON at init | Products loaded via loadProduct() API |
loadFirstProductInStage | Auto-load first product on init | Explicit loadProduct() call required |
editorBoxParameters | Properties shown in editor helper box | Replaced by cv-element-toolbar component |
boundingBoxProps | Property keys synced to bounding box | Bounding box logic redesigned in core |
boundingBoxMode | Bounding box display mode | Only clipping mode was ever used; now always clipping. Legacy values (false, 0, '0') silently normalized to null. |
customAdds | Control which media types users can add | Use mainBarModules / disabledModules instead |
uploadZonesTopped | Keep upload zones on top of all elements | Removed; upload zone z-ordering simplified |
canvasHeight | Alias for stageHeight | Redundant; use stageHeight |
maxCanvasHeight | Maximum canvas height limit | Removed; canvas size controlled by stageHeight |
deselectActiveOnOutside | Deselect element on outside click | Always deselects (standard canvas behavior) |
outOfBoundaryColor | Color tint for out-of-bounds elements | Replaced by clipping/bounding box visual feedback |
autoFillUploadZones | Auto-fill upload zones with first upload | Upload zone behavior simplified |
dragDropImagesToUploadZones | Drag-drop files into upload zones | Upload zones handle drops natively |
rulerFixed | Lock ruler to fixed position | Ruler addon redesigned; always follows viewport |
rulerPosition | Ruler placement (top/bottom) | Ruler addon uses standard top+left placement |
imageLoadTimestamp | Append timestamp to image URLs | No longer needed — use HTTP cache headers instead |
fabricCanvasOptions | FabricJS canvas options | Managed internally |
mobileGesturesBehaviour | Mobile gesture handling | Built-in gesture handling |
responsiveBreakpoints | Layout breakpoint widths | Automatic based on container width |
New Options
| Option | Type | Default | Scope | Description |
|---|---|---|---|---|
labels | Partial<ChamevoLabels> | {} | global | UI label overrides for i18n. Replaces langJSON. |
maxColorableSVGPaths | number | 10 | view | Max paths in multi-path SVG for per-path color editing. |
disabledModules | string[] | [] | view | Hide specific modules per view/print area. |
uploadStorageScope | 'global' | 'scoped' | 'global' | global | 'scoped' creates separate upload buckets per view/print area. |
aiService | AIServiceConfig | — | global | AI service config for text-to-image. |
enableDynamicViews | boolean | false | global | Allow user to add/remove/duplicate views. |
dynamicViewsOptions | DynamicViewsOptions | — | view | Dynamic views config. |
optionalView | boolean | false | view | Make view optional (user must unlock). |
industry | { type, opts } | — | view | Industry-specific behavior (e.g. 'engraving'). |
actions | ActionsConfig | — | global | Action bar layout: { left, center, right }. |
toolbar | CVToolbarConfig | — | global | Toolbar config: { placement, dynamicContext, openTextInputOnSelect, filters }. |
unitOfMeasurement | RulerUnit | 'mm' | global | Unit for ruler and dynamic views. |
customizationRequiredRule | 'any' | 'all' | 'any' | global | Whether any or all views need customization. |
unsavedProductAlert | boolean | false | global | Alert when leaving with unsaved changes. |
downloadFilename | string | 'Product' | global | Filename for product downloads. |
replaceInitialElements | boolean | false | global | Only replace initial elements on product change. |
guidedTour | Record<string, string> | null | null | global | Guided tour steps. Key = target selector, value = text. |
modalMode | string | false | false | global | CSS selector for trigger element. Customizer opens as modal overlay on trigger click. |
layouts | CVLayoutItem[] | string | [] | view | Layout templates. Accepts array or URL to JSON. |
See Options Reference for the complete list.
Deprecated Options (Moved to Module Config)
These options still work in ChamevoOptions for backward compatibility but are deprecated in favor of the modulesConfig property:
| Legacy Option | Module Config Replacement |
|---|---|
langJSON | labels option (inline object, not URL) |
allowedImageTypes | modulesConfig.uploads.allowedTypes |
uploadAgreementModal | modulesConfig.uploads.agreementModal |
fileServerURL | modulesConfig.uploads.fileServerURL |
imageQualityRatings | modulesConfig.uploads.qualityRatings |
designCategories | modulesConfig.graphics.designCategories |
textTemplates | modulesConfig.text.templates |
disableTextEmojis | modulesConfig.text.disableEmojis |
swapProductConfirmation | modulesConfig.products.confirmSwap |
namesNumbersEntryPrice | modulesConfig.namesNumbers.entryPrice |
namesNumbersDropdown | modulesConfig.namesNumbers.dropdown |
bulkVariations | modulesConfig.bulkVariations.variations |
bulkVariationsPlacement | modulesConfig.bulkVariations.placement |
pixabayApiKey | modulesConfig.pixabay.apiKey |
pixabayHighResImages | modulesConfig.pixabay.highRes |
pixabayLang | modulesConfig.pixabay.lang |
saveActionBrowserStorage | modulesConfig.myDesigns.maxDesigns |
layersOnlyEditable | modulesConfig.layers.onlyEditable |
Removed Pricing Properties
| Removed Property | Notes |
|---|---|
canvasSize | Pricing rules based on canvas dimensions are no longer supported. Use coverage to price based on how much of the print area is filled. |
imageSize | Pricing based on raw image dimensions is no longer supported. Use coverage instead. |
imageSizeScaled | Pricing based on scaled image dimensions is no longer supported. Use coverage instead. |
Component Changes
jQuery → Web Components
| FPD-JS | ChamevoJS |
|---|---|
| jQuery UI | StencilJS Web Components (shadow DOM) |
#fpd container | <cv-customizer> element |
| CSS classes | CSS custom properties (--cv-*) |
$('#fpd').on('event') | customizer.addEventListener('cvEvent') |
CSS Theming
Legacy: Hardcoded styles, difficult to customize.
ChamevoJS: CSS custom properties (--cv-*) for all visual tokens. Override on cv-customizer or any ancestor:
cv-customizer {
--cv-primary: #e11d48;
--cv-radius: 0.5rem;
}
Dark mode: <cv-customizer color-scheme="dark">. UI style presets: <cv-customizer ui-style="rounded">.
See the Theming guide for all available tokens.
Module System
Legacy: Modules registered on global FancyProductDesigner.additionalModules. UI controllers tightly coupled.
ChamevoJS: Independent cv-module-* web components. Canvas addons via canvas.use(). Per-module typed config props with resolution order: config prop → modulesConfig → default. Action buttons can be placed externally via <cv-action-button>.
Designs → Graphics rename: The legacy "designs" module is now cv-module-graphics with CVGraphicsModuleConfig. The type alias CVDesignsModuleConfig is kept as deprecated.
Saved Designs
Legacy: SaveLoad module stored CVView[] in localStorage.
ChamevoJS: cv-module-my-designs stores CVProduct objects. Pluggable storage via CVDesignStorage interface (default: localStorage, 50-design limit). Configure via CVMyDesignsModuleConfig.
i18n
Legacy: Translator class loading external JSON language files via langJSON URL.
ChamevoJS: labels option in ChamevoOptions with Partial<ChamevoLabels>:
customizer.setOptions({
labels: { addText: 'Texto', save: 'Guardar' },
});
See the i18n guide for the complete label key reference.
FabricJS Version
| Legacy | ChamevoJS | |
|---|---|---|
| Version | FabricJS 5.x | FabricJS 7.x |
| Object pattern | fabric.util.createClass() | ES6 classes with CV prefix |
| Canvas | One per view | Single shared canvas |
Legacy: One FabricJS canvas per view (caused browser crashes with many views due to GPU/WebGL limits).
ChamevoJS: Single canvas shared across all views. Only the active view is rendered. View switching saves state, clears canvas, and renders the new view.
Removed Dependencies
| Legacy Dependency | ChamevoJS Replacement |
|---|---|
| jQuery | Native DOM / StencilJS |
| vanilla-picker + tinycolor2 | cv-color-picker (zero-dep HSV picker) |
| webfontloader | FontService (CSS Font Loading API) |
| AreaSortable | sortable.ts (Pointer Events utility) |
Upload Zone → Print Area Conversion
FPD-JS used uploadZone: true image elements as drop targets for user uploads. ChamevoJS replaces this concept with structured print areas (CVPrintArea).
No migration work needed — ChamevoJS automatically converts legacy upload zone elements into print areas during loadProduct(). You can keep your existing product data as-is.
What Happens Automatically
- Elements with
uploadZone: trueare detected during product loading - The image is preloaded to get its natural dimensions
- A print area is created with calculated bounding box and output dimensions
- The original image becomes the print area's placeholder (shown until the user adds content)
- The upload zone element is removed from the canvas element list
Property Mapping
| FPD-JS Upload Zone | ChamevoJS Print Area | Notes |
|---|---|---|
source | placeholder | Original image shown as placeholder |
left, top | printingBox.left, printingBox.top | Direct mapping |
naturalWidth × scaleX | printingBox.width | Calculated from image dimensions |
naturalHeight × scaleY | printingBox.height | Calculated from image dimensions |
title | id | Slugified: "uz_" + title |
| — | output.width, output.height | Auto-calculated in mm at 72 DPI |
| — | showIndicator | Always enabled |
Example
Legacy product data with an upload zone:
{
"type": "image",
"title": "Front Print Area",
"source": "/images/upload-zone.png",
"parameters": {
"uploadZone": true,
"left": 100,
"top": 150,
"scaleX": 0.5,
"scaleY": 0.5
}
}
ChamevoJS automatically converts this to a print area during loading — no code changes required. The placeholder image is displayed until the user adds their own content.
Serialization
Once converted, getProduct() returns the print area in the modern format:
{
"printAreas": [{
"id": "uz_front_print_area",
"printingBox": { "left": 100, "top": 150, "width": 200, "height": 150 },
"output": { "width": 70.56, "height": 52.92 },
"placeholder": "/images/upload-zone.png"
}]
}
Fallback
If the upload zone image fails to preload (e.g. broken URL), the element is kept as a regular image element instead of being converted.
Next Steps
- Getting Started — Fresh start with ChamevoJS
- ChamevoJS API — Full method and property reference
- Options Reference — Complete options list
- Module Configs — Per-module configuration
- Events — Complete event reference