-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Customize the Froala default toolbar #2078
Comments
Above has always been possible |
The default rich editor buttons can now be customized by modifying the |
Great ! We can use it like in the code below but I think there can be a race problem that depends on when the custom extension JS is laoded. In this order :
We must hook the In this order :
We must directly extend Froala and add the button in the main JS flow as the Maybe throwing an event in This illustrate a case when the custom extension is loaded before the hook in Plugin boot
froala.plugins.js
|
I feel like this could be better. The logic is really coupled to the RichEditor widget class, not really the Form widget class. We should add behaviors to the WidgetBase for extensibility anyway (see #1586).
This would resolve the race condition and make everything hunky-dory again. |
@gabsource Did you have any joy extending the toolbar as per your example above? I'm trying but not getting very far... Any insight would be gratefully appreciated ;) |
Gotta agree with @seanthepottingshed. It has been a try-and-error pain for me and I haven't gotten it to work so far. Granted, I'm fairly new to OctoberCMS and how its plugin extension mechanisms work but I've really sat here for a whole day but it just didn't work. @daftspunk |
Here is an example for a SoundCloud embed I wrote:
/*
|-------------------------
| Froala SoundCloud Plugin
|-------------------------
*/
+function ($) {
/*
|--------------------------
| Initialise SoundCloud SDK
|--------------------------
*/
SC.initialize({
client_id : '{{ clientID }}'
});
/*
|----------------------------------------------------
| Define SoundCloud Icon, Register Command and Button
|----------------------------------------------------
*/
$.FroalaEditor.DefineIcon('soundCloudIcon', {
NAME : 'soundcloud'
});
$.FroalaEditor.RegisterCommand('insertSoundCloudTrack', {
title : 'Insert SoundCloud Track',
icon : 'soundCloudIcon',
showOnMobile : true,
refreshAfterCallback : false,
undo : false,
focus : false,
callback : function() {
this.soundCloudPlugin.showPopup();
}
});
/*
|----------------------------------------
| Register Insert SoundCloud HTML Command
|----------------------------------------
*/
$.FroalaEditor.RegisterCommand('insertSoundCloudHTML', {
undo : true,
refreshAfterCallback : false,
callback : function() {
var editor = this,
$trackURLField = $('#soundcloud-popup input'),
$header = $('#soundcloud-popup .fr-buttons'),
trackURL = $trackURLField.val();
$header.addClass('testing');
$header.removeClass('invalid');
SC.get('/resolve/?url=' + encodeURIComponent(trackURL), {
limit : 1
}, function(data) {
if(data.kind && data.kind === 'track') {
var trackTitle = data.user.username + ' | ' + data.title,
vimeoVideoHTML = '<figure class="tps" data-ui-block="false" contenteditable="false" tabindex="0">' + "\n" +
'<blockquote class="placeholder soundcloud-track">' + "\n" +
'<a href="' + trackURL + '" target="_blank">' + "\n" +
trackTitle +
'</a>' + "\n" +
'</blockquote>' + "\n" +
'<iframe width="100%" height="450" scrolling="no" frameborder="no" data-src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/' +
data.id +
'&auto_play=false&hide_related=true&show_comments=true&show_user=false&show_reposts=false&visual=false;&color=' +
accentColor.replace('#', '') +
'">' + "\n" +
'</iframe>' + "\n" +
'</figure>' + "\n";
editor.html.insert(vimeoVideoHTML);
editor.undo.saveStep();
editor.soundCloudPlugin.hidePopup();
$trackURLField.val('');
$header.removeClass('testing');
} else {
$header.removeClass('testing');
$header.addClass('invalid');
}
});
}
});
$.oc.richEditorButtons.push('insertSoundCloud');
/*
|-------------------------------
| Define SoundCloud Plugin Popup
|-------------------------------
*/
$.extend($.FroalaEditor.POPUP_TEMPLATES, {
'soundCloudPlugin.popup': '[_CUSTOM_LAYER_]'
});
$.FroalaEditor.PLUGINS.soundCloudPlugin = function (editor) {
function initPopup() {
var customLayer = '<div id="soundcloud-popup" class="tps-popup">' + "\n" +
'<div class="fr-buttons">' + "\n" +
'<label for="">SoundCloud</label>' + "\n" +
'<div class="loading-indicator">' + "\n" +
'<span></span>' + "\n" +
'</div>' + "\n" +
'<p class="error-message">Invalid URL</p>' + "\n" +
'</div>' + "\n" +
'<div class="form">' + "\n" +
'<input placeholder="Paste Embed URL">' + "\n" +
'<div class="fr-action-buttons">' + "\n" +
'<button type="button" class="fr-command fr-submit" data-cmd="insertSoundCloudHTML" tabindex="2" role="button">Insert SoundCloud</button>' + "\n" +
'</div>' + "\n" +
'</div>' + "\n" +
'</div>',
template = {
custom_layer : customLayer
},
$popup = editor.popups.create('soundCloudPlugin.popup', template);
return $popup;
}
/**
* Show Popup.
*/
function showPopup() {
var $popup = editor.popups.get('soundCloudPlugin.popup');
if (!$popup) {
$popup = initPopup();
}
editor.popups.setContainer('soundCloudPlugin.popup', editor.$tb);
var $btn = editor.$tb.find('.fr-command[data-cmd="insertSoundCloudTrack"]'),
left = $btn.offset().left + $btn.outerWidth() / 2,
top = $btn.offset().top + (editor.opts.toolbarBottom ? 10 : $btn.outerHeight() - 10);
editor.popups.show('soundCloudPlugin.popup', left, top, $btn.outerHeight());
}
/**
* Hide Popup.
*/
function hidePopup () {
editor.popups.hide('soundCloudPlugin.popup');
}
return {
showPopup : showPopup,
hidePopup : hidePopup
};
};
}(jQuery);
/*
|-------------------------
| Froala SoundCloud Plugin
|-------------------------
*/
.fr-popup {
.tps-popup {
.fr-buttons {
position: relative;
height: 35px;
font-size: 14px;
font-weight: 100;
font-family: 'Roboto';
line-height: 34px;
label {
display: block;
float: left;
margin: 0;
padding: 0 0 0 10px;
}
.loading-indicator {
position: absolute;
right: 10px;
padding: 0;
width: 34px;
height: 34px;
text-align: right;
background: none;
span {
opacity: 0;
background-size: 20px 20px;
transition: opacity 0.125s linear;
}
}
.error-message {
float: right;
margin: 0;
padding: 0 10px 0 0;
color: @accentColor;
font-weight: 700;
opacity: 0;
transition: opacity 0.125s linear;
}
&.testing {
.loading-indicator {
span {
opacity: 1;
}
}
.error-message {
opacity: 0;
}
}
&.invalid {
.error-message {
opacity: 1;
}
}
}
.form {
padding: 10px;
input {
margin: 0;
padding: 8px 13px 9px;
border: 1px solid #d1d6d9;
height: 38px;
line-height: 1.42857143;
font-size: 14px;
font-family: 'Roboto';
border-radius: 3px;
box-shadow: inset 0 1px 0 rgba(209,214,217,0.25), 0 1px 0 rgba(255,255,255,.5);
}
.fr-action-buttons {
button {
&.fr-command {
width: 100%;
font-family: 'Roboto';
text-shadow: none;
text-align: center;
background-color: @primaryColor;
&:hover {
background-color: @secondaryColor;
transition: background-color 0.25s linear;
}
}
}
}
}
}
}
.control-richeditor {
figure[data-ui-block] {
&.tps {
position: relative;
margin: 15px 0;
padding: 0;
border: 2px solid #FFFFFF;
&:focus {
border: 2px solid @primaryColor;
}
.video-player {
position: relative;
display: block;
width: 100%;
height: 0;
padding: 0 0 56.25% 0;
iframe {
position: absolute;
z-index: 1;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #000;
}
}
/*
|-------------------
| Editor Placeholder
|-------------------
*/
.placeholder {
margin: 0;
padding: 0 15px;
width: 100%;
background: #f2f2f2;
border: none;
border-radius: 3px;
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 1px 1px rgba(0,0,0,0.16);
a {
position: relative;
display: block;
margin: 0 0 0 40px;
line-height: 50px;
color: rgba(64,82,97,0.8) !important;
font-size: 18px;
font-weight: 700;
font-family: 'Roboto';
text-decoration: none;
&:hover {
color: @accentColor !important;
transition: color 0.25s linear;
}
&:before {
position: absolute;
top: 0;
left: -40px;
font-family: 'FontAwesome';
font-size: 30px;
font-weight: 100;
}
}
&.soundcloud-track {
a {
&:before {
content: "\f1be";
color: #ff7700;
}
}
+ iframe {
position: absolute;
height: 0;
}
}
}
}
}
} |
Added support for changing the global default for richeditor buttons. Addresses: #2677, #2384, #2078, #1743 and rainlab/pages-plugin#188
This approach doesn't seem to work anymore as of build 455. Is there a different way that should be used to would work to add buttons to the Rich Editor from a plugin? |
@mrelevance what doesn't work about it? Are you getting a specific error message? |
@mrelevance have you checked the dev console? Are there any errors being displayed? |
@LukeTowers no errors are reported in the console. |
@LukeTowers bumping as I haven't gotten any response to the last message, any ideas? |
@mrelevance this was fixed in 30f4d4c |
For anyone stumbling across this recently, this has been addressed as an API in v3.4. The documentation now includes these instructions: Registering a Custom ButtonThe following JavaScript code can be used to register a custom button as a command. oc.richEditorRegisterButton('insertCustomThing', {
title: 'Insert Something',
icon: '<i class="icon-star"></i>',
undo: true,
focus: true,
refreshOnCallback: true,
callback: function () {
this.html.insert('<strong>My Custom Thing!</strong>');
}
}); Then add the button to the default collection. oc.richEditorButtons.splice(0, 0, 'insertCustomThing'); |
Expected behavior
With the previous editor we could somehow extend the JS code inside a plugin to add buttons to the rich editor toolbar.
Actual behavior
We should be abale to customize the Froala editor toolbar with custom buttons (not only the one available in the bundled Froala version) :
toolbarButtons
of the Yaml)Buttons defined like in https://www.froala.com/wysiwyg-editor/docs/concepts/custom-button must be registered before the editor is instantiated but it seems we can not do this at the moment.
Reproduce steps
Not relevant
October build
341
The text was updated successfully, but these errors were encountered: