mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
#92 MoreLessField: Switch to ToggleField and remove
FEATURE Refactored MoreLessField->ToggleField FEATURE Refactored TogglePanel->ToggleCompositeField FEATURE Degrading gracefully (javascript), using behaviour+classes+prototype, partially i18ned, improved markup git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@43660 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
ba85ce7a0b
commit
35fb0cd0c5
@ -1003,7 +1003,7 @@ class SiteTree extends DataObject {
|
||||
new TextField("MetaTitle", "Title"),
|
||||
new TextareaField("MetaDescription", "Description"),
|
||||
new TextareaField("MetaKeywords", "Keywords"),
|
||||
new TogglePanel("Advanced Options...",array(
|
||||
new ToggleCompositeField("Advanced Options...",array(
|
||||
new TextareaField("ExtraMeta","Custom Meta Tags"),
|
||||
new LiteralField("", "<p>Manually specify a Priority for this page: (valid values are from 0 to 1, a zero will remove this page from the index)</p>"),
|
||||
new NumericField("Priority","Page Priority")),
|
||||
|
@ -626,8 +626,13 @@ class Translatable extends DataObjectDecorator {
|
||||
}
|
||||
else if(($fieldname = $field->Name()) && array_key_exists($fieldname,$this->original_values)) {
|
||||
// Get a copy of the original field to show the untranslated value
|
||||
if (is_subclass_of($field->class,'TextareaField')) $nonEditableField = new MoreLessField($fieldname,$field->Title(),'','+','-');
|
||||
else $nonEditableField = $field->performDisabledTransformation();
|
||||
if(is_subclass_of($field->class,'TextareaField')) {
|
||||
$nonEditableField = new ToggleField($fieldname,$field->Title(),'','+','-');
|
||||
$nonEditableField->labelMore = '+';
|
||||
$nonEditableField->labelLess = '-';
|
||||
} else {
|
||||
$nonEditableField = $field->performDisabledTransformation();
|
||||
}
|
||||
|
||||
$nonEditableField_holder = new CompositeField($nonEditableField);
|
||||
$nonEditableField_holder->setName($fieldname.'_holder');
|
||||
@ -736,3 +741,4 @@ class Translatable extends DataObjectDecorator {
|
||||
}
|
||||
|
||||
}
|
||||
?>
|
@ -1,60 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A field that shows only a part of its contents.
|
||||
* Using the 'more' and 'less' links you can switch to the complete or to the partial text, respectively
|
||||
*/
|
||||
|
||||
class MoreLessField extends ReadonlyField {
|
||||
|
||||
protected $moreText;
|
||||
protected $lessText;
|
||||
protected $charNum;
|
||||
/**
|
||||
* Creates a new More/Less field.
|
||||
* @param name The field name
|
||||
* @param title The field title
|
||||
* @param value The current value
|
||||
* @param moreText Text shown as a link to see the full content of the field
|
||||
* @param lessText Text shown as a link to see the partial view of the field content
|
||||
* @param chars Number of chars to preview. If zero it'll show the first line or sentence.
|
||||
*/
|
||||
function __construct($name, $title = "", $value = "", $moreText = 'more', $lessText = 'less', $chars = 0) {
|
||||
$this->moreText = $moreText;
|
||||
$this->lessText = $lessText;
|
||||
$this->charNum = $chars;
|
||||
parent::__construct($name, $title, $value);
|
||||
}
|
||||
|
||||
function Field() {
|
||||
$valforInput = $this->value ? Convert::raw2att($this->value) : "";
|
||||
$rawInput = Convert::html2raw($valforInput);
|
||||
if ($this->charNum) $reducedVal = substr($rawInput,0,$this->charNum);
|
||||
else $reducedVal = ereg_replace('([^\.]\.)[[:space:]].*','\\1',$rawInput);
|
||||
if (strlen($reducedVal) < strlen($rawInput)) {
|
||||
return <<<HTML
|
||||
<div class="readonly typography" id="{$this->id()}_reduced" style="display: inline;">$reducedVal
|
||||
<a onclick="\$('{$this->id()}_reduced').style.display='none'; \$('{$this->id()}').style.display='inline'; return false;" href="#"> $this->moreText</a>
|
||||
</div>
|
||||
<div class="readonly typography" id="{$this->id()}" style="display: none;">$this->value
|
||||
<a onclick="\$('{$this->id()}').style.display='none'; \$('{$this->id()}_reduced').style.display='inline'; return false;" href="#"> $this->lessText</a>
|
||||
</div>
|
||||
<br /><input type="hidden" name="$this->name" value="$valforInput" />
|
||||
HTML;
|
||||
} else {
|
||||
$this->dontEscape = true;
|
||||
return parent::Field();
|
||||
}
|
||||
}
|
||||
|
||||
function setMoreText($moreText) {
|
||||
$this->moreText = $moreText;
|
||||
}
|
||||
|
||||
function setLessText($lessText) {
|
||||
$this->lessText = $lessText;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
51
forms/ToggleCompositeField.php
Executable file
51
forms/ToggleCompositeField.php
Executable file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
/**
|
||||
* Allows visibility of a group of fields to be toggled using '+' and '-' icons
|
||||
*/
|
||||
class ToggleCompositeField extends CompositeField {
|
||||
|
||||
/**
|
||||
* @var $headingLevel int
|
||||
*/
|
||||
public $headingLevel = 2;
|
||||
|
||||
function __construct($title, $children) {
|
||||
$this->title = $title;
|
||||
$this->name = ereg_replace('[^A-Za-z0-9]','',$this->title);
|
||||
|
||||
$this->startClosed(true);
|
||||
|
||||
parent::__construct($children);
|
||||
}
|
||||
|
||||
public function FieldHolder() {
|
||||
Requirements::javascript("jsparty/prototype.js");
|
||||
Requirements::javascript("jsparty/behaviour.js");
|
||||
Requirements::javascript("jsparty/prototype_improvements.js");
|
||||
Requirements::javascript("sapphire/javascript/ToggleCompositeField.js");
|
||||
|
||||
return $this->renderWith("ToggleCompositeField");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the field should render open or closed by default.
|
||||
*
|
||||
* @param boolean
|
||||
*/
|
||||
public function startClosed($bool) {
|
||||
($bool) ? $this->addExtraClass('startClosed') : $this->removeExtraClass('startClosed');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return String
|
||||
*/
|
||||
public function HeadingLevel() {
|
||||
return $this->headingLevel;
|
||||
}
|
||||
|
||||
public function Type() {
|
||||
return ' toggleCompositeField';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
99
forms/ToggleField.php
Executable file
99
forms/ToggleField.php
Executable file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ReadonlyField with added toggle-capabilities - will preview the first sentence of the contained text-value,
|
||||
* and show the full content by a javascript-switch.
|
||||
*
|
||||
* Caution: Strips HTML-encoding for the preview.
|
||||
*/
|
||||
|
||||
class ToggleField extends ReadonlyField {
|
||||
|
||||
/**
|
||||
* @var $labelMore string Text shown as a link to see the full content of the field
|
||||
*/
|
||||
public $labelMore;
|
||||
|
||||
/**
|
||||
* @var $labelLess string Text shown as a link to see the partial view of the field content
|
||||
*/
|
||||
public $labelLess;
|
||||
|
||||
/**
|
||||
* @var $truncateMethod string (FirstSentence|FirstParagraph) @see {Text}
|
||||
*/
|
||||
public $truncateMethod = 'FirstSentence';
|
||||
|
||||
/**
|
||||
* @var $truncateChars int Number of chars to preview (optional).
|
||||
* Truncating will be applied with $truncateMethod by default.
|
||||
*/
|
||||
public $truncateChars;
|
||||
|
||||
/**
|
||||
* @param name The field name
|
||||
* @param title The field title
|
||||
* @param value The current value
|
||||
*/
|
||||
function __construct($name, $title = "", $value = "") {
|
||||
$this->labelMore = _t('ToggleField.MORE', 'more');
|
||||
$this->labelLess = _t('ToggleField.LESS', 'less');
|
||||
|
||||
$this->startClosed(true);
|
||||
|
||||
parent::__construct($name, $title, $value);
|
||||
}
|
||||
|
||||
function Field() {
|
||||
$content = '';
|
||||
|
||||
Requirements::javascript("jsparty/prototype.js");
|
||||
Requirements::javascript("jsparty/behaviour.js");
|
||||
Requirements::javascript("jsparty/prototype_improvements.js");
|
||||
Requirements::javascript("sapphire/javascript/ToggleField.js");
|
||||
|
||||
if($this->startClosed) $this->addExtraClass('startClosed');
|
||||
|
||||
$valforInput = $this->value ? Convert::raw2att($this->value) : "";
|
||||
$rawInput = Convert::html2raw($valforInput);
|
||||
|
||||
if($this->charNum) $reducedVal = substr($rawInput,0,$this->charNum);
|
||||
else $reducedVal = DBField::create('Text',$rawInput)->{$this->truncateMethod}();
|
||||
|
||||
// only create togglefield if the truncated content is shorter
|
||||
if(strlen($reducedVal) < strlen($rawInput)) {
|
||||
$content = <<<HTML
|
||||
<div class="readonly typography contentLess" style="display: none">
|
||||
$reducedVal
|
||||
<a href="#" class="triggerMore">$this->labelMore</a>
|
||||
</div>
|
||||
<div class="readonly typography contentMore">
|
||||
$this->value
|
||||
<a href="#" class="triggerLess">$this->labelLess</a>
|
||||
</div>
|
||||
<br />
|
||||
<input type="hidden" name="$this->name" value="$valforInput" />
|
||||
HTML;
|
||||
} else {
|
||||
$this->dontEscape = true;
|
||||
$content = parent::Field();
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the field should render open or closed by default.
|
||||
*
|
||||
* @param boolean
|
||||
*/
|
||||
public function startClosed($bool) {
|
||||
($bool) ? $this->addExtraClass('startClosed') : $this->removeExtraClass('startClosed');
|
||||
}
|
||||
|
||||
function Type() {
|
||||
return "toggleField";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Allows visibility of a group of fields to be toggled using + and - icons
|
||||
*/
|
||||
class TogglePanel extends CompositeField {
|
||||
protected $closed = false;
|
||||
|
||||
function __construct($title, $children, $startClosed = false) {
|
||||
$this->title = $title;
|
||||
$this->closed = $startClosed;
|
||||
$this->name = ereg_replace('[^A-Za-z0-9]','',$this->title);
|
||||
parent::__construct($children);
|
||||
}
|
||||
|
||||
public function FieldHolder() {
|
||||
Requirements::javascript("jsparty/prototype.js");
|
||||
Requirements::javascript("jsparty/behaviour.js");
|
||||
Requirements::javascript("jsparty/prototype_improvements.js");
|
||||
Requirements::javascript("sapphire/javascript/TogglePanel.js");
|
||||
|
||||
return $this->renderWith("TogglePanel");
|
||||
}
|
||||
|
||||
public function setClosed($closed) {
|
||||
$this->closed = $closed;
|
||||
}
|
||||
public function getClosed() {
|
||||
return $this->closed;
|
||||
}
|
||||
|
||||
public function ClosedClass() {
|
||||
if($this->closed) return " closed";
|
||||
}
|
||||
public function ClosedStyle() {
|
||||
if($this->closed) return "style=\"display: none\"";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
26
javascript/ToggleCompositeField.js
Executable file
26
javascript/ToggleCompositeField.js
Executable file
@ -0,0 +1,26 @@
|
||||
var ToggleCompositeField = Class.create();
|
||||
ToggleCompositeField.prototype = {
|
||||
initialize: function() {
|
||||
var rules = {};
|
||||
rules['#' + this.id + ' .trigger'] = {
|
||||
onclick: function(e) {
|
||||
this.toggle();
|
||||
Event.stop(e); return false;
|
||||
}.bind(this)
|
||||
};
|
||||
Behaviour.register(rules);
|
||||
|
||||
// close content by default
|
||||
if(Element.hasClassName(this, 'startClosed')) {
|
||||
Element.toggle($$('#' + this.id + ' .contentMore')[0]);
|
||||
}
|
||||
Element.toggle($$('#' + this.id + ' .triggerClosed')[0]);
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
Element.toggle($$('#' + this.id + ' .contentMore')[0]);
|
||||
Element.toggle($$('#' + this.id + ' .triggerClosed')[0]);
|
||||
Element.toggle($$('#' + this.id + ' .triggerOpened')[0]);
|
||||
}
|
||||
}
|
||||
ToggleCompositeField.applyTo('div.toggleCompositeField');
|
29
javascript/ToggleField.js
Normal file
29
javascript/ToggleField.js
Normal file
@ -0,0 +1,29 @@
|
||||
var ToggleField = Class.create();
|
||||
ToggleField.prototype = {
|
||||
initialize: function() {
|
||||
var rules = {};
|
||||
rules['#' + this.id + ' .triggerMore'] = {
|
||||
onclick: function(e) {
|
||||
this.toggle();
|
||||
Event.stop(e); return false;
|
||||
}.bind(this)
|
||||
};
|
||||
rules['#' + this.id + ' .triggerLess'] = {
|
||||
onclick: function(e) {
|
||||
this.toggle();
|
||||
Event.stop(e); return false;
|
||||
}.bind(this)
|
||||
};
|
||||
Behaviour.register(rules);
|
||||
|
||||
if(Element.hasClassName(this, 'startClosed')) {
|
||||
this.toggle();
|
||||
}
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
Element.toggle($$('#' + this.id + ' .contentLess')[0]);
|
||||
Element.toggle($$('#' + this.id + ' .contentMore')[0]);
|
||||
}
|
||||
}
|
||||
ToggleField.applyTo('div.toggleField');
|
@ -1,17 +0,0 @@
|
||||
Behaviour.register({
|
||||
'h2.TogglePanelHeader' : {
|
||||
onclick : function() {
|
||||
var contentDiv = $('panel_' + this.id);
|
||||
var toggleID = this.id.replace('panel_','') + '_toggle';
|
||||
Element.toggle(toggleID + '_closed');
|
||||
Element.toggle(toggleID + '_open');
|
||||
if(contentDiv.style.display == 'none') {
|
||||
contentDiv.style.display = '';
|
||||
Element.removeClassName(this, 'closed');
|
||||
} else {
|
||||
contentDiv.style.display = 'none';
|
||||
Element.addClassName(this, 'closed');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
12
templates/ToggleCompositeField.ss
Executable file
12
templates/ToggleCompositeField.ss
Executable file
@ -0,0 +1,12 @@
|
||||
<div id="$Name" class="$Type $extraClass">
|
||||
<h$HeadingLevel style="cursor: pointer;" class="trigger$ClosedClass">
|
||||
<img class="triggerClosed" src="sapphire/images/toggle-closed.gif" alt="+" style="display:none;" title="Show" />
|
||||
<img class="triggerOpened" src="sapphire/images/toggle-open.gif" alt="-" style="display:none;" title="Hide" />
|
||||
$Title
|
||||
</h$HeadingLevel>
|
||||
<div class="contentMore">
|
||||
<% control FieldSet %>
|
||||
$FieldHolder
|
||||
<% end_control %>
|
||||
</div>
|
||||
</div>
|
@ -1,10 +0,0 @@
|
||||
<h2 id="$id" style="cursor: pointer;" class="TogglePanelHeader$ClosedClass">
|
||||
<img id="{$id}_toggle_closed" src="sapphire/images/toggle-closed.gif" alt="+" title="Show" />
|
||||
<img id="{$id}_toggle_open" src="sapphire/images/toggle-open.gif" alt="-" style="display:none;" title="Hide" />
|
||||
$Title
|
||||
</h2>
|
||||
<div id="panel_$id" $ClosedStyle>
|
||||
<% control FieldSet %>
|
||||
$FieldHolder
|
||||
<% end_control %>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user