Closed brentonkelly1982 closed 5 years ago
I have finally figured this out on my own and want to share the result for anyone else that is struggling with this the way I did. This code snipped creates a custom, responsive video component that supports YouTube, Vimeo, MyVRSpot, and a custom iframe src. I was able to get the model to update in the canvas when a component setting (trait) is changed. I hope someone else finds this helpful.
(function() {
var PageBuilder = {
// ...
"BuildGrapesEditor": function() {
// INITIALLY LOAD THE SAVED APP CONTENT INTO THE EDITOR
var savedComponents = JSON.parse($("#grapes-components").val());
// DEFINE CUSTOM COMPONENTS *BEFORE* GRAPES INITIALIZATION (VERY IMPORTANT) - https://grapesjs.com/docs/modules/Components.html#define-custom-component-type
// ALSO ADD PLUGIN NAMES IN THIS METHODS RETURNED ARRAY
var plugins = this.GatherCustomComponents();
// INITIALIZE THE GRAPES EDITOR
this.GrapesEditor = grapesjs.init({
container: "#cs-grapes-editor",
height: "700px",
width: "auto",
showOffsets: true,
allowScripts: true,
components: savedComponents,
style: $("#grapes-css").val(),
storageManager: {
type: null,
autosave: false,
},
avoidInlineStyle: true,
plugins: plugins
});
this.GrapesCommands = this.GrapesEditor.Commands;
this.GrapesPanels = this.GrapesEditor.Panels;
this.GrapesBlocks = this.GrapesEditor.BlockManager;
},
"GatherCustomComponents": function() {
var _this = this;
// CUSTOM VIDEO COMPONENT
var customVideo = function(editor) {
editor.DomComponents.addType("csVideo", {
// FRAMEWORK METHOD - https://grapesjs.com/docs/modules/Components.html#model
"model": {
defaults: {
// FRAMEWORK DEFAULTS
tagName: "div",
draggable: true,
droppable: false,
classes: ["cs-gjs-video"],
content: '<iframe class="gjs-no-pointer" src="https://www.youtube.com/embed/mxD7QEt85ms" frameborder="0" allow="autoplay; fullscreen" scrolling="no"></iframe>' +
'<style>' +
'.cs-gjs-video {' +
'position: relative;' +
'margin: 5px 0px;' +
'}' +
'.cs-gjs-video:before {' +
'content: "";' +
'display: block;' +
'padding-top: 56.25%;' +
'}' +
'.cs-gjs-video iframe {' +
'width: 100%;' +
'height: 100%;' +
'position: absolute;' +
'top: 0px;' +
'left: 0px;' +
'}' +
'</style>',
editable: true,
// CUSTOM DEFAULTS
provider: "youtube",
videoId: "mxD7QEt85ms",
videoWidth: 560,
videoHeight: 315,
youtubeUrl: "https://www.youtube.com/embed/",
youtubeDefaultId: "mxD7QEt85ms",
vimeoUrl: "https://player.vimeo.com/video/",
vimeoDefaultId: "22439234",
myvrspotUrl: "https://live.myvrspot.com/iframe",
myvrspotDefaultId: "vff46db9afa6d",
iframeDefaultId: 'https://www.youtube.com/embed/mxD7QEt85ms',
loop: 0,
autoplay: 0,
controls: 1,
color: "",
rel: 1,
modestbranding: 0,
title: 1,
portrait: 1,
byline: 1
},
// FRAMEWORK METHOD
"init": function(properties, options) {
// SET TRAITS
var traits = this.getTraits();
this.set("traits", traits);
// MAKE COMPONENTS PANEL ACTIVE
if (_this.GrapesEditor.Panels !== undefined) {
_this.GrapesEditor.Panels.getButton("views", "open-tm").set("active", true);
}
// EVENT LISTENER FOR TRAIT CHANGES
this.listenTo(this, "change:provider", this.updateTraits);
this.listenTo(this, "change:videoId change:provider change:videoWidth change:videoHeight change:autoplay change:loop change:controls change:rel change:modestbranding change:color change:portrait change:title change:byline", this.updateModel);
},
// CUSTOM METHOD TO UPDATE THE COMPONENT MODEL - https://grapesjs.com/docs/modules/Components.html#define-custom-component-type
"updateModel": function() {
this.updateSrc();
var attributes = this.attributes;
var modelComponent = _this.GrapesEditor.getSelected();
modelComponent.addAttributes({
"data-cs-gjs-id": modelComponent.ccid
});
modelComponent.set("content", '' +
'<iframe class="gjs-no-pointer" src="' + attributes.src + '" frameborder="0" allow="autoplay; fullscreen" scrolling="no"></iframe>' +
'<style>' +
'[data-cs-gjs-id="' + modelComponent.ccid + '"] {' +
'position: relative;' +
'margin: 5px 0px;' +
'}' +
'[data-cs-gjs-id="' + modelComponent.ccid + '"]:before {' +
'content: "";' +
'display: block;' +
'padding-top: ' + ((attributes.videoHeight / attributes.videoWidth) * 100) + '%;' +
'}' +
'[data-cs-gjs-id="' + modelComponent.ccid + '"] iframe {' +
'width: 100%;' +
'height: 100%;' +
'position: absolute;' +
'top: 0px;' +
'left: 0px;' +
'}' +
'</style>' +
'');
},
// CUSTOM METHOD TO RETURN PROVIDER DROPDOWN TRAIT
"getProviderTrait": function() {
return {
type: "select",
label: "Provider",
name: "provider",
changeProp: 1,
options: [{
value: "youtube",
name: "YouTube"
},
{
value: "vimeo",
name: "Vimeo"
},
{
value: "myvrspot",
name: "MyVRSpot"
},
{
value: "iframe",
name: "Custom iframe"
}
],
};
},
// CUSTOM METHOD TO RETURN YOUTUBE TRAITS
"getYouTubeTraits": function() {
return [
this.getProviderTrait(),
{
type: "text",
label: "Video ID",
name: "videoId",
placeholder: "eg. mxD7QEt85ms",
changeProp: 1
},
{
type: "text",
label: "Video Width",
name: "videoWidth",
placeholder: "eg. 560",
changeProp: 1
},
{
type: "text",
label: "Video Height",
name: "videoHeight",
placeholder: "eg. 315",
changeProp: 1
},
{
type: "checkbox",
label: "Autoplay",
name: "autoplay",
changeProp: 1
},
{
type: "checkbox",
label: "Loop",
name: "loop",
changeProp: 1
},
{
type: "checkbox",
label: "Controls",
name: "controls",
changeProp: 1
},
{
type: "checkbox",
label: "Related",
name: "rel",
changeProp: 1
},
{
type: "checkbox",
label: "Modest Branding",
name: "modestbranding",
changeProp: 1
}
];
},
// CUSTOM METHOD TO RETURN VIMEO TRAITS
"getVimeoTraits": function() {
return [
this.getProviderTrait(),
{
type: "text",
label: "Video ID",
name: "videoId",
placeholder: "eg. 22439234",
changeProp: 1
},
{
type: "text",
label: "Video Width",
name: "videoWidth",
placeholder: "eg. 560",
changeProp: 1
},
{
type: "text",
label: "Video Height",
name: "videoHeight",
placeholder: "eg. 315",
changeProp: 1
},
{
type: "text",
label: "Color",
name: "color",
placeholder: "eg. #00adef",
changeProp: 1
},
{
type: "checkbox",
label: "Autoplay",
name: "autoplay",
changeProp: 1
},
{
type: "checkbox",
label: "Loop",
name: "loop",
changeProp: 1
},
{
type: "checkbox",
label: "Portrait",
name: "portrait",
changeProp: 1
},
{
type: "checkbox",
label: "Title",
name: "title",
changeProp: 1
},
{
type: "checkbox",
label: "Byline",
name: "byline",
changeProp: 1
}
];
},
// CUSTOM METHOD TO RETURN MYVRSPOT TRAITS
"getMyVRSpotTraits": function() {
return [
this.getProviderTrait(),
{
type: "text",
label: "Video ID",
name: "videoId",
placeholder: "eg. vff46db9afa6d",
changeProp: 1
},
{
type: "text",
label: "Video Width",
name: "videoWidth",
placeholder: "eg. 560",
changeProp: 1
},
{
type: "text",
label: "Video Height",
name: "videoHeight",
placeholder: "eg. 315",
changeProp: 1
}
];
},
// CUSTOM METHOD TO RETURN CUSTOM IFRAME TRAITS
"getIframeTraits": function() {
return [
this.getProviderTrait(),
{
type: "text",
label: "iframe Src Url",
name: "videoId",
placeholder: 'eg. https://player.vimeo.com/video/22439234?autoplay=1&loop=1&title=0&byline=0&portrait=0',
changeProp: 1
},
{
type: "text",
label: "Video Width",
name: "videoWidth",
placeholder: "eg. 560",
changeProp: 1
},
{
type: "text",
label: "Video Height",
name: "videoHeight",
placeholder: "eg. 315",
changeProp: 1
}
];
},
// CUSTOM HELPER METHOD TO GET THE TRAITS FOR THE SELECTED PROVIDER
"getTraits": function() {
var provider = this.get("provider");
var traits = "";
switch (provider) {
case "youtube":
traits = this.getYouTubeTraits();
break;
case "vimeo":
traits = this.getVimeoTraits();
break;
case "myvrspot":
traits = this.getMyVRSpotTraits();
break;
case "iframe":
traits = this.getIframeTraits();
break;
default:
traits = this.getYouTubeTraits();
break;
}
return traits;
},
// CUSTOM METHOD TO UPDATE TRAITS ON PROVIDER DROPDOWN CHANGE
"updateTraits": function() {
this.loadTraits(this.getTraits());
var provider = this.get("provider");
switch (provider) {
case "youtube":
traits = this.getYouTubeTraits();
break;
case "vimeo":
traits = this.getVimeoTraits();
break;
case "myvrspot":
traits = this.getMyVRSpotTraits();
break;
case "iframe":
traits = this.getIframeTraits();
break;
default:
traits = this.getYouTubeTraits();
break;
}
return traits;
},
// CUSTOM HELPER METHOD TO UPDATE THE IFRAME SRC WHEN TRAITS CHANGE
"updateSrc": function() {
var provider = this.get("provider");
switch (provider) {
case "youtube":
this.set("src", this.getYouTubeSrc());
break;
case "vimeo":
this.set("src", this.getVimeoSrc());
break;
case "myvrspot":
this.set("src", this.getMyVRSpotSrc());
break;
case "iframe":
this.set("src", this.getIframeSrc());
break;
}
},
// CUSTOM METHOD TO BUILD YOUTUBE IFRAME SRC
"getYouTubeSrc": function() {
var videoId = this.get("videoId");
var url = this.get("youtubeUrl");
url += videoId + "?";
url += this.get("autoplay") ? "&autoplay=1" : "";
url += this.get("controls") ? "" : "&controls=0&showinfo=0";
url += this.get("loop") ? "&loop=1&playlist=" + videoId : "";
url += this.get("rel") ? "" : "&rel=0";
url += this.get("modestbranding") ? "&modestbranding=1" : "";
return url;
},
// CUSTOM METHOD TO BUILD VIMEO IFRAME SRC
"getVimeoSrc": function() {
var videoId = this.get("videoId");
var url = this.get("vimeoUrl");
url += videoId + "?";
url += this.get("autoplay") ? "&autoplay=1" : "";
url += this.get("loop") ? "&loop=1" : "";
url += this.get("portrait") ? "&portrait=0" : "";
url += this.get("title") ? "&title=0" : "";
url += this.get("byline") ? "" : "&byline=0";
url += this.get("color") ? "&color=" + this.get("color") : "";
return url;
},
// CUSTOM METHOD TO BUILD MYVRSPOT IFRAME SRC
"getMyVRSpotSrc": function() {
var videoId = this.get("videoId");
var url = this.get("myvrspotUrl");
url += "?v=" + videoId;
return url;
},
// CUSTOM METHOD TO BUILD CUSTOM IFRAME SRC
"getIframeSrc": function() {
var videoId = this.get("videoId");
return videoId;
}
}
});
}
// RETURN AN ARRAY OF DEFINED COMPONENTS/PLUGINS
return [customVideo];
},
// ...
"ExtendGrapesBlocks": function() {
var _this = this;
// VIDEO BLOCK
this.GrapesBlocks.add("csVideo", {
label: 'Video',
content: {
type: "csVideo",
name: "Video",
attributes: {
"data-element-type": "csVideo"
}
},
category: 'Multimedia',
attributes: {
"class": "fa fa-youtube-play"
},
select: true
});
},
// ...
};
})();
Hi! I have read and re-read the GrapesJS Docs and API Reference on how to create a custom component and I just can't seem to figure it out. I initially started trying to extend the existing video component but then decided to make my own custom video component. I'm using the existing video component as a guide. My component can be dragged onto the canvas and the component settings (traits) can be toggled but nothing updates in the canvas. My component tagName is a div because I need a wrapper to make the inner iframe responsive via CSS. That in itself could be an issue in how I'm adding the iframe but I can't get far enough with this to tell for sure.
I really appreciate any insight that you can provide!
Code snippet update and moved to closing comment