Gallery Plugin

You can use this plugin to browse through images that either can be organized into subfolders or into one big folder. This plugin also has some basic image editing features built in as well.

With this you can open a gallery from our Email editor or Variable editor plugin with their hooks.

You can initialize this plugin with the following function call:

const galleryConfig = {
	plugin: "gallery",
	data: {
		currentImgSrc, // The current image
		dimensions, // Fixed image dimensions.
	},
	settings: {
		hideHeader: false, // hides the header
		folderTree: {
			_id: root,
			name: "Root folder",
			children: [{}], 
		}, // Object following the folder structure you want to use, every folder has an _id and a name. The optional children array contains the subfolder objects.
		selectedFolderId: "root", // The _id of the currently selected folder.
		maxImagePerPage: 12, // Set the number of images
		maxFileSize: 5, // Set the maximum size of uploadable images in Megabytes
	},
	hooks: {}, // an object of available hooks (for example onFolderSelected)
};

// Fullscreen
const galleryInstance = await chamaileonPlugins.createFullscreenPlugin(galleryConfig);

// Inline
const galleryInstance = await chamaileonPlugins.createInlinePlugin(
	galleryConfig,
	{
		container: "#gallery", /* HTML Element */
		dimensions: {
			width: 1000, // default 100%
			height: 720, // default 100%
			scale: 1, // default 1
		}
	}
);

Gallery instance functions

These will be returned after a successful plugin initialization.

galleryInstance.methods

These are methods provided by the plugin instance.

galleryInstance.methods.updateSettings

Updates the settings inside the plugin instance on the fly.

const newSettings = {
	maxImagePerPage: 15,
	selectedFolderId: "otherFolderId",
}

await galleryInstance.methods.updateSettings(newSettings);

galleryInstance.methods.updateData

Updates the data inside the plugin instance on the fly.

await galleryInstance.methods.updateData({ editImgSrc, dimensions });

galleryInstance.methods.updateHooks

Updates the hooks inside the plugin instance on the fly.

You can read more about the updateHooks method here.

await galleryInstance.methods.updateHooks({ hooks, resetHooks });

galleryInstance.methods.pickImage

Returns the src of the image that was selected inside the gallery. You can await this method right after the plugin is initialized or updated and it will return a Promise that will be resolved if the user choses an image.

const { src } = await galleryInstance.methods.pickImage();

galleryInstance.show

Shows the preview instance iframe. Fullscreen mode only.

You can read more about the optionalParams object here.

await galleryInstance.show(optionalParams);

galleryInstance.hide

Hides the preview instance iframe. Fullscreen mode only.

await galleryInstance.hide();

galleryInstance.showSplashScreen

Shows the splash screen inside the preview instance. Fullscreen mode only.

await galleryInstance.showSplashScreen();

galleryInstance.hideSplashScreen

Hides the splash screen inside the preview instance. Fullscreen mode only.

galleryInstance.hideSplashScreen();

galleryInstance.destroy

Destroys the preview instance.

await galleryInstance.destroy();

Gallery configuration

Property Type Description
data object The initial data of the plugin instance
settings object The initial settings of the plugin instance
hooks object You can register callbacks on multiple events coming from the plugin. For more, please check out the galleryConfig.hooks section.

galleryConfig.data

The data object has the following properties:

Property Type Description
currentImgSrc object The current image src.
dimensions object You can fix the returned image dimensions with this parameter.

galleryConfig.data.dimensions

Property Type Description
width integer The fixed image width.
height integer The fixed image height.

galleryConfig.settings

The settings object has the following properties:

Property Type Description
folderTree object To organize the image assets in a user-friendly way.
selectedFolderId string With Folder Tree defined, you can specify which folder will be the default for the gallery instance. The Gallery will start a request to fetch images from this folder at the initialization.
maxImagePerPage number The gallery uses infinite scrolling as pagination, and you can decide how many pictures are loaded for a single request. The default is 12.
maxFileSize number With default 5 MB, you can alter the maximum file size (in megabytes) users can upload to your server. The field requires a number as a value.

galleryConfig.settings.folderTree

The folder tree is designed to help with the organization of image assets in a user-friendly way. It's recommended to follow the folder tree structure of your emails.

Each folder has the following properties:

Property Type Description
_id string The _id of the folder.
name string The name of the folder.
children array An array containing the subfolder objects.
canCreateSubfolder boolean If true, the user will be able to create subfolders inside this folder. If false or missing, the subfolder creation is disabled.
canRename boolean If true, the user will be able to rename this folder. If false or missing, the folder cannot be renamed. This parameter does not apply to the root folder.
canDelete boolean If true, the user will be able to delete this folder. If false or missing, the folder cannot be deleted. This parameter does not apply to the root folder.

Example:

galleryConfig.settings.folderTree = {
	_id: "root",
	name: "Root folder",
	canCreateSubfolder: true,
	children: [
		{
			_id: "021548489999",
			name: "Favorite Images",
			canCreateSubfolder: true,
			canRename: true,
			canDelete: false,
		},
	], 
};

galleryConfig.hooks

Each and every hook should be an asynchronous process, so all of the hook handler functions have to return Promises.

In most cases you just have to resolve the promise when the async operation is done without any params, but in some cases, you will have to resolve certain objects with properties that the plugin can use. Similarly to the parameters, we always expect an object to be resolved, even if it only has one property. (This way it will be easier to add new properties later on if needed.)

If any errors occurred on your side, you can reject the promise with an instance of Error. In this case, the error message will be shown in a snackbar inside the plugin instance.

function handler(params) {
	return new Promise((resolve, reject) => {
		// You can put the logic here.
		// Resolve the promise when everything is okay
		// Reject the promise on error

		if (!error) {
			resolve(dataToResolve) // In some cases, you don't have to resolve any data. You can resolve the promise without a parameter.
		} else {
			reject(new Error("Your error message"))
		}
	})
}

Note that, with the async syntax, the unexpected errors will be also displayed:

async function handler(params) {
	// Unexpected errors will also cause promise rejections in this case
	// For example, if you get a timeout error, that will also be displayed in a snackbar in the editor.
	// Any exception will be caught by the SDK and the message property of the error object will be shown in a snackbar.
	return dataToResolve;
}

Below are the list of hooks that you can use. Read more about them in the following sections.

galleryConfig.hooks = {
	close,
	onUploadImage,
	onSaveUrl,
	onUpdateImage,
	onDeleteImage,
	onFolderSelected,
	onCreateSubfolder,
	onRenameFolder,
	onDeleteFolder,
};

galleryConfig.hooks.close

This hook is called when the top left back arrow button is clicked. You should hide the plugin with this and you can also implement a custom logic that runs after the plugin is hidden.

/*
Params: nothing.

Has to resolve: nothing.
*/
galleryConfig.hooks.close = () => {
	return new Promise(resolve => {
		galleryInstance.hide();
		resolve();
	});
};

galleryConfig.hooks.onFolderSelected

There are a few cases when this function is invoked. The first one is right after the initialization, the second case is when someone selects a folder, the third is when someone searches for an image in the folder and the fourth is when the user scrolls down through the images and more of them needs to be loaded.

Based on the parameters of this function, you can fetch the corresponding data from your backend, and resolve it as an object that contains the images array and the count of the query.

/*
Params:
 - selectedFolderId: current folder id.
 - parents: returns an array that contains every parent of the current folder
 - searchValue: search string from the search input.
 - orderValue: in which order are the images currently sorted.
 - maxImagePerPage: maximum number of images in a single page.
 - pageNumber: current page number inside the gallery.

Has to resolve:
 - images: an array with objects that contain the image information
   - each object has to contain:
     - name
     - src
     - _id
 - count: the maximum number of images that match every criteria provided in the parameters
*/

galleryConfig.hooks.onFolderSelected = ({
	selectedFolderId, parents, searchValue, orderValue, maxImagePerPage, pageNumber,
}) => {
	return new Promise(resolve => {
		// you can put  the logic here that filters, orders and fetches the images.
		// when it's done, you can resolve the promise.
		resolve({ images, count });
	});
};

galleryConfig.hooks.onUploadImage

This function is called when an image is uploaded or when a stock photo is chosen to be saved.

The image is encoded to Base64 format. If you want to convert it to a binary format, this is the place where you can do that before sending it to your backend.

/*
Params:
 - selectedFolderId: current folder id.
 - parents: returns an array that contains every parent of the current folder
 - image:
   - data: Base64 encoded image data
   - name: image name

Has to resolve:
 - image: the final image object with _id, name and src fields
*/

galleryConfig.hooks.onUploadImage = ({ selectedFolderId, parents, image }) => {
	return new Promise(resolve => {
		// you can put  the logic here that saves the image in your database.
		// when it's done, you can resolve the promise.
		resolve({ _id, name, src });
	});
	};

galleryConfig.hooks.onSaveUrl

This function is called when an image is saved from an URL. In this case we send the plain URL instead of the encoded image data.

/*
Params:
 - selectedFolderId: current folder id.
 - parents: returns an array that contains every parent of the current folder
 - image:
   - url: image url
   - name: image name

Has to resolve:
 - image: the final image object with _id, name and src fields
*/

galleryConfig.hooks.onSaveUrl = ({ selectedFolderId, parents, image }) => {
	return new Promise(resolve => {
		// you can put here the logic to save the image url in your database
		// and when it's done, you can resolve the promise.
		resolve({ _id, name, src });
	});
};

galleryConfig.hooks.onUpdateImage

This function is called when an image title is changed inside the plugin.

/*
Params:
 - imageId: the id of the updated image
 - selectedFolderId: current folder id.
 - parents: returns an array that contains every parent of the current folder
 - image:
   - data: image url
   - name: image name

Has to resolve: nothing.
*/

galleryConfig.hooks.onUpdateImage = ({ imageId, parents, selectedFolderId, image }) => {
	return new Promise(resolve => {
		// you can put the logic here to save the new title.
		resolve();
	});
};

galleryConfig.hooks.onDeleteImage

This function is called when an image is deleted inside the plugin.

/*
Params:
 - imageId: the id of the updated image
 - selectedFolderId: current folder id.
 - parents: returns an array that contains every parent of the current folder

Has to resolve: nothing.
*/

galleryConfig.hooks.onDeleteImage = ({ imageId, parents, selectedFolderId }) => {
	return new Promise(resolve => {
		// you can put the logic here that deletes the image from your backend.
		resolve();
	});
};

galleryConfig.hooks.onCreateSubfolder

This function is called when a subfolder is created inside the plugin.

/*
Params:
 - selectedFolderId: the id of the folder in which the user created a subfolder
 - name: the name of the new subfolder
 - parents: returns an array that contains every parent of the current folder

Has to resolve:
 - _id: the id of the new subfolder
 - name: the name of the new subfolder (it can differ from the one given in the params)
 - canCreateSubfolder: true if the users will be able to create subfolders inside the new folder. Defaults to false
 - canRename: true if the users will be able to rename the new folder. Defaults to false
 - canDelete: true if the users will be able to delete the new folder. Defaults to false
*/

galleryConfig.hooks.onCreateSubfolder = ({ selectedFolderId, name, parents }) => {
	return new Promise(resolve => {
		// you can put the logic here that creates a subfolder.
		resolve();
	});
};

galleryConfig.hooks.onRenameFolder

This function is called when a folder is renamed inside the plugin.

/*
Params:
 - selectedFolderId: the id of the folder
 - name: the new name of the folder
 - parents: returns an array that contains every parent of the current folder

Has to resolve:
 - name: the new name of the folder (it can differ from the one given in the params)
*/

galleryConfig.hooks.onRenameFolder = ({ selectedFolderId, name, parents }) => {
	return new Promise(resolve => {
		// you can put the logic here that saves the new folder name.
		resolve();
	});
};

galleryConfig.hooks.onDeleteFolder

This function is called when a folder is deleted inside the plugin.

/*
Params:
 - selectedFolderId: the id of the folder
 - parents: returns an array that contains every parent of the current folder

Has to resolve: nothing.
*/

galleryConfig.hooks.onDeleteFolder = ({ selectedFolderId, parents }) => {
	return new Promise(resolve => {
		// you can put the logic here that deletes the folder.
		resolve();
	});
};

CORS

You need to enable CORS headers on the image sources that you will use inside the plugin. It's needed because we have to fetch the full image data and without the CORS headers present it will throw an error. When these headers are not present on the image source we will use a fallback proxy route but it can cause some delays with the loading.

Extra plugins

The gallery supports the PhotoEditor SDK extension with advanced image editing features. Please contact us if you are interested.

How to use it with our plugins

Usage in the email editor hooks

const editorInstance = await chamaileonPlugins.createFullscreenPlugin({
	plugin: "editor",
	data: {},
	settings: {},
	hooks: {
		onEditImage: async ({ originalImage, lockDimensions }) => {
			galleryInstance.methods.updateData({ currentImgSrc: originalImage, dimensions: lockDimensions });
			galleryInstance.show();
			const { src } = await galleryInstance.methods.pickImage();
			galleryInstance.hide();
			return { src };
		},
		onEditBackgroundImage: async ({	originalImage }) => {
			galleryInstance.methods.updateData({ currentImgSrc: originalImage, dimensions: null });
			galleryInstance.show();
			const { src } = await galleryInstance.methods.pickImage();
			galleryInstance.hide();
			return { src };
		},
	},
});

Usage in the variable editor hooks

const variableEditorInstance = await chamaileonPlugins.createFullscreenPlugin({
	plugin: "variable-editor",
	data: {},
	settings: {},
	hooks: {
		onEditImage: async () => {
			galleryInstance.methods.updateData({ currentImgSrc: "", dimensions: null });
			galleryInstance.show();
			const { src } = await galleryInstance.methods.pickImage();
			galleryInstance.hide();
			return { src };
		},
	},
});

Examples

You can also check out the gallery plugin on the Chamaileon SDK Playground.