Added link selector interface to WYSIWYG editor

This commit is contained in:
Dan Brown 2016-09-01 20:36:22 +01:00
parent 56df64063d
commit 5b64358ef1
No known key found for this signature in database
GPG Key ID: 46D9F943C24A2EF9
9 changed files with 163 additions and 39 deletions

View File

@ -600,6 +600,58 @@ module.exports = function (ngApp, events) {
} }
}]); }]);
ngApp.directive('entityLinkSelector', [function($http) {
return {
restict: 'A',
link: function(scope, element, attrs) {
const selectButton = element.find('.entity-link-selector-confirm');
let callback = false;
let entitySelection = null;
// Handle entity selection change, Stores the selected entity locally
function entitySelectionChange(entity) {
entitySelection = entity;
if (entity === null) {
selectButton.attr('disabled', 'true');
} else {
selectButton.removeAttr('disabled');
}
}
events.listen('entity-select-change', entitySelectionChange);
// Handle selection confirm button click
selectButton.click(event => {
hide();
if (entitySelection !== null) callback(entitySelection);
});
// Show selector interface
function show() {
element.fadeIn(240);
}
// Hide selector interface
function hide() {
element.fadeOut(240);
}
// Listen to confirmation of entity selections (doubleclick)
events.listen('entity-select-confirm', entity => {
hide();
callback(entity);
});
// Show entity selector, Accessible globally, and store the callback
window.showEntityLinkSelector = function(passedCallback) {
show();
callback = passedCallback;
};
}
};
}]);
ngApp.directive('entitySelector', ['$http', '$sce', function ($http, $sce) { ngApp.directive('entitySelector', ['$http', '$sce', function ($http, $sce) {
return { return {
@ -613,26 +665,60 @@ module.exports = function (ngApp, events) {
// Add input for forms // Add input for forms
const input = element.find('[entity-selector-input]').first(); const input = element.find('[entity-selector-input]').first();
// Detect double click events
var lastClick = 0;
function isDoubleClick() {
let now = Date.now();
let answer = now - lastClick < 300;
lastClick = now;
return answer;
}
// Listen to entity item clicks // Listen to entity item clicks
element.on('click', '.entity-list a', function(event) { element.on('click', '.entity-list a', function(event) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
let item = $(this).closest('[data-entity-type]'); let item = $(this).closest('[data-entity-type]');
itemSelect(item); itemSelect(item, isDoubleClick());
}); });
element.on('click', '[data-entity-type]', function(event) { element.on('click', '[data-entity-type]', function(event) {
itemSelect($(this)); itemSelect($(this), isDoubleClick());
}); });
// Select entity action // Select entity action
function itemSelect(item) { function itemSelect(item, doubleClick) {
let entityType = item.attr('data-entity-type'); let entityType = item.attr('data-entity-type');
let entityId = item.attr('data-entity-id'); let entityId = item.attr('data-entity-id');
let isSelected = !item.hasClass('selected'); let isSelected = !item.hasClass('selected') || doubleClick;
element.find('.selected').removeClass('selected').removeClass('primary-background'); element.find('.selected').removeClass('selected').removeClass('primary-background');
if (isSelected) item.addClass('selected').addClass('primary-background'); if (isSelected) item.addClass('selected').addClass('primary-background');
let newVal = isSelected ? `${entityType}:${entityId}` : ''; let newVal = isSelected ? `${entityType}:${entityId}` : '';
input.val(newVal); input.val(newVal);
if (!isSelected) {
events.emit('entity-select-change', null);
}
if (!doubleClick && !isSelected) return;
let link = item.find('.entity-list-item-link').attr('href');
let name = item.find('.entity-list-item-name').text();
if (doubleClick) {
events.emit('entity-select-confirm', {
id: Number(entityId),
name: name,
link: link
});
}
if (isSelected) {
events.emit('entity-select-change', {
id: Number(entityId),
name: name,
link: link
});
}
} }
// Get search url with correct types // Get search url with correct types

View File

@ -135,6 +135,11 @@ $(function () {
$(this).closest('.overlay').fadeOut(240); $(this).closest('.overlay').fadeOut(240);
}); });
$('.overlay').click(function(event) {
if (!$(event.target).hasClass('overlay')) return;
$(this).fadeOut(240);
});
}); });
// Page specific items // Page specific items

View File

@ -96,6 +96,15 @@ var mceOptions = module.exports = {
}, },
file_browser_callback: function (field_name, url, type, win) { file_browser_callback: function (field_name, url, type, win) {
if (type === 'file') {
window.showEntityLinkSelector(function(entity) {
var originalField = win.document.getElementById(field_name);
originalField.value = entity.link;
$(originalField).closest('.mce-form').find('input').eq(2).val(entity.name);
});
}
if (type === 'image') {
// Show image manager // Show image manager
window.ImageManager.showExternal(function (image) { window.ImageManager.showExternal(function (image) {
@ -116,6 +125,8 @@ var mceOptions = module.exports = {
html += '</a>'; html += '</a>';
win.tinyMCE.activeEditor.execCommand('mceInsertContent', false, html); win.tinyMCE.activeEditor.execCommand('mceInsertContent', false, html);
}); });
}
}, },
paste_preprocess: function (plugin, args) { paste_preprocess: function (plugin, args) {
var content = args.content; var content = args.content;

View File

@ -100,3 +100,13 @@ $button-border-radius: 2px;
} }
} }
.button[disabled] {
background-color: #BBB;
cursor: default;
&:hover {
background-color: #BBB;
cursor: default;
box-shadow: none;
}
}

View File

@ -39,11 +39,7 @@
} }
} }
.popup-header { .corner-button {
display: block;
position: relative;
height: 40px;
.popup-close {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
@ -51,13 +47,20 @@
height: 40px; height: 40px;
border-radius: 0; border-radius: 0;
box-shadow: none; box-shadow: none;
} }
.popup-header, .popup-footer {
display: block;
position: relative;
height: 40px;
.popup-title { .popup-title {
color: #FFF; color: #FFF;
padding: 8px $-m; padding: 8px $-m;
} }
} }
#entity-selector-wrap .popup-body .form-group {
margin: 0;
}
.image-manager-body { .image-manager-body {
min-height: 60vh; min-height: 60vh;
} }

View File

@ -1,5 +1,5 @@
<div class="book entity-list-item" data-entity-type="book" data-entity-id="{{$book->id}}"> <div class="book entity-list-item" data-entity-type="book" data-entity-id="{{$book->id}}">
<h3 class="text-book"><a class="text-book" href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i>{{$book->name}}</a></h3> <h3 class="text-book"><a class="text-book entity-list-item-link" href="{{$book->getUrl()}}"><i class="zmdi zmdi-book"></i><span class="entity-list-item-name">{{$book->name}}</span></a></h3>
@if(isset($book->searchSnippet)) @if(isset($book->searchSnippet))
<p class="text-muted">{!! $book->searchSnippet !!}</p> <p class="text-muted">{!! $book->searchSnippet !!}</p>
@else @else

View File

@ -6,8 +6,8 @@
</a> </a>
<span class="text-muted">&nbsp;&nbsp;&raquo;&nbsp;&nbsp;</span> <span class="text-muted">&nbsp;&nbsp;&raquo;&nbsp;&nbsp;</span>
@endif @endif
<a href="{{ $chapter->getUrl() }}" class="text-chapter"> <a href="{{ $chapter->getUrl() }}" class="text-chapter entity-list-item-link">
<i class="zmdi zmdi-collection-bookmark"></i>{{ $chapter->name }} <i class="zmdi zmdi-collection-bookmark"></i><span class="entity-list-item-name">{{ $chapter->name }}</span>
</a> </a>
</h3> </h3>
@if(isset($chapter->searchSnippet)) @if(isset($chapter->searchSnippet))

View File

@ -22,15 +22,24 @@
@include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id]) @include('partials/image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
<div id="entity-selector-wrap"> <div id="entity-selector-wrap">
<div class="overlay"> <div class="overlay" entity-link-selector>
<div class="popup-body small flex-child"> <div class="popup-body small flex-child">
<div class="popup-header primary-background"> <div class="popup-header primary-background">
<div class="popup-title">Entity Select</div> <div class="popup-title">Entity Select</div>
<button class="popup-close neg button">x</button> <button type="button" class="corner-button neg button">x</button>
</div> </div>
@include('partials/entity-selector', ['name' => 'entity-selector']) @include('partials/entity-selector', ['name' => 'entity-selector'])
<div class="popup-footer">
<button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">Select</button>
</div>
</div> </div>
</div> </div>
</div> </div>
<script>
(function() {
})();
</script>
@stop @stop

View File

@ -1,6 +1,6 @@
<div class="page {{$page->draft ? 'draft' : ''}} entity-list-item" data-entity-type="page" data-entity-id="{{$page->id}}"> <div class="page {{$page->draft ? 'draft' : ''}} entity-list-item" data-entity-type="page" data-entity-id="{{$page->id}}">
<h3> <h3>
<a href="{{ $page->getUrl() }}" class="text-page"><i class="zmdi zmdi-file-text"></i>{{ $page->name }}</a> <a href="{{ $page->getUrl() }}" class="text-page entity-list-item-link"><i class="zmdi zmdi-file-text"></i><span class="entity-list-item-name">{{ $page->name }}</span></a>
</h3> </h3>
@if(isset($page->searchSnippet)) @if(isset($page->searchSnippet))