mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API Refactor the CMS layouting to provide access to options.
It is now possible to change the threeColumnLayout width options for the columns via entwine property LayoutOptions and accessor methods. Thanks @robert-h-curry, @clarkepaul for contributing!
This commit is contained in:
parent
544d2eb6e1
commit
d4f13fe532
@ -1463,10 +1463,6 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
return $this->CSSClasses('Controller');
|
||||
}
|
||||
|
||||
public function IsPreviewExpanded() {
|
||||
return ($this->request->getVar('cms-preview-expanded'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
|
@ -338,6 +338,8 @@ body.cms { overflow: hidden; }
|
||||
|
||||
.cms-edit-form.CMSMain { padding: 0; }
|
||||
|
||||
.cms-container .column-hidden { display: none; }
|
||||
|
||||
/** -------------------------------------------- Tabs -------------------------------------------- */
|
||||
.ui-tabs { padding: 0; background: none; }
|
||||
.ui-tabs .ui-tabs { position: static; }
|
||||
@ -571,6 +573,8 @@ p.message { margin-bottom: 12px; }
|
||||
select.preview-dropdown { display: inline; width: auto; padding-right: 20px; }
|
||||
|
||||
.cms-preview { background-color: #eceff1; height: 100%; width: 100%; }
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
.cms-preview.is-collapsed .cms-preview-toggle a, .cms-preview.is-collapsed .cms-preview-toggle .cms .jstree .jstree-wholerow a:hover, .cms .jstree .jstree-wholerow .cms-preview.is-collapsed .cms-preview-toggle a:hover, .cms-preview.is-collapsed .cms-preview-toggle .cms .jstree .jstree-wholerow #vakata-contextmenu li.vakata-hover > a, .cms .jstree .jstree-wholerow #vakata-contextmenu .cms-preview.is-collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.is-collapsed .cms-preview-toggle .cms #vakata-contextmenu .jstree .jstree-wholerow li.vakata-hover > a, .cms #vakata-contextmenu .jstree .jstree-wholerow .cms-preview.is-collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.is-collapsed .cms-preview-toggle .cms .jstree .jstree-wholerow .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu li.vakata-hover > a, .cms .jstree .jstree-wholerow .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .cms-preview.is-collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.is-collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .cms .jstree .jstree-wholerow li.vakata-hover > a, .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .cms .jstree .jstree-wholerow .cms-preview.is-collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.is-collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow a:hover, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow .cms-preview.is-collapsed .cms-preview-toggle a:hover, .cms-preview.is-collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow .cms #vakata-contextmenu li.vakata-hover > a, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow .cms #vakata-contextmenu .cms-preview.is-collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.is-collapsed .cms-preview-toggle .cms #vakata-contextmenu .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow li.vakata-hover > a, .cms #vakata-contextmenu .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow .cms-preview.is-collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.is-collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow #vakata-contextmenu li.vakata-hover > a, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow #vakata-contextmenu .cms-preview.is-collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.is-collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .jstree .jstree-wholerow li.vakata-hover > a, .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .jstree .jstree-wholerow .cms-preview.is-collapsed .cms-preview-toggle li.vakata-hover > a { left: -15px; }
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
@ -579,6 +583,11 @@ select.preview-dropdown { display: inline; width: auto; padding-right: 20px; }
|
||||
=======
|
||||
.cms-preview .preview-scroll { height: 100%; width: 100%; overflow: auto; }
|
||||
=======
|
||||
=======
|
||||
.cms-preview.collapsed .cms-preview-toggle a, .cms-preview.collapsed .cms-preview-toggle .cms .jstree .jstree-wholerow a:hover, .cms .jstree .jstree-wholerow .cms-preview.collapsed .cms-preview-toggle a:hover, .cms-preview.collapsed .cms-preview-toggle .cms .jstree .jstree-wholerow #vakata-contextmenu li.vakata-hover > a, .cms .jstree .jstree-wholerow #vakata-contextmenu .cms-preview.collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.collapsed .cms-preview-toggle .cms #vakata-contextmenu .jstree .jstree-wholerow li.vakata-hover > a, .cms #vakata-contextmenu .jstree .jstree-wholerow .cms-preview.collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.collapsed .cms-preview-toggle .cms .jstree .jstree-wholerow .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu li.vakata-hover > a, .cms .jstree .jstree-wholerow .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .cms-preview.collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .cms .jstree .jstree-wholerow li.vakata-hover > a, .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .cms .jstree .jstree-wholerow .cms-preview.collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow a:hover, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow .cms-preview.collapsed .cms-preview-toggle a:hover, .cms-preview.collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow .cms #vakata-contextmenu li.vakata-hover > a, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow .cms #vakata-contextmenu .cms-preview.collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.collapsed .cms-preview-toggle .cms #vakata-contextmenu .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow li.vakata-hover > a, .cms #vakata-contextmenu .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow .cms-preview.collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow #vakata-contextmenu li.vakata-hover > a, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow #vakata-contextmenu .cms-preview.collapsed .cms-preview-toggle li.vakata-hover > a, .cms-preview.collapsed .cms-preview-toggle .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .jstree .jstree-wholerow li.vakata-hover > a, .TreeDropdownField .treedropdownfield-panel #vakata-contextmenu .jstree .jstree-wholerow .cms-preview.collapsed .cms-preview-toggle li.vakata-hover > a { left: -15px; }
|
||||
>>>>>>> API Refactor the layout calls to allow for all mode options.
|
||||
=======
|
||||
>>>>>>> Hide columns using css classes. The class is now called column-hidden.
|
||||
.cms-preview .preview-note { font-weight: 18px; display: block; position: absolute; text-align: center; width: 300px; height: 82px; left: 50%; top: 50%; margin-top: -50px; margin-left: -150px; /* half of width */ color: #CDD7DC; font-weight: bold; font-size: 22px; text-shadow: 0 1px 0 #fff; }
|
||||
.cms-preview .preview-note span { background: url('../images/sprites-64x64-s88957ee578.png') 0 0 no-repeat; width: 50px; height: 41px; margin: 0 auto 20px; display: block; }
|
||||
.cms-preview .preview-scroll { height: 100%; width: 100%; overflow: auto; position: relative; }
|
||||
|
@ -6,20 +6,64 @@
|
||||
|
||||
$.fn.layout.defaults.resize = false;
|
||||
|
||||
var minMenuWidth = 40;
|
||||
var maxMenuWidth = 150;
|
||||
var prefContentWidth = 820;
|
||||
var prefPreviewWidth = 500;
|
||||
var minPreviewWidth = 400;
|
||||
|
||||
/**
|
||||
* Acccess the global variable in the same way the plugin does it.
|
||||
*/
|
||||
jLayout = (typeof jLayout === 'undefined') ? {} : jLayout;
|
||||
|
||||
jLayout.threeColumnCompressor = function (spec) {
|
||||
var obj = {}, menu = $.jLayoutWrap(spec.menu), content = $.jLayoutWrap(spec.content), preview = $.jLayoutWrap(spec.preview);
|
||||
/**
|
||||
* Factory function for generating new type of algorithm for our CMS.
|
||||
*
|
||||
* Spec requires a definition of three column elements:
|
||||
* - `menu` on the left
|
||||
* - `content` area in the middle (includes the EditForm, side tool panel, actions, breadcrumbs and tabs)
|
||||
* - `preview` on the right (will be shown if there is enough space)
|
||||
*
|
||||
* Required options:
|
||||
* - `minContentWidth`: minimum size for the content display as long as the preview is visible
|
||||
* - `minPreviewWidth`: preview will not be displayed below this size
|
||||
* - `mode`: one of "split", "content" or "preview"
|
||||
*
|
||||
* The algorithm first checks which columns are to be visible and which hidden.
|
||||
*
|
||||
* In the case where both preview and content should be shown it first tries to assign half of non-menu space to
|
||||
* preview and the other half to content. Then if there is not enough space for either content or preview, it tries
|
||||
* to allocate the minimum acceptable space to that column, and the rest to the other one. If the minimum
|
||||
* requirements are still not met, it falls back to showing content only.
|
||||
*
|
||||
* @param spec A structure defining columns and parameters as per above.
|
||||
*/
|
||||
jLayout.threeColumnCompressor = function (spec, options) {
|
||||
// Spec sanity checks.
|
||||
if (typeof spec.menu==='undefined' ||
|
||||
typeof spec.content==='undefined' ||
|
||||
typeof spec.preview==='undefined') {
|
||||
throw 'Spec is invalid. Please provide "menu", "content" and "preview" elements.';
|
||||
}
|
||||
if (typeof options.minContentWidth==='undefined' ||
|
||||
typeof options.minPreviewWidth==='undefined' ||
|
||||
typeof options.mode==='undefined') {
|
||||
throw 'Spec is invalid. Please provide "minContentWidth", "minPreviewWidth", "mode"';
|
||||
}
|
||||
if (options.mode!=='split' && options.mode!=='content' && options.mode!=='preview') {
|
||||
throw 'Spec is invalid. "mode" should be either "split", "content" or "preview"';
|
||||
}
|
||||
|
||||
// Instance of the algorithm being produced.
|
||||
var obj = {
|
||||
options: options
|
||||
};
|
||||
|
||||
// Internal column handles, also implementing layout.
|
||||
var menu = $.jLayoutWrap(spec.menu),
|
||||
content = $.jLayoutWrap(spec.content),
|
||||
preview = $.jLayoutWrap(spec.preview);
|
||||
|
||||
/**
|
||||
* Required interface implementations follow.
|
||||
* Refer to https://github.com/bramstein/jlayout#layout-algorithms for the interface spec.
|
||||
*/
|
||||
obj.layout = function (container) {
|
||||
var contentHidden = (content.item.is('.is-collapsed'));
|
||||
|
||||
var size = container.bounds(),
|
||||
insets = container.insets(),
|
||||
top = insets.top,
|
||||
@ -27,55 +71,71 @@
|
||||
left = insets.left,
|
||||
right = size.width - insets.right;
|
||||
|
||||
var menuWidth = $('#cms-menu.cms-panel').hasClass('collapsed') ? minMenuWidth : maxMenuWidth;
|
||||
var contentWidth = contentHidden ? 0 : prefContentWidth;
|
||||
var previewWidth = right - left - (menuWidth + contentWidth);
|
||||
var menuWidth = spec.menu.width(),
|
||||
contentWidth = 0,
|
||||
previewWidth = 0;
|
||||
|
||||
if (!contentHidden) {
|
||||
var previewWidth = right - left - (menuWidth + contentWidth);
|
||||
var previewUnderlay = false;
|
||||
|
||||
// If preview width is less than the minimum size, take some off the menu
|
||||
if (previewWidth < prefPreviewWidth) {
|
||||
if (previewWidth < minPreviewWidth) {
|
||||
contentWidth = right - left - menuWidth;
|
||||
if (this.options.mode==='preview') {
|
||||
// All non-menu space allocated to preview.
|
||||
contentWidth = 0;
|
||||
previewWidth = right - left - menuWidth;
|
||||
previewUnderlay = true;
|
||||
|
||||
if (contentWidth < prefContentWidth) {
|
||||
} else if (this.options.mode==='content') {
|
||||
// All non-menu space allocated to content.
|
||||
contentWidth = right - left - menuWidth;
|
||||
previewWidth = right - left - menuWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (previewWidth > 500) {
|
||||
previewWidth = 0;
|
||||
} else { // ==='split'
|
||||
// Split view - first try 50-50 distribution.
|
||||
contentWidth = (right - left - menuWidth) / 2;
|
||||
previewWidth = right - left - (menuWidth + contentWidth);
|
||||
|
||||
// If violating one of the minima, try to readjust towards satisfying it.
|
||||
if (contentWidth < this.options.minContentWidth) {
|
||||
contentWidth = this.options.minContentWidth;
|
||||
previewWidth = right - left - (menuWidth + contentWidth);
|
||||
} else if (previewWidth < this.options.minPreviewWidth) {
|
||||
previewWidth = this.options.minPreviewWidth;
|
||||
contentWidth = right - left - (menuWidth + previewWidth);
|
||||
}
|
||||
|
||||
// If still violating one of the (other) minima, remove the preview and allocate everything to content.
|
||||
if (contentWidth < this.options.minContentWidth || previewWidth < this.options.minPreviewWidth) {
|
||||
contentWidth = right - left - menuWidth;
|
||||
previewWidth = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply classes for elements that might not be visible at all.
|
||||
spec.content.toggleClass('column-hidden', contentWidth===0);
|
||||
spec.preview.toggleClass('column-hidden', previewWidth===0);
|
||||
|
||||
// Apply the widths to columns, and call subordinate layouts to arrange the children.
|
||||
menu.bounds({'x': left, 'y': top, 'height': bottom - top, 'width': menuWidth});
|
||||
menu.doLayout();
|
||||
|
||||
left += menuWidth;
|
||||
|
||||
content.bounds({'x': left, 'y': top, 'height': bottom - top, 'width': contentWidth});
|
||||
content.item.css({display: contentHidden ? 'none' : 'block'});
|
||||
content.doLayout();
|
||||
if (!previewUnderlay) left += contentWidth;
|
||||
|
||||
left += contentWidth;
|
||||
|
||||
preview.bounds({'x': left, 'y': top, 'height': bottom - top, 'width': previewWidth});
|
||||
preview.doLayout();
|
||||
|
||||
|
||||
return container;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to generate the required `preferred`, `minimum` and `maximum` interface functions.
|
||||
*/
|
||||
function typeLayout(type) {
|
||||
var func = type + 'Size';
|
||||
|
||||
return function (container) {
|
||||
var menuSize = menu[func](), contentSize = content[func](), previewSize = preview[func](), insets = container.insets();
|
||||
var menuSize = menu[func](),
|
||||
contentSize = content[func](),
|
||||
previewSize = preview[func](),
|
||||
insets = container.insets();
|
||||
|
||||
width = menuSize.width + contentSize.width + previewSize.width;
|
||||
height = Math.max(menuSize.height, contentSize.height, previewSize.height);
|
||||
@ -87,9 +147,11 @@
|
||||
};
|
||||
}
|
||||
|
||||
// Generate interface functions.
|
||||
obj.preferred = typeLayout('preferred');
|
||||
obj.minimum = typeLayout('minimum');
|
||||
obj.maximum = typeLayout('maximum');
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
|
@ -218,14 +218,17 @@
|
||||
onchange: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var content = $('.cms-content');
|
||||
var container = $('.cms-container');
|
||||
var state = $(this).val();
|
||||
|
||||
if (state == 'split') {
|
||||
content.removeClass('is-collapsed');
|
||||
container.splitViewMode();
|
||||
} else if (state == 'edit') {
|
||||
container.contentViewMode();
|
||||
} else {
|
||||
content.addClass('is-collapsed');
|
||||
container.previewMode();
|
||||
}
|
||||
content.parent().redraw();
|
||||
|
||||
this.addIcon(); //run generic addIcon, on select.preview-dropdown
|
||||
}
|
||||
});
|
||||
|
@ -102,6 +102,17 @@ jQuery.noConflict();
|
||||
|
||||
StateChangeCount: 0,
|
||||
|
||||
/**
|
||||
* Options for the threeColumnCompressor layout algorithm.
|
||||
*
|
||||
* See LeftAndMain.Layout.js for description of these options.
|
||||
*/
|
||||
LayoutOptions: {
|
||||
minContentWidth: 820,
|
||||
minPreviewWidth: 400,
|
||||
mode: 'split'
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructor: onmatch
|
||||
*/
|
||||
@ -145,14 +156,66 @@ jQuery.noConflict();
|
||||
onaftersubmitform: function(){ this.redraw(); }
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensure the user can see the requested section - restore the default view.
|
||||
*/
|
||||
'from .cms-menu-list li a': {
|
||||
onclick: function() {
|
||||
this.splitViewMode();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Change the options of the threeColumnCompressor layout, and trigger layouting. You can provide any or
|
||||
* all options. The remaining options will not be changed.
|
||||
*/
|
||||
updateLayoutOptions: function(newSpec) {
|
||||
var spec = this.getLayoutOptions();
|
||||
$.extend(spec, newSpec);
|
||||
this.redraw();
|
||||
},
|
||||
|
||||
/**
|
||||
* Enable the split view - with content on the left and preview on the right.
|
||||
*/
|
||||
splitViewMode: function() {
|
||||
this.updateLayoutOptions({
|
||||
mode: 'split'
|
||||
});
|
||||
this.redraw();
|
||||
},
|
||||
|
||||
/**
|
||||
* Content only.
|
||||
*/
|
||||
contentViewMode: function() {
|
||||
this.updateLayoutOptions({
|
||||
mode: 'content'
|
||||
});
|
||||
this.redraw();
|
||||
},
|
||||
|
||||
/**
|
||||
* Preview only.
|
||||
*/
|
||||
previewMode: function() {
|
||||
this.updateLayoutOptions({
|
||||
mode: 'preview'
|
||||
});
|
||||
this.redraw();
|
||||
},
|
||||
|
||||
redraw: function() {
|
||||
if(window.debug) console.log('redraw', this.attr('class'), this.get(0));
|
||||
|
||||
// Use the three column compressor layout, which squishes preview, then menu, then content
|
||||
this.data('jlayout', jLayout.threeColumnCompressor({
|
||||
// Reset the algorithm.
|
||||
this.data('jlayout', jLayout.threeColumnCompressor(
|
||||
{
|
||||
menu: this.children('.cms-menu'),
|
||||
content: this.children('.cms-content'),
|
||||
preview: this.children('.cms-preview')}
|
||||
preview: this.children('.cms-preview')
|
||||
},
|
||||
this.getLayoutOptions()
|
||||
));
|
||||
|
||||
// Trigger layout algorithm once at the top. This also lays out children - we move from outside to
|
||||
@ -426,10 +489,9 @@ jQuery.noConflict();
|
||||
|
||||
// Set loading state and store element state
|
||||
var origStyle = contentEl.attr('style');
|
||||
var origVisible = contentEl.is(':visible');
|
||||
var origParent = contentEl.parent();
|
||||
var origParentLayoutApplied = (typeof origParent.data('jlayout')!=='undefined');
|
||||
var layoutClasses = ['east', 'west', 'center', 'north', 'south'];
|
||||
var layoutClasses = ['east', 'west', 'center', 'north', 'south', 'column-hidden'];
|
||||
var elemClasses = contentEl.attr('class');
|
||||
var origLayoutClasses = [];
|
||||
if(elemClasses) {
|
||||
@ -443,7 +505,6 @@ jQuery.noConflict();
|
||||
.removeClass(layoutClasses.join(' '))
|
||||
.addClass(origLayoutClasses.join(' '));
|
||||
if(origStyle) newContentEl.attr('style', origStyle);
|
||||
newContentEl.css('visibility', 'hidden');
|
||||
|
||||
// Allow injection of inline styles, as they're not allowed in the document body.
|
||||
// Not handling this through jQuery.ondemand to avoid parsing the DOM twice.
|
||||
@ -453,9 +514,6 @@ jQuery.noConflict();
|
||||
// Replace panel completely (we need to override the "layout" attribute, so can't replace the child instead)
|
||||
contentEl.replaceWith(newContentEl);
|
||||
|
||||
// Unset loading and restore element state (to avoid breaking existing panel visibility, e.g. with preview expanded)
|
||||
if(origVisible) newContentEl.css('visibility', 'visible');
|
||||
|
||||
// Force jlayout to rebuild internal hierarchy to point to the new elements.
|
||||
// This is only necessary for elements that are at least 3 levels deep. 2nd level elements will
|
||||
// be taken care of when we lay out the top level element (.cms-container).
|
||||
|
@ -137,6 +137,12 @@ body.cms {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
// Hide threeColumnCompressor column.
|
||||
.cms-container {
|
||||
.column-hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/** --------------------------------------------
|
||||
* Tabs
|
||||
@ -917,14 +923,6 @@ select.preview-dropdown {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
&.is-collapsed {
|
||||
.cms-preview-toggle {
|
||||
a {
|
||||
left: -15px; // point left
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.preview-note {
|
||||
font-weight: 18px;
|
||||
display: block;
|
||||
|
@ -26,16 +26,13 @@
|
||||
</select>
|
||||
</span>
|
||||
|
||||
|
||||
<div class="cms-preview-states switch-states">
|
||||
<input type="checkbox" name="cms-preview" class="state cms-preview" 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" class="$FirstLast <% if isActive %> active<% end_if %>">$Title</a>
|
||||
<% end_loop %>
|
||||
</span>
|
||||
<span class="switch"></span>
|
||||
|
@ -13,7 +13,7 @@
|
||||
$Menu
|
||||
$Content
|
||||
|
||||
<div class="cms-preview east <% if IsPreviewExpanded %>is-expanded<% else %>is-collapsed<% end_if %>" data-layout-type="border">
|
||||
<div class="cms-preview east" data-layout-type="border">
|
||||
<div class="preview-note"><span><!-- --></span><% _t('CMSPageHistoryController_versions.ss.PREVIEW','Website preview') %></div>
|
||||
<div class="preview-scroll">
|
||||
<div class="preview-device-outer">
|
||||
|
@ -21,11 +21,13 @@ the common `Page` object (a new PHP class `MyPage` will look for a `MyPage.ss` t
|
||||
We can use this to create a different base template with `LeftAndMain.ss`
|
||||
(which corresponds to the `LeftAndMain` PHP controller class).
|
||||
|
||||
Copy the template markup of the base implementation at `framework/admin/templates/LeftAndMain.ss` into `mysite/templates/LeftAndMain.ss`. It will automatically be picked up by the CMS logic. Add a new section after the `$Content` tag:
|
||||
Copy the template markup of the base implementation at `framework/admin/templates/LeftAndMain.ss` into
|
||||
`mysite/templates/LeftAndMain.ss`. It will automatically be picked up by the CMS logic. Add a new section after
|
||||
the `$Content` tag:
|
||||
|
||||
:::ss
|
||||
...
|
||||
<div class="cms-container" data-layout="{type: 'border'}">
|
||||
<div class="cms-container" data-layout-type="border">
|
||||
$Menu
|
||||
$Content
|
||||
<div class="cms-bottom-bar south">
|
||||
@ -46,6 +48,8 @@ plus the height value in our CSS. It instructs the existing parent layout how to
|
||||
This layout manager ([jLayout](http://www.bramstein.com/projects/jlayout/))
|
||||
allows us to build complex layouts with minimal JavaScript configuration.
|
||||
|
||||
See [layout reference](../reference/layout) for more specific information on CMS layouting.
|
||||
|
||||
## Include custom CSS in the CMS
|
||||
|
||||
In order to show the links in one line, we'll add some CSS, and get it to load with the CMS interface.
|
||||
@ -129,5 +133,6 @@ blocks and concepts for more complex extensions as well.
|
||||
|
||||
## Related
|
||||
|
||||
* [CMS Architecture](../reference/cms-architecture)
|
||||
* [Reference: CMS Architecture](../reference/cms-architecture)
|
||||
* [Reference: Layout](../reference/layout)
|
||||
* [Topics: Rich Text Editing](../topics/rich-text-editing)
|
||||
|
@ -85,25 +85,11 @@ So to add or "subclass" a tools panel, simply create this file and it's automati
|
||||
|
||||
## Layout and Panels
|
||||
|
||||
The CMS markup is structured into "panels", which are the base units containing
|
||||
interface components (or other panels), as declared by the class `cms-panel`. Panels can be made collapsible, and
|
||||
get the ability to be resized and aligned with a layout manager, in our case [jLayout](http://www.bramstein.com/projects/jlayout/).
|
||||
This layout manager applies CSS declarations (mostly dimensions and positioning) via JavaScript,
|
||||
by extracting additional metadata from the markup in the form of HTML5 data attributes.
|
||||
We're using a "border layout" which separates the panels into five areas: north, south, east, west and center (all of which are optional).
|
||||
As layouts can be nested, this allows for some powerful combinations. Our
|
||||
[Howto: Extend the CMS Interface](../howto/extend-cms-interface) has a practical example on
|
||||
how to add a bottom panel to the CMS UI.
|
||||
The various panels and UI components within them are loosely coupled to the layout engine through the `data-layout-type`
|
||||
attribute. The layout is triggered on the top element and cascades into children, with a `redraw` method defined on
|
||||
each panel and UI component that needs to update itself as a result of layouting.
|
||||
|
||||
The various panels and UI components within them are not tightly coupled
|
||||
to the layout engine, so any changes in dimension which impact the overall layout
|
||||
need to be handled manually. In SilverStripe, we've established a convention for a `redraw()`
|
||||
method on each panel and UI component for this purpose, which is usually invoked
|
||||
through its parent container. Invocation order 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.
|
||||
|
||||
![Layout variations](_images/cms-architecture.png)
|
||||
Refer to [Layout reference](../reference/layout) for further information.
|
||||
|
||||
## Forms
|
||||
|
||||
@ -497,4 +483,5 @@ through the `PjaxResponseNegotiator` class (see above).
|
||||
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)
|
||||
* [Howto: Customize the CMS tree](../howto/customize-cms-tree)
|
||||
* [Reference: ModelAdmin](../reference/modeladmin)
|
||||
* [Reference: Layout](../reference/layout)
|
||||
* [Topics: Rich Text Editing](../topics/rich-text-editing)
|
||||
|
115
docs/en/reference/layout.md
Normal file
115
docs/en/reference/layout.md
Normal file
@ -0,0 +1,115 @@
|
||||
# CMS layout
|
||||
|
||||
## Overview
|
||||
|
||||
The CMS markup is structured into "panels", which are the base units containing interface components (or other panels),
|
||||
as declared by the class `cms-panel`. Panels can be made collapsible, and get the ability to be resized and aligned with
|
||||
a layout manager, in our case [jLayout](http://www.bramstein.com/projects/jlayout/). This layout manager applies CSS
|
||||
declarations (mostly dimensions and positioning) via JavaScript.
|
||||
|
||||
We've established a convention for a `redraw` method on each panel and UI component that need to update their content as
|
||||
a result of changes to their position, size or visibility. This method would usually be invoked by the parent container.
|
||||
|
||||
The layout manager does not dynamically track changes to panel sizes - we have to trigger laying out manually each time
|
||||
we need an update to happen (for example from `window::onresize` event, or panel toggling). It then cascades through the
|
||||
children setting sizes and positions, which in turn requires redrawing of some of the elements.
|
||||
|
||||
The easiest way to update the layout of the CMS is to call `redraw` on the top-level `.cms-container` element.
|
||||
|
||||
:::js
|
||||
$('.cms-container').redraw();
|
||||
|
||||
This causes the framework to:
|
||||
|
||||
* reset the _threeColumnCompressor_ algorithm with the current layout options (that can be set via
|
||||
`updateLayoutOptions`)
|
||||
* trigger `layout` which cascades into all children resizing and positioning subordinate elements (this is internal
|
||||
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
|
||||
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.
|
||||
|
||||
Caveat #2: 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.
|
||||
|
||||
![Layout variations](_images/cms-architecture.png)
|
||||
|
||||
## Layout API
|
||||
|
||||
### redraw
|
||||
|
||||
Define `redraw` methods on panels that need to adjust themselves after their sizes, positions or visibility have been
|
||||
changed.
|
||||
|
||||
Call `redraw` on `.cms-container` to re-layout the CMS.
|
||||
|
||||
### data-layout-type attribute
|
||||
|
||||
Layout manager will automatically apply algorithms to the children of `.cms-container` by inspecting the
|
||||
`data-layout-type` attribute. Let's take the content toolbar as an example of a second-level layout application:
|
||||
|
||||
:::html
|
||||
<div class="cms-content-tools west cms-panel cms-panel-layout"
|
||||
data-expandOnClick="true"
|
||||
data-layout-type="border"
|
||||
id="cms-content-tools-CMSMain">
|
||||
<%-- content utilising border's north, south, east, west and center classes --%>
|
||||
</div>
|
||||
|
||||
For detailed discussion on available algorithms refer to
|
||||
[jLayout algorithms](https://github.com/bramstein/jlayout#layout-algorithms).
|
||||
|
||||
Our [Howto: Extend the CMS Interface](../howto/extend-cms-interface) has a practical example on how to add a bottom
|
||||
panel to the CMS UI.
|
||||
|
||||
### Methods
|
||||
|
||||
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:
|
||||
`$('.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.
|
||||
|
||||
### CSS classes
|
||||
|
||||
If as a result of alogorithm's calculations the column becomes hidden, `column-hidden` class is added to it.
|
||||
|
||||
## ThreeColumnCompressor
|
||||
|
||||
You might have noticed that the top-level `.cms-container` has the `data-layout-type` set to `custom`. We use an inhouse
|
||||
_threeColumnCompressor_ algorithm for the layout of the menu, content and preview columns of the CMS. The annotated code
|
||||
for this algorithm can be found in `LeftAndMain.Layout.js`.
|
||||
|
||||
Since the layout-type for the element is set to `custom` and will be ignored by the layout manager, we apply the
|
||||
_threeColumnCompressor_ explicitly `LeftAndMain::redraw`. This way we also get a chance to provide options expected
|
||||
by the algorithm that are initially taken from the `LeftAndMain::LayoutOptions` entwine variable.
|
||||
|
||||
### Factory method
|
||||
|
||||
Use provided factory method to generate algorithm instances.
|
||||
|
||||
:::js
|
||||
jLayout.threeColumnCompressor(<column-spec-object>, <options-object>);
|
||||
|
||||
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
|
||||
|
||||
* _minContentWidth_: minimum size for the content display as long as the preview is visible
|
||||
* _minPreviewWidth_: preview will not be displayed below this size
|
||||
* _mode_: one of "split", "content" (content-only), "preview" (preview-only)
|
||||
|
||||
## Related
|
||||
|
||||
* [Reference: CMS Architecture](../reference/cms-architecture)
|
||||
* [Howto: Extend the CMS Interface](../howto/extend-cms-interface)
|
Loading…
Reference in New Issue
Block a user