This library allows you to build a custom UI for the Zakeke 3D configurator tool. Create your own unique and immersive 3D experience with ease!
Before you can use this project, you will need to install Node.js, a JavaScript runtime built on Chrome's V8 JavaScript engine. Here's how to do it:
-
Download the latest version of Node.js from the official Node.js website.
-
Run the installation file and follow the prompts in the Node.js Setup Wizard.
-
To confirm that Node.js was installed correctly, open your command prompt and run the following command:
node -v
You should see your installed version of Node.js displayed in the command prompt.
To get started with this project, follow these steps:
- Clone the project repository using the following command:
git clone https://github.com/UpCommerce/zakeke-configurator-react-example.git
- Install the necessary dependencies:
npm install
- Start the project:
npm run start
After running the example, a blank page will open on localhost:3000
. This is because Zakeke requires some parameters to know what product to load. Let's get these parameters!
Follow these steps to get all the parameters:
-
Go to the Zakeke configurator backoffice.
-
Add or edit a new product.
-
Navigate to the Shopping Preview page.
-
Right-click on the page -> inspect element.
-
Find the Zakeke
<iframe>
src attribute and copy all query string parameters. -
Paste the parameters in the localhost page.
Your URL should look something like this:
http://localhost:3000?modelCode=XXX&culture=XX&token=XXXXXX....
With these parameters, you should see the product loading in a basic UI interface. Start customizing it to your needs!
The library exposes four key elements:
-
ZakekeEnvironment
: A class that contains the state of the current scene. -
ZakekeProvider
: A React component that should wrap your application. -
ZakekeViewer
: A React component that will render the 3D scene. -
useZakeke
: An effect to retrieve data and execute methods.
Here is how a basic Zakeke theme would look like:
const zakekeEnvironment = new ZakekeEnvironment();
<ZakekeProvider environment={zakekeEnvironment}>
<div>
<ZakekeViewer />
</div>
</ZakekeProvider>
useZakeke
exposes the following values:
price: number;
isOutOfStock: boolean;
culture: string;
currency: string;
isSceneLoading: boolean;
isAddToCartLoading: boolean;
isInfoPointContentVisible: boolean;
isViewerReady: boolean;
fonts: FontFamily[];
disableTextColors: boolean;
defaultColor: string;
textColors: RestrictionColor[];
groups: Group[];
templates: Template[];
loadedComposition: { templateName: string; attributesOptions: Map<number, number>; selectedCategoryID: number | null } | null;
currentTemplate: Template | null;
items: Item[];
productName: string;
productCode: string;
product: Product | null;
cameras: Camera[];
sellerSettings: SellerSettings | null;
quantityRule: ProductQuantityRule | null;
eventMessages: EventMessage[] | null;
personalizedMessages: EventMessage[] | null;
visibleEventMessages: VisibleEventMessage[];
isAssetsLoading: boolean;
draftCompositions: ThemeCompositions[] | null;
additionalCustomProperties: { name: string; value: number; label: string; formatString: string }[] | null;
currentAttributesSelection: object | null;
currentCompositionInfo: { compositionId: string | null; compositionName: string | null; compositionTags: string[] | null } | null;
translations: Translations | null;
nftSettings: NftSettings | null;
useLegacyScreenshot: boolean;
isAIEnabled: boolean;
uiConfig: UIConfig | null;
backgroundColor: string;
removeBackground: boolean;
setMouseWheelZoomEnabled: (enabled: boolean) => void;
selectOption: (optionId: number) => void;
/**
* @internal
*/
internalAppendViewer: (container: HTMLElement) => void;
getPrintingMethodsRestrictions: () => PrintingMethodsRestrictions;
designUpdate: () => void;
createQuote: (formData: any) => Promise<any>;
setTemplate: (templateId: number) => Promise<void>;
isAreaVisible: (areaId: number) => boolean;
quantity: number;
saveTemplate: (templateName: string, selectedCategoryID: number | null, attributesOptions: Map<number, number>) => Promise<void>;
addToCart: (
additionalProperties: Record<string, any>,
OnBeforeSendDataToParent?: (data: OnBeforeSendDataToParent) => Promise<Record<string, any>>,
legacyScreenshot?: boolean,
nftForm?: NftForm
) => Promise<void>;
/**
* Create a PDF of the current configuration
* @returns The URL of the PDF
*/
getPDF: () => Promise<string>;
getOnlineScreenshot: (
width: number,
height: number,
legacyScreenshot?: boolean,
backgroundColor?: string,
padding?: number
) => Promise<{ originalUrl: string; rewrittenUrl: string }>;
setCamera: (id: string, onlyAngleOfView?: boolean, animate?: boolean) => void;
setCameraByName: (name: string, onlyAngleOfView?: boolean, animate?: boolean) => void;
setCameraZoomEnabled: (enabled: boolean) => void;
resetCameraPivot: () => void;
setCameraPivot: (meshId: string) => void;
fullyLoadFont: (fontFamily: string | FontFamily) => Promise<FontFamily>;
sanitizeString: (family: FontFamily, text: string) => string;
getSanitationText: (family: FontFamily, text: string) => TextSanitationResult;
getImages: (categoryId: number) => Promise<ZakekeImage[]>;
getMacroCategories: () => Promise<ImageMacroCategory[]>;
previewOnly__setItemImageFromBase64: (guid: string, base64: string) => void;
setItemImageFromFile: (guid: string, file: File) => Promise<void>;
addItemImage: (id: number, areaId: number) => Promise<void>;
/**
* Upload an image and get the uploaded image
* @param file The file to upload
* @param progress A callback to get the upload progress
* @returns The uploaded image
*/
createImage: (file: File, progress?: (percentage: number) => void) => Promise<ZakekeImage>;
createImageFromUrl: (url: string) => Promise<ZakekeImage>;
setItemImage: (guid: string, imageId: number) => Promise<void>;
setItemFontFamily: (guid: string, fontFamily: string) => void;
setItemColor: (guid: string, color: string) => void;
setItemBold: (guid: string, bold: boolean) => void;
setItemItalic: (guid: string, italic: boolean) => void;
setItemTextOnPath: (guid: string, areaId: number, value: boolean) => void;
setItemText: (guid: string, text: string) => void;
addItemText: (settings: { text: string; fontFamily: string }, areaId: number) => Promise<void>;
/**
* Remove an item from the customization
* @param guid The guid of the item to remove
*/
removeItem: (guid: string) => Promise<void>;
setQuantity: (quantity: number) => void;
getShareCompositionUrl: () => Promise<string>;
saveComposition: (customPreviewSize?: CustomPreviewSize) => Promise<void>;
loadComposition: (id: string) => Promise<void>;
switchFullscreen: () => void;
openSecondScreen: () => void;
isFullscreenMode: boolean;
zoomIn: () => void;
zoomOut: () => void;
updateView: (adjustCamera?: boolean) => void;
setHighlightSettings: (settings: { color: string; size: number }) => void;
hasExplodedMode: () => boolean;
isExplodedMode: boolean;
setExplodedMode: (exploded: boolean) => void;
// Scene manipulation
getMeshIDbyName: (name: string) => string | undefined | null;
hideMeshAndSaveState: (meshId: string) => void;
restoreMeshVisibility: (meshId: string) => void;
setMeshDesignVisibility: (meshId: string, visible: boolean) => void;
// Events
clearListeners: () => void;
addFocusAttributesListener: (listenerFunction: FocusAttributesEventListener) => void;
focusAttribute: (attributeId: number) => void;
// Highlight
highlightGroup: (groupId: number) => void;
highlightAttribute: (attributeId: number) => void;
// AR
getQrCodeArUrl: (device: 'iOS' | 'Android') => Promise<string>;
getMobileArUrl: (onFlyArUrl?: string) => Promise<Blob | string | null>;
openArMobile: (url: string) => void;
isSceneArEnabled: () => boolean;
isArDeviceCompliant: () => boolean;
IS_ANDROID: boolean;
IS_IOS: boolean;
setBackgroundColor: (color: string, alpha: number) => void;
getTryOnUrl: (tryOnUrl?: string) => Promise<string>;
isSceneTryOnEnabled: () => boolean;
setCameraLocked: (isBlocked: boolean) => void;
getTemplateUploadRestrictictions: (templateId: number, areaId: number) => TemplateUploadRestrictictions;
saveDraftsComposition: (name: string, tags: string[], isCopy?: boolean) => Promise<void>;
loadSavedComposition: (docID: string) => Promise<void>;
deleteSavedComposition: (docId: string) => Promise<void>;
exportSceneToGlb: () => Promise<string | Blob | null>;
// reset, undo, redo
reset: () => Promise<void>;
undo: () => Promise<void>;
redo: () => Promise<void>;
// copyright checkbox
getCopyrightMessageAccepted: () => boolean;
setCopyrightMessageAccepted: (copyrightMandatoryCheckbox: boolean) => void;
validationNFTEmail: (email: string) => boolean;
validationNFTWalletAddress: (walletAddress: string) => boolean;
templateMacroCategories: TemplateMacroCategory[] | null;
applyTemplate: (templateGroupCompositionId: number) => Promise<void>;
configureByAI: (text: string) => Promise<void>;
hasVTryOnEnabled: boolean;
canUseTryOn: boolean;
canUsePD: boolean;
getTryOnSettings: () => Zakeke.TryOnSettings | undefined;
isTryOnMeshVisible: boolean;
isMandatoryPD: boolean;
isVisibleMeshShownForTryOn: boolean;
setPDDistance: (distance: number) => void;
pdDistance: () => number;
exportTryOnMeshToGlb: () => Promise<string | Blob | null>;
You can find a full and updated list of the values exposed by useZakeke
in the source code (the definitions files) of the library.
Please refer to the source code comments for a detailed understanding of each value and their usage.
Note: Always ensure to handle these values appropriately in your React components to ensure a smooth user experience.