You can install the Chamaileon SDK package from https://npmjs.com like this:
npm i @chamaileon-sdk/plugins
And use it like this
import chamaileonSdk from "@chamaileon-sdk/plugins";
Or like this
const chamaileonSdk = require("@chamaileon-sdk/plugins");
You can also import the Chamaileon SDK package from our CDN like this
<script type="module">
import chamaileonSdk from "https://cdn.chamaileon.io/sdk/##CURRENT_VERSION##/es/plugins.js";
</script>
You can find the newest version number here.
You can also use it in a script tag from our CDN like this
<script src="https://cdn.chamaileon.io/sdk/##CURRENT_VERSION##/umd/plugins.js"></script>
With this we add the library to the window
object so you can access it like this:
window.chamaileonSdk({...})
You can find the newest version number here.
There are some differences in the configuration compared to the old SDK. The biggest one is that you have to call the chamaileonSdk()
function directly instead of chamaileonSdk.init()
.
See the example below:
const accessTokenRequest = await fetch("https://sdk-api.chamaileon.io/api/v2/tokens/generate", {
method: "GET",
headers: {
"Authorization": `Bearer ${apiKey}`,
},
});
const accessTokenResponse = await accessTokenRequest.json();
const accessToken = accessTokenResponse.result
const chamaileonPlugins = await window.chamaileonSdk.init({
mode: "serverless",
accessToken: accessToken,
whitelabel: {
locale: "en",
urls: {
splashScreen: "https://chamaileon-sdk.github.io/splashscreen-and-logo-examples/splashScreen.html",
createLogoJS: "https://chamaileon-sdk.github.io/splashscreen-and-logo-examples/createLogo.js"
},
colors: {
primary: "#2D3291",
secondary: "#009f4a",
red: "#ff5546",
darkBlue: "#2d3291",
darkGreen: "#00af6e",
lightGreen: "#50d791",
weirdGreen: "#50d791",
pink: "#ff91a0",
yellow: "#ffd23c"
}
}
});
async function fetchAccessToken(){
const accessTokenRequest = await fetch("https://sdk-api.chamaileon.io/api/v2/tokens/generate", {
method: "GET",
headers: {
"Authorization": `Bearer ${apiKey}`,
},
});
if (!accessTokenRequest.ok) {
throw new Error("SDK token generation failed");
}
const accessTokenResponse = await accessTokenRequest.json();
return accessTokenResponse.result;
}
import createChamaileonPlugins as chamaileonSdk from "@chamaileon-sdk/plugins";
const accessToken = await fetchAccessToken();
const whitelabelConfig = {
locale: "en",
urls: {
splashScreen: "https://chamaileon-sdk.github.io/splashscreen-and-logo-examples/splashScreen.html",
createLogoJS: "https://chamaileon-sdk.github.io/splashscreen-and-logo-examples/createLogo.js"
},
colors: {
"primary": "#00C0E7",
"secondary": "#009f4a",
"red": "#ff5546",
"darkBlue": "#2d3291",
"darkGreen": "#00af6e",
"lightGreen": "#50d791",
"weirdGreen": "#50d791",
"pink": "#ff91a0",
"yellow": "#ffd23c"
},
};
const chamaileonPlugins = await chamaileonSdk({
...whitelabelConfig,
accessToken,
getAccessToken: fetchAccessToken,
});
serverless
mode.The plugin-wrapper
div now has a z-index
of 2001
instead of 10
.
The difference in the return object after a successful init is the following:
{
openGallery,
previewEmail,
editEmail,
createThumbnail,
editVariables,
}
{
createInlinePlugin,
createFullscreenPlugin,
destroy,
}
The createInlinePlugin
and createFullscreenPlugin
replaces the separated plugin load function. It also means that you have to create every plugin that you need with these and they won't be created automatically.
Until now, every plugin - except the thumbnail
- was initialized as fullscreen. From now on, you can chose between inline and fullscreen plugins. The only exception is the thumbnail
plugin because it can only be initialized as an inline plugin.
The destroy function was moved to the return value instead of being present as chamaileonSdk.destroy()
.
await chamaileonPlugins.previewEmail({
document: {},
settings: {},
hooks: {},
});
await chamaileonPlugins.createFullscreenPlugin({
plugin: "preview",
data: { document },
settings: {},
hooks: {},
});
await chamaileonPlugins.createThumbnail({
document: {},
container: "#email-thumbnail",
});
await chamaileonPlugins.createInlinePlugin(
{
plugin: "thumbnail",
data: { document },
settings: {
height: 640,
width: 480,
scale: 0.5
},
hooks: {},
},
{
container: document.getElementById("email-thumbnail"),
dimensions: {
width: 640,
height: 480,
scale: 0.5,
}
}
);
plugin
type in a property instead of a custom function call.container
prop got moved into the second object parameter in the call functioncontainer
now accepts a HTMLElement
as well as a selectorOld | New |
---|---|
openGallery |
plugin: "gallery" |
previewEmail |
plugin: "preview" |
editEmail |
plugin: "editor" |
createThumbnail |
plugin: "thumbnail" |
editVariables |
plugin: "variable-editor" |
We modified the SDK so that most of the time you will only need one instance of every plugin. After you create an instance we will give you the option to modify the data and settings parameter of every plugin on the fly.
A plugin instance returns with the following:
{
close,
updateSettings,
setDocument,
...otherMethods
}
{
methods: {
updateSettings,
updateData,
...otherMethods
},
showSplashScreen,
hideSplashScreen,
show,
hide,
destroy,
};
{
destroy,
};
{
methods: {
updateSettings,
updateData,
...otherMethods
},
destroy,
};
setDocument
was renamed to updateData
With the show and hide functions you can avoid the old workflow of completely destroying and then re-initializing the same plugin over and over again. From now you can hide the plugin, update it's data and settings and then show it again. If you want you can even show the plugin and display the splash screen while you update the data in the plugin.
You should load every plugin you need after the SDK initialization. The best case scenario is to load it before your user wants to use it. You can then update the data object and the settings later when the user actually wants to interact with the plugin.
chamaileonPlugins.previewEmail({
document,
settings: {
...previewSettings
},
hooks: {
...previewHooks
},
})
const previewInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "preview",
data: {},
settings: {},
hooks: {
...previewHooks
},
});
await previewInstance.methods.updateSettings({ ...previewSettings });
await previewInstance.methods.updateData({ document });
await chamaileonPlugins.previewEmail({
document: {},
settings: {},
hooks: {},
});
const previewInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "preview",
data: { document },
settings: {},
hooks: {},
});
previewInstance.showSplashScreen(); // optional
await previewInstance.open();
previewInstance.hideSplashScreen(); // optional
The old SDK handled the close action and destroyed the plugin instance.
const previewInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "preview",
data: { document },
settings: {},
hooks: {
close: () => {
return new Promise(resolve => {
previewInstance.hide();
resolve();
});
},
},
});
// The only exception for this is the variable editor plugin
// because you have to add your custom button for this and catch it
// on the `onButtonClicked` action
const variableEditorInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "variable-editor",
data: { document },
settings: {},
hooks: {
onButtonClicked: ({ buttonId }) => {
return new Promise(resolve => {
if (buttonId === "close") {
variableEditorInstance.hide();
resolve();
}
reject();
});
},
},
});
code
element config from settings.elements.content
to settings.element.advanced
user
top level property inside the settings
propertydocument
top level property inside the data
propertygetDocument
top level method inside editorInstance.methods
objectgetEmailHtml
top level method inside editorInstance.methods
objectsetDocument
method to updateData
and moved it inside the editorInstance.methods
objectgetEmailJson
method, use the editorInstance.methods.getDocument
method instead. The returned returnedObject.body
will be the same as the deprecated method's result. We suggest that you save the whole returnObject
from now on because it returns the fontFiles
and variables
as well.emailJson
top level property, use the data.document.body
property insteadtitle
top level property, use the data.document.title
property insteadautoSaveInterval
top level property, use the settings.autoSaveInterval
property insteadtextInsertPluginButtons
top level property, use the settings.buttons.textInsert
property insteadopenVariableModal
function that was triggerable with the onClick config option on buttons. We have a default place for this button now.onDropdownButtonClicked
hook, use the onHeaderButtonClicked
hook insteademailJson
parameter on onSave
hook, use the returned document.body
insteademailJson
parameter on onAutoSave
hook, use the returned document.body
insteadonBeforeClose
and onAfterClose
hooks, use the close
hook instead
close
hookseditorConfig.hooks.onBeforeClose = () => { /* custom logic before the editor is closed */ };
editorConfig.hooks.onAfterClose = () => { /* custom logic after the editor is closed */ };
close
hookeditorConfig.hooks.close = () => {
/* custom logic before the editor is "closed" */
await editorInstance.hide();
/* custom logic after the editor is "closed" */
};
canLockBlocks
top level property, use the settings.addons.blockLock
property instead
// Disabled
settings.addons.blockLock = {
enabled: false,
}
// Hidden
settings.addons.blockLock = false;
blockLibraries
top level property, use the settings.blockLibraries
property instead
accessLevel
property and instead you can define the canDeleteBlock
, canRenameBlock
and canSaveBlock
properties.blockLibraries
config arrayblockLibraries: [
{
id: "email-blocks",
label: "Email's blocks",
accessLevel: "readOnly",
},
]
blockLibraries
config arraysettings.blockLibraries: [
{
id: "email-blocks",
label: "Email's blocks",
canDeleteBlock: false,
canRenameBlock: false,
canSaveBlock: false,
},
]
dropdownButtons
top level property, use the settings.buttons.header
property instead
dropdownButtons
content to it's itemsdropdownButtons
config arraydropdownButtons: [
{
id: "test-button-1",
label: "Show preview",
icon: "short_text",
},
]
dropdownButtons
config arraysettings.buttons.header = [
{
id: "dropdownButtons",
type: "dropdown",
icon: "dots-vertical",
style: "icon",
items: [
{
id: "test-button-1",
label: "Show preview",
icon: "short_text",
}
],
},
];
settings.buttons.header
parameter
settings.button.header = [
{
id: "preview",
type: "button",
icon: "eye",
label: "Preview",
color: "primary",
style: "outlined",
},
{
id: "dropdownButtons",
type: "dropdown",
icon: "dots-vertical",
style: "icon",
items: [],
},
]
hooks.onHeaderButtonClicked
hookconst previewInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "preview",
data: {},
settings: {},
hooks: {},
});
const editorInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "editor",
data: { document },
settings: {},
hooks: {
onHeaderButtonClicked: async ({ buttonId }) => {
if (buttonId === "preview") {
const currentJSON = await editorInstance.methods.getDocument();
await previewInstance.methods.updateData({ document: currentJSON });
previewInstance.show();
}
};
},
});
document
top level property inside the data
propertyhideHeader
top level property, use the settings.hideHeader
property insteadtitle
top level property, use the data.document.title
property insteadcontainer
property from the first object property into the second. The property now accepts a HTMLElement
as well as a selector.
await chamaileonPlugins.createThumbnail({
document: {},
container: "#email-thumbnail",
height: 640,
width: 480,
scale: 0.5,
scroll: true,
});
await chamaileonPlugins.createInlinePlugin(
{
plugin: "thumbnail",
data: { document },
settings: {
scroll: true,
},
hooks: {},
},
{
container: document.getElementById("email-thumbnail"),
dimensions: {
width: 640,
height: 480,
scale: 0.5,
}
}
);
DOMHeight
hook to sendDOMHeight
getDocumentHeight
hook into getDocumentHeight
methodheight
top level property inside the settings
propertywidth
top level property inside the settings
propertyscale
top level property inside the settings
propertyscroll
top level property inside the settings
propertydocument
top level property inside the data
propertyeditImgSrc
top level property inside the data
as currentImgSrc
propertydimensions
top level property inside the data
propertysettings.photoEditorLicece
to settings.photoEditorLicense
Promise
that resolves with the selected image. Instead you have to call the pickImage
method on the initialized instance instead.
const { src } = await chamaileonPlugins.openGallery({ editImgSrc, dimensions });
const galleryInstance = await chamaileonPlugins.createFullscreenPlugin({
plugin: "gallery",
data: {
currentImgSrc,
dimensions,
},
settings: {},
hooks: {},
});
const { src } = await galleryInstance.methods.pickImage();
setDocument
to updateData
settings.buttons.textInsertPlugin
to settings.buttons.textInsert`document
top level property inside the data
propertyoutlined
style and black colorvariableEditorConfig.settings.buttons.header = {
left: [
{
id: "close",
icon: "arrow-left"
},
],
right: [
{
id: "prev",
label: "Prev",
},
],
};
variableEditorConfig.settings.buttons.header = {
left: [
{
id: "close",
icon: "arrow-left",
color: "#000",
},
],
right: [
{
id: "prev",
label: "Prev",
style: "outlined,
color: "#000",
},
],
};
You can visit our Chamaileon SDK Playground to see the new interface in action.