Added support for components in partials
This commit is contained in:
parent
a9eecfecd5
commit
9a05a408c9
|
|
@ -13618,6 +13618,71 @@ html.cssanimations .fancy-layout .form-tabless-fields .loading-indicator-contain
|
|||
.inspector-fields td select {
|
||||
width: 90%;
|
||||
}
|
||||
.inspector-fields td div.external-param-editor-container {
|
||||
position: relative;
|
||||
padding-right: 30px;
|
||||
}
|
||||
.inspector-fields td div.external-param-editor-container div.external-editor {
|
||||
bottom: 0;
|
||||
margin: -5px -12px;
|
||||
right: 30px;
|
||||
left: auto;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
-webkit-transition: left 0.2s;
|
||||
transition: left 0.2s;
|
||||
-webkit-transform: translateZ(0);
|
||||
-ms-transform: translateZ(0);
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
}
|
||||
.inspector-fields td div.external-param-editor-container div.external-editor div.controls {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.inspector-fields td div.external-param-editor-container div.external-editor div.controls a {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 30px;
|
||||
color: #2b3e50;
|
||||
outline: none;
|
||||
}
|
||||
.inspector-fields td div.external-param-editor-container div.external-editor div.controls a i {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: 10px;
|
||||
top: 4px;
|
||||
font-size: 15px;
|
||||
}
|
||||
.inspector-fields td div.external-param-editor-container div.external-editor div.controls input {
|
||||
position: absolute;
|
||||
display: block;
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding-left: 30px;
|
||||
padding-right: 12px;
|
||||
background: transparent;
|
||||
}
|
||||
.inspector-fields td div.external-param-editor-container.editor-visible div.external-editor div.controls input {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
.inspector-fields td.active div.external-param-editor-container div.external-editor div.controls input {
|
||||
background: white;
|
||||
}
|
||||
.inspector-fields td.dropdown div.external-param-editor-container div.external-editor {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
bottom: auto;
|
||||
}
|
||||
.inspector-fields th {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
* - placeholder - placholder text, for text and dropdown properties
|
||||
* - depends - a list of properties the property depend on, for dropdown lists
|
||||
* - options - an option list for dropdown lists, optional. If not provided the options are loaded with AJAX.
|
||||
* - noExternalParameter - disables the external parameter feature for the property.
|
||||
* Example of the configuration string (a single property):
|
||||
* [{"property":"max-width","title":"Max width","type":"string"}]
|
||||
*
|
||||
|
|
@ -36,6 +37,10 @@
|
|||
* The inspector uses this field to read and write field values. The field value is a JSON string, an object with keys matching property
|
||||
* names and values matching property values.
|
||||
*
|
||||
* Any HTML element that wraps the inspectable element can have the data-inspector-external-parameters property that enables the external
|
||||
* parameters support. External parameters saved with the special syntax {{paramName}}. The external parameter feature can be disabled
|
||||
* on per-property basis with the noExternalParameter option.
|
||||
*
|
||||
* Events
|
||||
* - change - the event is triggered on the inspectable element when it's properties are updated.
|
||||
* - showing.oc.inspector - triggered before the Inspector is displayed. Allows to cancel the action with e.preventDefault()
|
||||
|
|
@ -96,10 +101,10 @@
|
|||
data-dismiss="popover" \
|
||||
aria-hidden="true">×</button> \
|
||||
</div> \
|
||||
<form> \
|
||||
<form autocomplete="off"> \
|
||||
<table class="inspector-fields {{#tableClass}}{{/tableClass}}"> \
|
||||
{{#properties}} \
|
||||
<tr id="{{#propFormat}}{{property}}{{/propFormat}}" \
|
||||
<tr id="{{#propFormat}}{{property}}{{/propFormat}}" data-property="{{property}}" \
|
||||
{{#dataGroupIndex}}{{/dataGroupIndex}} \
|
||||
class="{{#cellClass}}{{/cellClass}}"> \
|
||||
<th {{#colspan}}{{/colspan}}><div><div><span class="title-element" title="{{title}}"> \
|
||||
|
|
@ -156,6 +161,9 @@
|
|||
if (this.itemType == 'property' && this.groupIndex !== undefined)
|
||||
result += self.groupExpanded(this.group) ? ' expanded' : ' collapsed'
|
||||
|
||||
if (this.itemType == 'property' && this.noExternalParameter)
|
||||
result += ' no-external-parameter'
|
||||
|
||||
return result
|
||||
}
|
||||
},
|
||||
|
|
@ -211,7 +219,7 @@
|
|||
width: 400
|
||||
})
|
||||
|
||||
self.$el.on('hiding.oc.popover', function(e){self.onBeforeHide(e)})
|
||||
self.$el.on('hiding.oc.popover', function(e){return self.onBeforeHide(e)})
|
||||
self.$el.on('hide.oc.popover', function(){self.cleanup()})
|
||||
self.$el.addClass('inspector-open')
|
||||
|
||||
|
|
@ -225,12 +233,15 @@
|
|||
self.editors[0].focus()
|
||||
}
|
||||
|
||||
if (self.$el.closest('[data-inspector-external-parameters]').length > 0)
|
||||
self.initExternalParameterEditor(self.$el.data('oc.popover').$container)
|
||||
|
||||
$.each(self.editors, function(){
|
||||
if (this.init !== undefined)
|
||||
this.init()
|
||||
})
|
||||
|
||||
$('[data-toggle=tooltip]', self.$el.data('oc.popover').$container).tooltip({placement: 'auto right', container: 'body'})
|
||||
$('[data-toggle=tooltip]', self.$el.data('oc.popover').$container).tooltip({placement: 'auto right', container: 'body', delay: 500})
|
||||
|
||||
var $container = self.$el.data('oc.popover').$container
|
||||
$container.on('click', 'tr.group', function(){
|
||||
|
|
@ -248,6 +259,145 @@
|
|||
displayPopover()
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes the external parameter editors for properties that support this feature.
|
||||
*/
|
||||
Inspector.prototype.initExternalParameterEditor = function($container) {
|
||||
var self = this
|
||||
|
||||
$('table.inspector-fields tr', $container).each(function(){
|
||||
if (!$(this).hasClass('no-external-parameter')) {
|
||||
var property = $(this).data('property'),
|
||||
$td = $('td', this),
|
||||
$editorContainer = $('<div class="external-param-editor-container"></div>'),
|
||||
$editor = $(
|
||||
'<div class="external-editor"> \
|
||||
<div class="controls"> \
|
||||
<input type="text" tabindex="-1"/> \
|
||||
<a href="#" tabindex="-1"> \
|
||||
<i class="oc-icon-terminal"></i> \
|
||||
</a> \
|
||||
</div> \
|
||||
</div>')
|
||||
|
||||
$editorContainer.append($td.children())
|
||||
$editorContainer.append($editor)
|
||||
$td.append($editorContainer)
|
||||
|
||||
var $editorLink = $('a', $editor)
|
||||
|
||||
$editorLink.click(function() {
|
||||
return self.toggleExternalParameterEditor($(this))
|
||||
})
|
||||
.attr('title', 'Click to enter the external parameter name to load the property value from')
|
||||
.tooltip({'container': 'body', delay: 500})
|
||||
|
||||
var $input = $editor.find('input'),
|
||||
propertyValue = self.propertyValues[property]
|
||||
|
||||
$input.on('focus', function(){
|
||||
var $field = $(this)
|
||||
|
||||
$('td', $field.closest('table')).removeClass('active')
|
||||
$field.closest('td').addClass('active')
|
||||
})
|
||||
|
||||
$input.on('change', function(){
|
||||
self.markPropertyChanged(property, true)
|
||||
})
|
||||
|
||||
var matches = []
|
||||
if (matches = propertyValue.match(/^\{\{([^\}]+)\}\}$/)) {
|
||||
var value = $.trim(matches[1])
|
||||
|
||||
if (value.length > 0) {
|
||||
self.showExternalParameterEditor($editorContainer, $editor, $editorLink, $td, true)
|
||||
$editor.find('input').val(value)
|
||||
self.writeProperty(property, null, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Inspector.prototype.showExternalParameterEditor = function($container, $editor, $editorLink, $cell, noAnimation) {
|
||||
var position = $editor.position()
|
||||
$('input', $editor).focus()
|
||||
|
||||
if (!noAnimation) {
|
||||
$editor.css({
|
||||
'left': position.left + 'px',
|
||||
'right': 0
|
||||
})
|
||||
} else
|
||||
$editor.css('right', 0)
|
||||
|
||||
setTimeout(function(){
|
||||
$editor.css('left', 0)
|
||||
$cell.scrollTop(0)
|
||||
}, 0)
|
||||
|
||||
$container.addClass('editor-visible')
|
||||
$editorLink.attr('data-original-title', 'Click to enter the property value')
|
||||
this.toggleCellEditorVisibility($cell, false)
|
||||
$editor.find('input').attr('tabindex', 0)
|
||||
}
|
||||
|
||||
Inspector.prototype.toggleExternalParameterEditor = function($editorLink) {
|
||||
var $container = $editorLink.closest('.external-param-editor-container'),
|
||||
$editor = $('.external-editor', $container),
|
||||
$cell = $editorLink.closest('td'),
|
||||
self = this
|
||||
|
||||
$editorLink.tooltip('hide')
|
||||
|
||||
if (!$container.hasClass('editor-visible')) {
|
||||
self.showExternalParameterEditor($container, $editor, $editorLink, $cell)
|
||||
} else {
|
||||
var left = $container.width()
|
||||
|
||||
$editor.css('left', left + 'px')
|
||||
setTimeout(function(){
|
||||
$editor.css({
|
||||
'left': 'auto',
|
||||
'right': '30px'
|
||||
})
|
||||
$container.removeClass('editor-visible')
|
||||
$container.closest('td').removeClass('active')
|
||||
}, 200)
|
||||
$editorLink.attr('data-original-title', 'Click to enter the external parameter name to load the property value from')
|
||||
$editor.find('input').attr('tabindex', -1)
|
||||
self.toggleCellEditorVisibility($cell, true)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
Inspector.prototype.toggleCellEditorVisibility = function($cell, show) {
|
||||
var $container = $('.external-param-editor-container', $cell)
|
||||
|
||||
$container.children().each(function(){
|
||||
var $el = $(this)
|
||||
|
||||
if ($el.hasClass('external-editor'))
|
||||
return
|
||||
|
||||
if (show)
|
||||
$el.removeClass('hide')
|
||||
else {
|
||||
var height = $cell.data('inspector-cell-height')
|
||||
|
||||
if (!height) {
|
||||
height = $cell.height()
|
||||
$cell.data('inspector-cell-height', height)
|
||||
}
|
||||
|
||||
$container.css('height', height + 'px')
|
||||
$el.addClass('hide')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates group nodes in the property set
|
||||
*/
|
||||
|
|
@ -379,17 +529,22 @@
|
|||
return null
|
||||
}
|
||||
|
||||
Inspector.prototype.writeProperty = function(property, value) {
|
||||
Inspector.prototype.writeProperty = function(property, value, noChangedStatusUpdate) {
|
||||
this.propertyValues[property] = value
|
||||
this.propertyValuesField.val(JSON.stringify(this.propertyValues))
|
||||
|
||||
if (this.originalPropertyValues[property] === undefined || this.originalPropertyValues[property] != value) {
|
||||
this.$el.trigger('change')
|
||||
this.markPropertyChanged(property, true)
|
||||
} else
|
||||
this.markPropertyChanged(property, false)
|
||||
if (!noChangedStatusUpdate) {
|
||||
this.$el.trigger('change')
|
||||
this.markPropertyChanged(property, true)
|
||||
}
|
||||
} else {
|
||||
if (!noChangedStatusUpdate)
|
||||
this.markPropertyChanged(property, false)
|
||||
}
|
||||
|
||||
this.$el.trigger('propertyChanged.oc.Inspector', [property])
|
||||
if (!noChangedStatusUpdate)
|
||||
this.$el.trigger('propertyChanged.oc.Inspector', [property])
|
||||
|
||||
return value
|
||||
}
|
||||
|
|
@ -412,6 +567,7 @@
|
|||
|
||||
var editor = new $.oc.inspector.editors[editorClass](editorId, this, data)
|
||||
this.editors.push(editor)
|
||||
editor.inspectorCellId = editorId
|
||||
|
||||
return editor.renderEditor()
|
||||
}
|
||||
|
|
@ -429,11 +585,40 @@
|
|||
}
|
||||
|
||||
Inspector.prototype.onBeforeHide = function(e) {
|
||||
var $container = this.$el.data('inspector-container'),
|
||||
externalParamErrorFound = false,
|
||||
self = this
|
||||
|
||||
$.each(this.editors, function() {
|
||||
this.applyValue()
|
||||
if (!self.editorExternalPropertyEnabled(this))
|
||||
this.applyValue()
|
||||
else {
|
||||
var $cell = $('#'+this.inspectorCellId, $container),
|
||||
$extPropEditorContainer = $cell.find('.external-param-editor-container'),
|
||||
$input = $extPropEditorContainer.find('.external-editor input'),
|
||||
val = $.trim($input.val())
|
||||
|
||||
if (val.length == 0) {
|
||||
alert('Please enter external parameter name for the '+this.fieldDef.title+' property.')
|
||||
externalParamErrorFound = true
|
||||
setTimeout(function(){
|
||||
$input.focus()
|
||||
}, 0)
|
||||
return false
|
||||
}
|
||||
|
||||
self.writeProperty(this.fieldDef.property, '{{ '+val+' }}')
|
||||
}
|
||||
})
|
||||
|
||||
var eH = $.Event('hiding.oc.inspector')
|
||||
if (externalParamErrorFound) {
|
||||
e.preventDefault()
|
||||
return false
|
||||
}
|
||||
|
||||
var eH = $.Event('hiding.oc.inspector'),
|
||||
ispector = this
|
||||
|
||||
this.$el.trigger(eH, [{values: this.propertyValues}])
|
||||
if (eH.isDefaultPrevented()) {
|
||||
e.preventDefault()
|
||||
|
|
@ -441,6 +626,9 @@
|
|||
}
|
||||
|
||||
$.each(this.editors, function() {
|
||||
if (ispector.editorExternalPropertyEnabled(this))
|
||||
return true
|
||||
|
||||
if (this.validate === undefined)
|
||||
return true
|
||||
|
||||
|
|
@ -458,10 +646,17 @@
|
|||
return false
|
||||
})
|
||||
|
||||
var $contianer = this.$el.data('inspector-container')
|
||||
$('[data-toggle=tooltip]', this.$el.data('oc.popover').$container).tooltip('hide')
|
||||
}
|
||||
|
||||
Inspector.prototype.editorExternalPropertyEnabled = function(editor) {
|
||||
var $container = this.$el.data('inspector-container'),
|
||||
$cell = $('#'+editor.inspectorCellId, $container),
|
||||
$extPropEditorContainer = $cell.find('.external-param-editor-container')
|
||||
|
||||
return $extPropEditorContainer.hasClass('editor-visible')
|
||||
}
|
||||
|
||||
//
|
||||
// EDITOR DEFINITIONS
|
||||
// ==================
|
||||
|
|
@ -474,6 +669,7 @@
|
|||
* - validate(), optional, validates the value and returns the validation error message
|
||||
* - applyValue(), applies the editor value
|
||||
* - focus(), focuses the editor input element, if applicable
|
||||
* - init(), sets the initial editor value
|
||||
*/
|
||||
|
||||
// STRING EDITOR
|
||||
|
|
@ -483,7 +679,7 @@
|
|||
this.inspector = inspector
|
||||
this.fieldDef = fieldDef
|
||||
this.editorId = editorId
|
||||
this.selector = '#'+this.editorId+' input'
|
||||
this.selector = '#'+this.editorId+' input.string-editor'
|
||||
|
||||
var self = this
|
||||
$(document).on('focus', this.selector, function() {
|
||||
|
|
@ -498,6 +694,11 @@
|
|||
})
|
||||
}
|
||||
|
||||
InspectorEditorString.prototype.init = function() {
|
||||
var value = $.trim(this.inspector.readProperty(this.fieldDef.property))
|
||||
$(this.selector).val(value)
|
||||
}
|
||||
|
||||
InspectorEditorString.prototype.applyValue = function() {
|
||||
this.inspector.writeProperty(this.fieldDef.property, $.trim($(this.selector).val()))
|
||||
}
|
||||
|
|
@ -505,11 +706,10 @@
|
|||
InspectorEditorString.prototype.renderEditor = function() {
|
||||
var data = {
|
||||
id: this.editorId,
|
||||
value: $.trim(this.inspector.readProperty(this.fieldDef.property)),
|
||||
placeholder: this.fieldDef.placeholder !== undefined ? this.fieldDef.placeholder : ''
|
||||
}
|
||||
|
||||
return Mustache.render('<td class="text" id="{{id}}"><input type="text" value="{{value}}" placeholder="{{placeholder}}"/></td>', data)
|
||||
return Mustache.render('<td class="text" id="{{id}}"><input type="text" class="string-editor" placeholder="{{placeholder}}"/></td>', data)
|
||||
}
|
||||
|
||||
InspectorEditorString.prototype.validate = function() {
|
||||
|
|
@ -525,6 +725,7 @@
|
|||
|
||||
InspectorEditorString.prototype.focus = function() {
|
||||
$(this.selector).focus()
|
||||
$(this.selector).closest('td').scrollLeft(0)
|
||||
}
|
||||
|
||||
$.oc.inspector.editors.inspectorEditorString = InspectorEditorString;
|
||||
|
|
@ -550,14 +751,8 @@
|
|||
}
|
||||
|
||||
InspectorEditorCheckbox.prototype.renderEditor = function() {
|
||||
var isChecked = this.inspector.readProperty(this.fieldDef.property)
|
||||
|
||||
if (isChecked == '0' || isChecked == 'false')
|
||||
isChecked = false
|
||||
|
||||
var data = {
|
||||
id: this.editorId,
|
||||
checked: isChecked ? 'checked' : null,
|
||||
cbId: this.editorId + '-cb',
|
||||
title: this.fieldDef.title
|
||||
}
|
||||
|
|
@ -565,6 +760,15 @@
|
|||
return Mustache.render(this.getTemplate(), data)
|
||||
}
|
||||
|
||||
InspectorEditorCheckbox.prototype.init = function() {
|
||||
var isChecked = this.inspector.readProperty(this.fieldDef.property)
|
||||
|
||||
if (isChecked == '0' || isChecked == 'false')
|
||||
isChecked = false
|
||||
|
||||
$(this.selector).prop(checked, isChecked)
|
||||
}
|
||||
|
||||
InspectorEditorCheckbox.prototype.focus = function() {
|
||||
$(this.selector).closest('div').focus()
|
||||
}
|
||||
|
|
@ -576,7 +780,7 @@
|
|||
custom-checkbox nolabel"> \
|
||||
<input type="checkbox" \
|
||||
value="1" \
|
||||
{{checked}} id="{{cbId}}"/> \
|
||||
id="{{cbId}}"/> \
|
||||
<label for="{{cbId}}">{{title}}</label> \
|
||||
</div> \
|
||||
</td> \
|
||||
|
|
@ -594,6 +798,7 @@
|
|||
this.editorId = editorId
|
||||
this.selector = '#'+this.editorId+' select'
|
||||
this.dynamicOptions = this.fieldDef.options ? false : true
|
||||
this.initialization = false
|
||||
|
||||
var self = this
|
||||
|
||||
|
|
@ -603,7 +808,7 @@
|
|||
}
|
||||
|
||||
InspectorEditorDropdown.prototype.applyValue = function() {
|
||||
this.inspector.writeProperty(this.fieldDef.property, $(this.selector).val())
|
||||
this.inspector.writeProperty(this.fieldDef.property, $(this.selector).val(), this.initialization)
|
||||
}
|
||||
|
||||
InspectorEditorDropdown.prototype.renderEditor = function() {
|
||||
|
|
@ -678,7 +883,7 @@
|
|||
this.indicatorContainer.addClass('loading-indicator-container').addClass('size-small').addClass('transparent')
|
||||
}
|
||||
|
||||
this.loadOptions()
|
||||
this.loadOptions(true)
|
||||
}
|
||||
|
||||
if (this.fieldDef.depends)
|
||||
|
|
@ -721,7 +926,7 @@
|
|||
this.indicatorContainer.loadIndicator('hide')
|
||||
}
|
||||
|
||||
InspectorEditorDropdown.prototype.loadOptions = function() {
|
||||
InspectorEditorDropdown.prototype.loadOptions = function(initialization) {
|
||||
var $form = $(this.selector).closest('form'),
|
||||
data = this.inspector.propertyValues,
|
||||
$select = $(this.selector),
|
||||
|
|
@ -755,7 +960,9 @@
|
|||
else
|
||||
$('option:first-child', $select).attr("selected", "selected");
|
||||
|
||||
self.initialization = initialization
|
||||
$select.trigger('change')
|
||||
self.initialization = false
|
||||
|
||||
self.hideLoadingIndicator()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -90,6 +90,92 @@
|
|||
select {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
div.external-param-editor-container {
|
||||
position: relative;
|
||||
padding-right: 30px;
|
||||
|
||||
div.external-editor {
|
||||
bottom: 0;
|
||||
margin: -5px -12px;
|
||||
right: 30px;
|
||||
left: auto;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
|
||||
.transition(left 0.2s);
|
||||
.transform( ~'translateZ(0)');
|
||||
will-change: transform;
|
||||
|
||||
div.controls {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
||||
a {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 30px;
|
||||
color: #2b3e50;
|
||||
outline: none;
|
||||
|
||||
i {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: 10px;
|
||||
top: 4px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
position: absolute;
|
||||
display: block;
|
||||
border: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding-left: 30px;
|
||||
padding-right: 12px;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.editor-visible {
|
||||
div.external-editor {
|
||||
div.controls {
|
||||
input {
|
||||
background: @color-inspector-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
div.external-param-editor-container {
|
||||
div.external-editor {
|
||||
div.controls {
|
||||
input {
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.dropdown div.external-param-editor-container div.external-editor {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
bottom: auto;
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
|
|
|
|||
|
|
@ -580,7 +580,7 @@
|
|||
|
||||
var $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout')
|
||||
if ($componentList.length == 0) {
|
||||
alert('Components can be added only to pages and layouts.')
|
||||
alert('Components can be added only to pages, partials and layouts.')
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -183,36 +183,4 @@ abstract class ComponentBase extends Extendable
|
|||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a page link to another page. Allows mapping to the other page's
|
||||
* component properties for the purpose of extracting URL routing parameters.
|
||||
* @param string $page Page name or page file name
|
||||
* @param string $class Component class name
|
||||
* @param array $mappings ['componentProperty' => 'routed value']
|
||||
* @return string
|
||||
*/
|
||||
// protected function makePageLink($page, $class, $mappings = [])
|
||||
// {
|
||||
// if (!isset($this->pageLinkCache[$page.$class])) {
|
||||
// $this->pageLinkCache[$page.$class] = $this->getOtherPageComponent($page, $class);
|
||||
// }
|
||||
|
||||
// if (!$component = $this->pageLinkCache[$page.$class])
|
||||
// return null;
|
||||
|
||||
// $params = [];
|
||||
// foreach ($mappings as $property => $value) {
|
||||
|
||||
// if (!$param = $component->property($property))
|
||||
// continue;
|
||||
|
||||
// if (substr($param, 0, 1) == ':')
|
||||
// $param = substr($param, 1);
|
||||
|
||||
// $params[$param] = $value;
|
||||
// }
|
||||
|
||||
// return $this->pageUrl($page, $params);
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,12 +20,13 @@ class ComponentHelpers
|
|||
$result = [];
|
||||
|
||||
$property = [
|
||||
'property' => 'oc.alias',
|
||||
'title' => Lang::get('cms::lang.component.alias'),
|
||||
'description' => Lang::get('cms::lang.component.alias_description'),
|
||||
'type' => 'string',
|
||||
'validationPattern' => '^[a-zA-Z]+[0-9a-z\_]*$',
|
||||
'validationMessage' => Lang::get('cms::lang.component.validation_message')
|
||||
'property' => 'oc.alias',
|
||||
'title' => Lang::get('cms::lang.component.alias'),
|
||||
'description' => Lang::get('cms::lang.component.alias_description'),
|
||||
'type' => 'string',
|
||||
'validationPattern' => '^[a-zA-Z]+[0-9a-z\_]*$',
|
||||
'validationMessage' => Lang::get('cms::lang.component.validation_message'),
|
||||
'noExternalParameter' => true
|
||||
];
|
||||
$result[] = $property;
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,8 @@ class Controller extends BaseController
|
|||
|
||||
protected static $instance = null;
|
||||
|
||||
protected $partialComponentStack = [];
|
||||
|
||||
/**
|
||||
* Creates the controller.
|
||||
* @param \Cms\Classes\Theme $theme Specifies the CMS theme.
|
||||
|
|
@ -391,6 +393,8 @@ class Controller extends BaseController
|
|||
{
|
||||
$manager = ComponentManager::instance();
|
||||
|
||||
$properties = $this->setComponentPropertiesFromParameters($properties, []);
|
||||
|
||||
if ($addToLayout) {
|
||||
if (!$componentObj = $manager->makeComponent($name, $this->layoutObj, $properties)) {
|
||||
throw new CmsException(Lang::get('cms::lang.component.not_found', ['name'=>$name]));
|
||||
|
|
@ -664,6 +668,8 @@ class Controller extends BaseController
|
|||
*/
|
||||
public function renderPartial($name, $parameters = [], $throwException = true)
|
||||
{
|
||||
$vars = $this->vars;
|
||||
|
||||
/*
|
||||
* Alias @ symbol for ::
|
||||
*/
|
||||
|
|
@ -748,12 +754,63 @@ class Controller extends BaseController
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Run partial functions (only for real partials)
|
||||
*/
|
||||
|
||||
if ($partial instanceof \Cms\Classes\Partial) {
|
||||
$manager = ComponentManager::instance();
|
||||
|
||||
foreach ($partial->settings['components'] as $component => $properties) {
|
||||
list($name, $alias) = strpos($component, ' ') ?
|
||||
explode(' ', $component) : array($component, $component);
|
||||
|
||||
$properties = $this->setComponentPropertiesFromParameters($properties, $parameters, []);
|
||||
|
||||
if (!$componentObj = $manager->makeComponent($name, $this->pageObj, $properties))
|
||||
throw new CmsException(Lang::get('cms::lang.component.not_found', ['name'=>$name]));
|
||||
|
||||
$componentObj->alias = $alias;
|
||||
$parameters[$alias] = $partial->components[$alias] = $componentObj;
|
||||
|
||||
array_push($this->partialComponentStack, [
|
||||
'name' => $alias,
|
||||
'obj' => $componentObj
|
||||
]);
|
||||
|
||||
$componentObj->init();
|
||||
$componentObj->onInit(); // Deprecated: Remove ithis line if year >= 2015
|
||||
}
|
||||
|
||||
CmsException::mask($this->page, 300);
|
||||
$parser = new CodeParser($partial);
|
||||
$partialObj = $parser->source($this->page, $this->layout, $this);
|
||||
CmsException::unmask();
|
||||
|
||||
CmsException::mask($partial, 300);
|
||||
$partialObj->onStart();
|
||||
$partial->runComponents();
|
||||
$partialObj->onEnd();
|
||||
CmsException::unmask();
|
||||
}
|
||||
|
||||
/*
|
||||
* Render the parital
|
||||
*/
|
||||
CmsException::mask($partial, 400);
|
||||
$this->loader->setObject($partial);
|
||||
$template = $this->twig->loadTemplate($partial->getFullPath());
|
||||
$result = $template->render(array_merge($this->vars, $parameters));
|
||||
CmsException::unmask();
|
||||
|
||||
$result = $template->render(array_merge($this->vars, $parameters));
|
||||
CmsException::unmask($this->partialComponentStack);
|
||||
|
||||
if ($partial instanceof \Cms\Classes\Partial) {
|
||||
if ($this->partialComponentStack) {
|
||||
array_pop($this->partialComponentStack);
|
||||
}
|
||||
}
|
||||
|
||||
$this->vars = $vars;
|
||||
$this->componentContext = null;
|
||||
return $result;
|
||||
}
|
||||
|
|
@ -961,6 +1018,11 @@ class Controller extends BaseController
|
|||
return $this->layout->components[$name];
|
||||
}
|
||||
|
||||
foreach ($this->partialComponentStack as $componentInfo) {
|
||||
if ($componentInfo['name'] == $name)
|
||||
return $componentInfo['obj'];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -1017,50 +1079,32 @@ class Controller extends BaseController
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a basic component object for another page, useful for extracting properties.
|
||||
* @param string $page Page name or page file name
|
||||
* @param string $class Component class name
|
||||
* @return ComponentBase
|
||||
* Sets component property values from partial parameters.
|
||||
* The property values should be defined as {{ param }}.
|
||||
* @param array &$properties Specifies the component properties loaded from the partial.
|
||||
* @param array $parameters Specifies the partial parameters.
|
||||
* @return Returns updated properties.
|
||||
*/
|
||||
// public function getOtherPageComponent($page, $class)
|
||||
// {
|
||||
// $class = Str::normalizeClassName($class);
|
||||
// $theme = $this->getTheme();
|
||||
// $manager = ComponentManager::instance();
|
||||
// $componentObj = new $class;
|
||||
protected function setComponentPropertiesFromParameters(&$properties, $parameters)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
// if (($page = Page::loadCached($theme, $page)) && isset($page->settings['components'])) {
|
||||
// foreach ($page->settings['components'] as $component => $properties) {
|
||||
// list($name, $alias) = strpos($component, ' ') ?
|
||||
// explode(' ', $component) :
|
||||
// array($component, $component)
|
||||
// ;
|
||||
// if ($manager->resolve($name) == $class) {
|
||||
// $componentObj->setProperties($properties);
|
||||
// $componentObj->alias = $alias;
|
||||
// return $componentObj;
|
||||
// }
|
||||
// }
|
||||
$routerParameters = $this->router->getParameters();
|
||||
|
||||
// if (!isset($page->settings['layout']))
|
||||
// return null;
|
||||
foreach ($properties as $propertyName=>$propertyValue) {
|
||||
$matches = [];
|
||||
if (preg_match('/^\{\{([^\}]+)\}\}$/', $propertyValue, $matches)) {
|
||||
$paramName = trim($matches[1]);
|
||||
|
||||
// $layout = $page->settings['layout'];
|
||||
// if (($layout = Layout::loadCached($theme, $layout)) && isset($layout->settings['components'])) {
|
||||
// foreach ($layout->settings['components'] as $component => $properties) {
|
||||
// list($name, $alias) = strpos($component, ' ') ?
|
||||
// explode(' ', $component) :
|
||||
// array($component, $component)
|
||||
// ;
|
||||
// if ($manager->resolve($name) == $class) {
|
||||
// $componentObj->setProperties($properties);
|
||||
// $componentObj->alias = $alias;
|
||||
// return $componentObj;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
if ( substr($paramName, 0, 1) == ':' ) {
|
||||
$paramName = substr($paramName, 1);
|
||||
$result[$propertyName] = array_key_exists($paramName, $routerParameters) ? $routerParameters[$paramName] : null;
|
||||
} else
|
||||
$result[$propertyName] = array_key_exists($paramName, $parameters) ? $parameters[$paramName] : null;
|
||||
} else
|
||||
$result[$propertyName] = $propertyValue;
|
||||
}
|
||||
|
||||
// return null;
|
||||
// }
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,4 +17,13 @@ class Partial extends CmsCompoundObject
|
|||
{
|
||||
return 'partials';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of a PHP class to us a parent for the PHP class created for the object's PHP section.
|
||||
* @return mixed Returns the class name or null.
|
||||
*/
|
||||
public function getCodeClassParent()
|
||||
{
|
||||
return '\Cms\Classes\PartialCode';
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
<?php namespace Cms\Classes;
|
||||
|
||||
/**
|
||||
* Parent class for PHP classes created for partial PHP sections.
|
||||
*
|
||||
* @package october\cms
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class PartialCode extends CodeBase
|
||||
{
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ fields:
|
|||
path: partial_toolbar
|
||||
cssClass: collapse-visible
|
||||
|
||||
components: Cms\FormWidgets\Components
|
||||
|
||||
secondaryTabs:
|
||||
stretch: true
|
||||
fields:
|
||||
|
|
@ -21,4 +23,10 @@ secondaryTabs:
|
|||
tab: cms::lang.editor.markup
|
||||
stretch: true
|
||||
type: codeeditor
|
||||
language: twig
|
||||
language: twig
|
||||
|
||||
code:
|
||||
tab: cms::lang.editor.code
|
||||
stretch: true
|
||||
type: codeeditor
|
||||
language: php
|
||||
|
|
|
|||
|
|
@ -90,6 +90,8 @@ class Index extends Controller
|
|||
$this->addJs('/modules/cms/assets/js/october.cmspage.js', 'core');
|
||||
$this->addJs('/modules/cms/assets/js/october.dragcomponents.js', 'core');
|
||||
$this->addJs('/modules/cms/assets/js/october.tokenexpander.js', 'core');
|
||||
$this->addJs('/modules/backend/formwidgets/codeeditor/assets/js/codeeditor.js', 'core');
|
||||
|
||||
$this->addCss('/modules/cms/assets/css/october.components.css', 'core');
|
||||
|
||||
// Preload Ace editor modes explicitly, because they could be changed dynamically
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
<?= Form::open([
|
||||
'class' => 'layout',
|
||||
'data-change-monitor' => 'true',
|
||||
'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close'))
|
||||
]) ?>
|
||||
<?php
|
||||
$formConfig = [
|
||||
'class' => 'layout',
|
||||
'data-change-monitor' => 'true',
|
||||
'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close')),
|
||||
'data-inspector-external-parameters' => true
|
||||
];
|
||||
|
||||
echo Form::open($formConfig) ?>
|
||||
<?= $form->render() ?>
|
||||
|
||||
<input type="hidden" value="<?= ($templateType) ?>" name="templateType" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue