mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API Move state to enwtine properties, provide API for preview.
Also the preview state is now kept between panel loads. We also use a redraw function to update appearance based on the state.
This commit is contained in:
parent
9312c70696
commit
8f5acd70b3
@ -1044,7 +1044,7 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
$fields->push(new HiddenField('ParentID'));
|
||||
}
|
||||
|
||||
// Added in-line to the form, but plucked into different view by LeftAndMain.Preview.js upon load
|
||||
// Added in-line to the form, but plucked into different view by frontend scripts.
|
||||
if(in_array('CMSPreviewable', class_implements($record))) {
|
||||
$navField = new LiteralField('SilverStripeNavigator', $this->getSilverStripeNavigator());
|
||||
$navField->setAllowHTML(true);
|
||||
|
@ -94,14 +94,6 @@
|
||||
$('.cms-container').clearCurrentTabState(); // clear state to avoid override later on
|
||||
firstTabWithErrors.closest('.tabset').tabs('select', firstTabWithErrors.attr('id'));
|
||||
}
|
||||
|
||||
// Move navigator to preview if one is available.
|
||||
// If not, just leave the links in the form.
|
||||
var previewEl = $('.cms-preview');
|
||||
if(previewEl.length) {
|
||||
// TODO Relies on DOM element order (the second .cms-navigator is the "old" one)
|
||||
previewEl.find('.cms-preview-controls').html(this.find('.cms-navigator').detach());
|
||||
}
|
||||
|
||||
this._super();
|
||||
},
|
||||
|
@ -1,13 +1,11 @@
|
||||
(function($) {
|
||||
$.entwine('ss', function($){
|
||||
$.entwine('ss.preview', function($){
|
||||
|
||||
/**
|
||||
* Shows a previewable website state alongside its editable version in backend UI,
|
||||
* typically a page. This allows CMS users to seamlessly switch between preview and
|
||||
* edit mode in the same browser window. The preview panel is embedded in the layout
|
||||
* of the backend UI, and loads its content via an iframe.
|
||||
*
|
||||
* The admin UI itself is collapsible, leaving most screen space to this panel.
|
||||
*
|
||||
* Relies on the server responses to indicate if a preview URL is available for the
|
||||
* currently loaded admin interface. If no preview is available, the panel is "blocked"
|
||||
@ -17,7 +15,103 @@
|
||||
* while all external links are disabled (via JavaScript).
|
||||
*/
|
||||
$('.cms-preview').entwine({
|
||||
|
||||
|
||||
/**
|
||||
* List of SilverStripeNavigator states (SilverStripeNavigatorItem classes) to search for.
|
||||
* The order is significant - if the state is not available, preview will start searching the list
|
||||
* from the beginning.
|
||||
*/
|
||||
AllowedStates: ['StageLink', 'LiveLink'],
|
||||
|
||||
/**
|
||||
* API
|
||||
* Name of the current preview state - one of the "AllowedStates".
|
||||
*/
|
||||
CurrentStateName: null,
|
||||
|
||||
/**
|
||||
* API
|
||||
* Current size selection.
|
||||
*/
|
||||
CurrentSizeName: 'auto',
|
||||
|
||||
/**
|
||||
* API
|
||||
* Switch the preview to different state.
|
||||
* stateName can be one of the "AllowedStates".
|
||||
*/
|
||||
changeState: function(stateName) {
|
||||
this.setCurrentStateName(stateName);
|
||||
this._updatePreview();
|
||||
this.redraw();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* API
|
||||
* Change the preview mode.
|
||||
* modeName can be: split, content, preview.
|
||||
*/
|
||||
changeMode: function(modeName) {
|
||||
var container = $('.cms-container');
|
||||
|
||||
if (modeName == 'split') {
|
||||
container.entwine('.ss').splitViewMode();
|
||||
} else if (modeName == 'content') {
|
||||
container.entwine('.ss').contentViewMode();
|
||||
} else {
|
||||
container.entwine('.ss').previewMode();
|
||||
}
|
||||
|
||||
this.redraw();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* API
|
||||
* Change the preview size.
|
||||
* sizeName can be: auto, desktop, tablet, mobile.
|
||||
*/
|
||||
changeSize: function(sizeName) {
|
||||
this.setCurrentSizeName(sizeName);
|
||||
|
||||
this.removeClass('auto desktop tablet mobile')
|
||||
.addClass(sizeName);
|
||||
|
||||
this.redraw();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
/**
|
||||
* API
|
||||
* Update the visual appearance to match the internal preview state.
|
||||
*/
|
||||
redraw: function() {
|
||||
if(window.debug) console.log('redraw', this.attr('class'), this.get(0));
|
||||
|
||||
// Update preview state selector.
|
||||
var currentStateName = this.getCurrentStateName();
|
||||
if (currentStateName) {
|
||||
this.find('.cms-preview-states').changeVisibleState(currentStateName);
|
||||
}
|
||||
|
||||
// Update preview mode selectors.
|
||||
var layoutOptions = $('.cms-container').entwine('.ss').getLayoutOptions();
|
||||
if (layoutOptions) {
|
||||
// There are two mode selectors that we need to keep in sync. Redraw both.
|
||||
$('.preview-mode-selector').changeVisibleMode(layoutOptions.mode);
|
||||
}
|
||||
|
||||
// Update preview size selector.
|
||||
var currentSizeName = this.getCurrentSizeName();
|
||||
if (currentSizeName) {
|
||||
this.find('.preview-size-selector').changeVisibleSize(this.getCurrentSizeName());
|
||||
}
|
||||
},
|
||||
|
||||
onadd: function() {
|
||||
var self = this, layoutContainer = this.parent();
|
||||
// this.resizable({
|
||||
@ -34,7 +128,7 @@
|
||||
|
||||
// Load edit view for new page, but only if the preview is activated at the moment.
|
||||
// This avoids e.g. force-redirections of the edit view on RedirectorPage instances.
|
||||
self.loadCurrentPage();
|
||||
self._loadCurrentPage();
|
||||
});
|
||||
|
||||
this.data('cms-preview-initialized', true);
|
||||
@ -42,33 +136,70 @@
|
||||
// Preview might not be available in all admin interfaces - block/disable when necessary
|
||||
this.append('<div class="cms-preview-overlay ui-widget-overlay-light"></div>');
|
||||
this.find('.cms-preview-overlay-light').hide();
|
||||
$('.cms-preview-toggle-link')[this.canPreview() ? 'show' : 'hide']();
|
||||
$('.cms-preview-toggle-link')[this._canPreview() ? 'show' : 'hide']();
|
||||
|
||||
self._fixIframeLinks();
|
||||
this.updatePreview();
|
||||
this._updatePreview();
|
||||
|
||||
this._super();
|
||||
},
|
||||
|
||||
loadUrl: function(url) {
|
||||
/**
|
||||
* Load the URL into the preview iframe.
|
||||
*/
|
||||
_loadUrl: function(url) {
|
||||
this.find('iframe').attr('src', url);
|
||||
},
|
||||
|
||||
updatePreview: function() {
|
||||
var url = $('.cms-edit-form').choosePreviewLink();
|
||||
/**
|
||||
* Fetch available states from the current SilverStripeNavigator (SilverStripeNavigatorItems).
|
||||
* Navigator is supplied by the backend and contains all state options for the current object.
|
||||
*/
|
||||
_getNavigatorStates: function() {
|
||||
// Walk through available states and get the URLs.
|
||||
var urlMap = $.map(this.getAllowedStates(), function(name) {
|
||||
var stateLink = $('.cms-preview-states .switch-options a[name=' + name + ']');
|
||||
return stateLink.length ? {name: name, url: stateLink.attr('href')} : null;
|
||||
});
|
||||
|
||||
if(url) {
|
||||
this.loadUrl(url);
|
||||
this.unblock();
|
||||
} else {
|
||||
this.block();
|
||||
this.toggle();
|
||||
}
|
||||
return urlMap;
|
||||
},
|
||||
|
||||
updateAfterXhr: function(){
|
||||
$('.cms-preview-toggle-link')[this.canPreview() ? 'show' : 'hide']();
|
||||
this.updatePreview();
|
||||
/**
|
||||
* Reload the preview while keeping current state.
|
||||
* Fall back to first preferred state if state is no longer available.
|
||||
*/
|
||||
_updatePreview: function() {
|
||||
var states = this._getNavigatorStates();
|
||||
var currentStateName = this.getCurrentStateName();
|
||||
var currentState = null;
|
||||
|
||||
// Find current state within currently available states.
|
||||
if (states) {
|
||||
currentState = $.grep(states, function(state, index) {
|
||||
return currentStateName===state.name;
|
||||
});
|
||||
}
|
||||
|
||||
if (currentState[0]) {
|
||||
// State is available.
|
||||
this._loadUrl(currentState[0].url);
|
||||
this._unblock();
|
||||
} else if (states.length) {
|
||||
// Fall back to first preferred state.
|
||||
this.setCurrentStateName(states[0].name);
|
||||
this._loadUrl(states[0].url);
|
||||
this._unblock();
|
||||
} else {
|
||||
// No state available.
|
||||
this._block();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
_updateAfterXhr: function(){
|
||||
$('.cms-preview-toggle-link')[this._canPreview() ? 'show' : 'hide']();
|
||||
this._updatePreview();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -76,7 +207,7 @@
|
||||
*/
|
||||
'from .cms-container': {
|
||||
onafterstatechange: function(){
|
||||
this.updateAfterXhr();
|
||||
this._updateAfterXhr();
|
||||
}
|
||||
},
|
||||
|
||||
@ -86,7 +217,7 @@
|
||||
*/
|
||||
'from .cms-container .cms-edit-form': {
|
||||
onaftersubmitform: function(){
|
||||
this.updateAfterXhr();
|
||||
this._updateAfterXhr();
|
||||
}
|
||||
},
|
||||
|
||||
@ -94,10 +225,10 @@
|
||||
* Loads the matching edit form for a page viewed in the preview iframe,
|
||||
* based on metadata sent along with this document.
|
||||
*/
|
||||
loadCurrentPage: function() {
|
||||
var doc = this.find('iframe')[0].contentDocument, containerEl = this.getLayoutContainer();
|
||||
_loadCurrentPage: function() {
|
||||
var doc = this.find('iframe')[0].contentDocument, containerEl = $('.cms-container');
|
||||
|
||||
if(!this.canPreview()) return;
|
||||
if(!this._canPreview()) return;
|
||||
|
||||
// Load this page in the admin interface if appropriate
|
||||
var id = $(doc).find('meta[name=x-page-id]').attr('content');
|
||||
@ -108,7 +239,7 @@
|
||||
// Ignore behaviour without history support (as we need ajax loading
|
||||
// for the new form to load in the background)
|
||||
if(window.History.enabled)
|
||||
$('.cms-container').loadPanel(editLink);
|
||||
$('.cms-container').entwine('.ss').loadPanel(editLink);
|
||||
}
|
||||
},
|
||||
|
||||
@ -117,8 +248,8 @@
|
||||
*
|
||||
* Returns: {boolean}
|
||||
*/
|
||||
canPreview: function() {
|
||||
var contentEl = this.getLayoutContainer().find('.cms-content');
|
||||
_canPreview: function() {
|
||||
var contentEl = $('.cms-container .cms-content');
|
||||
// Only load if we're in the "edit page" view
|
||||
var blockedClasses = ['CMSPagesController', 'CMSPageHistoryController'];
|
||||
return !(contentEl.is('.' + blockedClasses.join(',.')));
|
||||
@ -146,27 +277,38 @@
|
||||
if (href.match(/^http:\/\//)) links[i].setAttribute('target', '_blank');
|
||||
}
|
||||
|
||||
// Hide duplicate navigator, as it replicates existing UI in the CMS
|
||||
// Hide the navigator from the preview iframe and use only the CMS one.
|
||||
var navi = doc.getElementById('SilverStripeNavigator');
|
||||
if(navi) navi.style.display = 'none';
|
||||
var naviMsg = doc.getElementById('SilverStripeNavigatorMessage');
|
||||
if(naviMsg) naviMsg.style.display = 'none';
|
||||
},
|
||||
|
||||
block: function() {
|
||||
_block: function() {
|
||||
this.addClass('blocked');
|
||||
},
|
||||
|
||||
unblock: function() {
|
||||
_unblock: function() {
|
||||
this.removeClass('blocked');
|
||||
},
|
||||
|
||||
getLayoutContainer: function() {
|
||||
return this.parents('.cms-container');
|
||||
},
|
||||
|
||||
redraw: function() {
|
||||
if(window.debug) console.log('redraw', this.attr('class'), this.get(0));
|
||||
}
|
||||
});
|
||||
|
||||
$('.cms-edit-form').entwine({
|
||||
/**
|
||||
* Initialise the navigator - move it from the EditForm to the preview.
|
||||
*/
|
||||
onadd: function() {
|
||||
var previewEl = $('.cms-preview .cms-preview-controls');
|
||||
var navigatorEl = $('.cms-edit-form .cms-navigator');
|
||||
|
||||
if (navigatorEl.length && previewEl.length) {
|
||||
// Preview is available - install the navigator.
|
||||
previewEl.html($('.cms-edit-form .cms-navigator').detach());
|
||||
$('.cms-preview').changeMode('split');
|
||||
} else {
|
||||
// Preview not available.
|
||||
$('.cms-preview').changeMode('content');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -181,63 +323,69 @@
|
||||
}
|
||||
});
|
||||
|
||||
$('.switch-options a').entwine({
|
||||
onclick: function(e) {
|
||||
var preview = $('.cms-preview');
|
||||
var loadSibling = $(this).siblings('a');
|
||||
var checkbox = $(this).closest('.cms-preview-states').find('input');
|
||||
if(checkbox.attr('checked') !== undefined){
|
||||
checkbox.attr('checked', false);
|
||||
}else{
|
||||
checkbox.attr('checked', true);
|
||||
/**
|
||||
* "Preview state" functions.
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
$('.cms-preview-states').entwine({
|
||||
/**
|
||||
* Change the displayed state.
|
||||
*/
|
||||
changeVisibleState: function(state) {
|
||||
// Arbitrary mapping from checkbox state to the preview state.
|
||||
if (state==='LiveLink') {
|
||||
this.find('.cms-preview-checkbox').prop('checked', false);
|
||||
} else {
|
||||
this.find('.cms-preview-checkbox').prop('checked', true);
|
||||
}
|
||||
preview.loadUrl($(loadSibling).attr('href'));
|
||||
}
|
||||
});
|
||||
|
||||
$('.cms-preview-states .switch-options a').entwine({
|
||||
/**
|
||||
* Reacts to the user changing the state of the preview.
|
||||
* TODO Rewrite this function to ensure we can handle 1,2,3+ states.
|
||||
*/
|
||||
onclick: function(e) {
|
||||
var targetStateName = $(this).siblings('a').attr('name');
|
||||
|
||||
// Reload preview with the selected state.
|
||||
$('.cms-preview').changeState(targetStateName);
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$('.preview-mode-selector select').entwine({
|
||||
onchange: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var container = $('.cms-container');
|
||||
var state = $(this).val();
|
||||
|
||||
if (state == 'split') {
|
||||
container.splitViewMode();
|
||||
} else if (state == 'edit') {
|
||||
container.contentViewMode();
|
||||
} else {
|
||||
container.previewMode();
|
||||
}
|
||||
|
||||
this.addIcon();
|
||||
|
||||
// Synchronise other preview-mode selectors to display the same state.
|
||||
$('.preview-mode-selector select').not(this)
|
||||
.val(this.val())
|
||||
/**
|
||||
* "Preview mode" functions
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
$('.preview-mode-selector').entwine({
|
||||
/**
|
||||
* Change the displayed mode.
|
||||
*/
|
||||
changeVisibleMode: function(mode) {
|
||||
this.find('select')
|
||||
.val(mode)
|
||||
.trigger('liszt:updated')
|
||||
.addIcon();
|
||||
._addIcon();
|
||||
}
|
||||
});
|
||||
|
||||
$('.preview-size-selector select').entwine({
|
||||
$('.preview-mode-selector select').entwine({
|
||||
/**
|
||||
* Reacts to the user changing the preview mode.
|
||||
*/
|
||||
onchange: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var preview = $('.cms-preview');
|
||||
var size = $(this).val();
|
||||
|
||||
preview
|
||||
.removeClass('auto desktop tablet mobile')
|
||||
.addClass(size);
|
||||
|
||||
this.addIcon();
|
||||
var targetStateName = $(this).val();
|
||||
$('.cms-preview').changeMode(targetStateName);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* React to state view mode changes by showing/hiding the preview-mode selector.
|
||||
* Adjust the visibility of the preview-mode selector in the CMS part (hidden if preview is visible).
|
||||
*/
|
||||
$('.cms-preview.column-hidden').entwine({
|
||||
onmatch: function() {
|
||||
@ -251,7 +399,7 @@
|
||||
});
|
||||
|
||||
/**
|
||||
* Initialise the CMS's preview-mode selector.
|
||||
* Initialise the preview-mode selector in the CMS part (could be hidden if preview is visible).
|
||||
*/
|
||||
$('#preview-mode-dropdown-in-content').entwine({
|
||||
onmatch: function() {
|
||||
@ -268,18 +416,51 @@
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* "Preview size" functions
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
$('.preview-size-selector').entwine({
|
||||
/**
|
||||
* Change the displayed size.
|
||||
*/
|
||||
changeVisibleSize: function(size) {
|
||||
this.find('select')
|
||||
.val(size)
|
||||
.trigger('liszt:updated')
|
||||
._addIcon();
|
||||
}
|
||||
});
|
||||
|
||||
$('.preview-size-selector select').entwine({
|
||||
/**
|
||||
* Trigger change in the preview size.
|
||||
*/
|
||||
onchange: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var targetSizeName = $(this).val();
|
||||
$('.cms-preview').changeSize(targetSizeName);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Chosen plumbing.
|
||||
* -------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Add a class to the chzn select trigger based on the currently
|
||||
* selected option. Update as this changes
|
||||
*/
|
||||
$('.preview-selector select.preview-dropdown').entwine({
|
||||
'onliszt:showing_dropdown': function() {
|
||||
this.siblings().find('.chzn-drop').addClass('open').alignRight();
|
||||
this.siblings().find('.chzn-drop').addClass('open')._alignRight();
|
||||
},
|
||||
'onliszt:hiding_dropdown': function() {
|
||||
this.siblings().find('.chzn-drop').removeClass('open').removeRightAlign();
|
||||
this.siblings().find('.chzn-drop').removeClass('open')._removeRightAlign();
|
||||
},
|
||||
addIcon: function(){
|
||||
_addIcon: function(){
|
||||
var selected = this.find(':selected');
|
||||
var iconClass = selected.attr('data-icon');
|
||||
|
||||
@ -299,7 +480,7 @@
|
||||
*/
|
||||
$('.preview-selector a.chzn-single').entwine({
|
||||
onmatch: function() {
|
||||
this.closest('.preview-selector').find('select').addIcon();
|
||||
this.closest('.preview-selector').find('select')._addIcon();
|
||||
this._super();
|
||||
},
|
||||
onunmatch: function() {
|
||||
@ -309,7 +490,7 @@
|
||||
|
||||
|
||||
$('.preview-selector .chzn-drop').entwine({
|
||||
alignRight: function(){
|
||||
_alignRight: function(){
|
||||
var that = this;
|
||||
$(this).hide();
|
||||
/* Delay so styles applied after chosen applies css
|
||||
@ -320,7 +501,7 @@
|
||||
$(that).show();
|
||||
}, 100);
|
||||
},
|
||||
removeRightAlign:function(){
|
||||
_removeRightAlign:function(){
|
||||
$(this).css({right:'auto'});
|
||||
}
|
||||
|
||||
@ -361,41 +542,32 @@
|
||||
}
|
||||
}); */
|
||||
|
||||
|
||||
$('.cms-edit-form').entwine({
|
||||
/**
|
||||
* Choose applicable preview link based on form data,
|
||||
* in a fixed order of priority: The PreviewURL field is used as an override,
|
||||
* which falls back to stage or live URLs.
|
||||
*
|
||||
* @return String Absolute URL
|
||||
*/
|
||||
choosePreviewLink: function() {
|
||||
var self = this, urls = $.map(['PreviewURL', 'StageLink', 'LiveLink'], function(name) {
|
||||
var val = self.find(':input[name=' + name + ']').val();
|
||||
return val ? val : null;
|
||||
});
|
||||
return urls ? urls[0] : false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Recalculate the preview space to allow for horizontal scrollbar and the preview actions panel
|
||||
var toolbarSize = 53; // Height of the preview actions panel
|
||||
/**
|
||||
* Recalculate the preview space to allow for horizontal scrollbar and the preview actions panel
|
||||
*/
|
||||
$('.preview-scroll').entwine({
|
||||
redraw: function() {
|
||||
/**
|
||||
* Height of the preview actions panel
|
||||
*/
|
||||
ToolbarSize: 53,
|
||||
|
||||
_redraw: function() {
|
||||
var toolbarSize = this.getToolbarSize();
|
||||
|
||||
if(window.debug) console.log('redraw', this.attr('class'), this.get(0));
|
||||
var previewHeight = (this.height() - toolbarSize);
|
||||
this.height(previewHeight);
|
||||
},
|
||||
|
||||
onmatch: function() {
|
||||
this.redraw();
|
||||
this._redraw();
|
||||
this._super();
|
||||
},
|
||||
|
||||
onunmatch: function() {
|
||||
this._super();
|
||||
}
|
||||
// Todo: Need to recalculate on resize of browser
|
||||
// TODO: Need to recalculate on resize of browser
|
||||
|
||||
});
|
||||
|
||||
|
@ -20,12 +20,12 @@
|
||||
</span>
|
||||
|
||||
<div class="cms-preview-states switch-states">
|
||||
<input type="checkbox" name="cms-preview" class="state cms-preview" id="cms-preview-state" checked>
|
||||
<input type="checkbox" name="cms-preview" class="state cms-preview-checkbox" id="cms-preview-state" checked>
|
||||
<label for="cms-preview-state">
|
||||
<span class="switch-options">
|
||||
<% loop Items %>
|
||||
$Items.count
|
||||
<a href="$Link" class="$FirstLast <% if isActive %> active<% end_if %>">$Title</a>
|
||||
<a href="$Link" name="$Name" class="$FirstLast <% if isActive %> active<% end_if %>">$Title</a>
|
||||
<% end_loop %>
|
||||
</span>
|
||||
<span class="switch"></span>
|
||||
|
@ -27,13 +27,17 @@ This causes the framework to:
|
||||
to the layout manager)
|
||||
* trigger `redraw` on children which also cascades deeper into the hierarchy (this is framework activity)
|
||||
|
||||
Caveat #1: `layout` is also triggered when a DOM element is replaced with AJAX in `LeftAndMain::handleAjaxResponse`. In
|
||||
<div class="notice" markdown='1'>
|
||||
Caveat: `layout` is also triggered when a DOM element is replaced with AJAX in `LeftAndMain::handleAjaxResponse`. In
|
||||
this case it is triggered on the parent of the element being replaced so jLayout has a chance to rebuild its algorithms.
|
||||
Calling the top level `layout` is not enough as it will wrongly descend down the detached element's hierarchy.
|
||||
</div>
|
||||
|
||||
Caveat #2: invocation order of the `redraws` is crucial here, generally going from innermost to outermost elements. For
|
||||
<div class="notice" markdown='1'>
|
||||
Caveat: invocation order of the `redraws` is crucial here, generally going from innermost to outermost elements. For
|
||||
example, the tab panels have be applied in the CMS form before the form itself is layouted with its sibling panels to
|
||||
avoid incorrect dimensions.
|
||||
</div>
|
||||
|
||||
![Layout variations](_images/cms-architecture.png)
|
||||
|
||||
@ -70,12 +74,12 @@ panel to the CMS UI.
|
||||
The following methods are available as an interface to underlying _threeColumnCompressor_ algorithm on the
|
||||
`.cms-container` entwine:
|
||||
|
||||
* _getLayoutOptions_: get currently used _threeColumnCompressor_ options.
|
||||
* _updateLayoutOptions_: change specified options and trigger the laying out:
|
||||
* **getLayoutOptions**: get currently used _threeColumnCompressor_ options.
|
||||
* **updateLayoutOptions**: change specified options and trigger the laying out:
|
||||
`$('.cms-container').updateLayoutOptions({mode: 'split'});`
|
||||
* _splitViewMode_: enable side by side editing.
|
||||
* _contentViewMode_: only menu and content areas are shown.
|
||||
* _previewMode_: only menu and preview areas are shown.
|
||||
* **splitViewMode**: enable side by side editing.
|
||||
* **contentViewMode**: only menu and content areas are shown.
|
||||
* **previewMode**: only menu and preview areas are shown.
|
||||
|
||||
### CSS classes
|
||||
|
||||
@ -103,7 +107,7 @@ The parameters are as follows:
|
||||
* **column-spec-object**: object providing the _menu_, _content_ and _preview_ elements (all fields mandatory)
|
||||
* **options-object**: object providing the configuration (all fields mandatory, see options below)
|
||||
|
||||
### Available options
|
||||
### Layout options
|
||||
|
||||
* _minContentWidth_: minimum size for the content display as long as the preview is visible
|
||||
* _minPreviewWidth_: preview will not be displayed below this size
|
||||
@ -112,4 +116,5 @@ The parameters are as follows:
|
||||
## Related
|
||||
|
||||
* [Reference: CMS Architecture](../reference/cms-architecture)
|
||||
* [Reference: Preview](../reference/preview)
|
||||
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)
|
||||
|
104
docs/en/reference/preview.md
Normal file
104
docs/en/reference/preview.md
Normal file
@ -0,0 +1,104 @@
|
||||
# CMS preview
|
||||
|
||||
## Overview
|
||||
|
||||
With the addition of side-by-side editing, the preview has the ability to appear within the CMS window when editing
|
||||
content in the _Pages_ section of the CMS. The site is rendered into an iframe. It will update itself whenever the
|
||||
content is saved, and relevant pages will be loaded for editing when the user navigates around in the preview.
|
||||
|
||||
The root element for preview is `.cms-preview` which maintains the internal states neccessary for rendering. It provides
|
||||
function calls for transitioning between these states and has the ability to redraw the area.
|
||||
|
||||
In terms of backend support, it relies on `SilverStripeNavigator` to be rendered into the `.cms-edit-form`.
|
||||
_LeftAndMain_ will automatically take care of generating it as long as the `*_SilverStripeNavigator` template is found -
|
||||
first segment has to match current _LeftAndMain_-derived class (e.g. `LeftAndMain_SilverStripeNavigator`).
|
||||
|
||||
<div class="notice" markdown='1'>
|
||||
Caveat: `SilverStripeNavigator` and `CMSPreviewable` interface currently only support SiteTree objects that are
|
||||
_Versioned_. They are not general enough for using on any other DataObject. That pretty much limits the extendability
|
||||
of the feature.
|
||||
</div>
|
||||
|
||||
If the `SilverStripeNavigator` structure is found, it is detached and installed in the `.cms-preview-control` panel at
|
||||
the bottom of the preview, and the preview is enabled into _split_ mode.
|
||||
|
||||
We use `ss.preview` entwine namespace for all preview-related entwines.
|
||||
|
||||
## Preview states
|
||||
|
||||
States are the site stages: _live_, _stage_ etc. Preview states are picked up from the `SilverStripeNavigator`.
|
||||
You can invoke the state change by calling:
|
||||
|
||||
```js
|
||||
$('.cms-preview').entwine('.ss.preview').changeState('StageLink');
|
||||
```
|
||||
|
||||
Note the state names come from `SilverStripeNavigatorItems` class names - thus the _Link_ in their names. This call will
|
||||
also redraw the state selector to fit with the internal state. See `AllowedStates` in `.cms-preview` entwine for the
|
||||
list of supported states.
|
||||
|
||||
You can get the current state by calling:
|
||||
|
||||
```js
|
||||
$('.cms-preview').entwine('.ss.preview').getCurrentStateName();
|
||||
```
|
||||
|
||||
## Preview sizes
|
||||
|
||||
This selector defines how the preview iframe is rendered, and try to emulate different device sizes. The options are
|
||||
hardcoded. The option names map directly to CSS classes applied to the `.cms-preview` and are as follows:
|
||||
|
||||
* _auto_: responsive layout
|
||||
* _desktop_
|
||||
* _tablet_
|
||||
* _mobile_
|
||||
|
||||
You can switch between different types of display sizes programmatically, which has the benefit of redrawing the
|
||||
related selector and maintaining a consistent internal state:
|
||||
|
||||
```js
|
||||
$('.cms-preview').entwine('.ss.preview').changeSize('auto');
|
||||
```
|
||||
|
||||
You can find out current size by calling:
|
||||
|
||||
```js
|
||||
$('.cms-preview').entwine('.ss.preview').getCurrentSizeName();
|
||||
```
|
||||
|
||||
## Preview modes
|
||||
|
||||
Preview modes map to the modes supported by the _threeColumnCompressor_ layout algorithm, see
|
||||
[layout reference](../reference/layout) for more details. You can change modes by calling:
|
||||
|
||||
```js
|
||||
$('.cms-preview').entwine('.ss.preview').changeMode('preview');
|
||||
```
|
||||
|
||||
Currently active mode is stored on the `.cms-container` along with related internal states of the layout. You can reach
|
||||
it by calling:
|
||||
|
||||
```js
|
||||
$('.cms-container').entwine('.ss').getLayoutOptions().mode;
|
||||
```
|
||||
|
||||
<div class="notice" markdown='1'>
|
||||
Caveat: the `.preview-mode-selector` appears twice, once in the preview and second time in the CMS actions area as
|
||||
`#preview-mode-dropdown-in-cms`. This is done because the user should still have access to the mode selector even if
|
||||
preview is not visible. Currently CMS Actions are a separate area to the preview option selectors, even if they try
|
||||
to appear as one horizontal bar.
|
||||
</div>
|
||||
|
||||
## Preview API
|
||||
|
||||
Namespace `ss.preview`, selector `.cms-preview`:
|
||||
|
||||
* **getCurrentStateName**: get the name of the current state (e.g. _LiveLink_ or _StageLink_).
|
||||
* **getCurrentSizeName**: get the name of the current device size.
|
||||
* **changeState**: one of the `AllowedStates`.
|
||||
* **changeSize**: one of _auto_, _desktop_, _tablet_, _mobile_.
|
||||
* **changeMode**: maps to _threeColumnLayout_ modes - _split_, _preview_, _content_.
|
||||
|
||||
## Related
|
||||
|
||||
* [Reference: Layout](../reference/layout)
|
@ -3,7 +3,7 @@
|
||||
|
||||
<option data-icon="icon-split" class="icon-split icon-view first" value="split"><% _t('SilverStripeNavigator.SplitView', 'Split mode') %></option>
|
||||
<option data-icon="icon-preview" class="icon-preview icon-view" value="preview"><% _t('SilverStripeNavigator.PreviewView', 'Preview mode') %></option>
|
||||
<option data-icon="icon-edit" class="icon-edit icon-view" value="edit"><% _t('SilverStripeNavigator.EditView', 'Edit mode') %></option>
|
||||
<option data-icon="icon-edit" class="icon-edit icon-view last" value="content"><% _t('SilverStripeNavigator.EditView', 'Edit mode') %></option>
|
||||
<!-- Dual window not implemented yet -->
|
||||
<!--
|
||||
<option data-icon="icon-window" class="icon-window icon-view last" value="window"><% _t('SilverStripeNavigator.DualWindowView', 'Dual Window') %></option>
|
||||
|
Loading…
Reference in New Issue
Block a user