#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:
Ingo Schommer 2007-10-21 23:05:46 +00:00
parent ba85ce7a0b
commit 35fb0cd0c5
11 changed files with 228 additions and 133 deletions

View File

@ -1003,7 +1003,7 @@ class SiteTree extends DataObject {
new TextField("MetaTitle", "Title"), new TextField("MetaTitle", "Title"),
new TextareaField("MetaDescription", "Description"), new TextareaField("MetaDescription", "Description"),
new TextareaField("MetaKeywords", "Keywords"), new TextareaField("MetaKeywords", "Keywords"),
new TogglePanel("Advanced Options...",array( new ToggleCompositeField("Advanced Options...",array(
new TextareaField("ExtraMeta","Custom Meta Tags"), 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 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")), new NumericField("Priority","Page Priority")),

View File

@ -619,15 +619,20 @@ class Translatable extends DataObjectDecorator {
$tasks = array( $tasks = array(
'dup' => array(), 'dup' => array(),
); );
foreach ($fields as $field) { foreach($fields as $field) {
if ($field->isComposite()) { if ($field->isComposite()) {
$innertasks = $this->duplicateOrReplaceFields($field->FieldSet()); $innertasks = $this->duplicateOrReplaceFields($field->FieldSet());
$tasks['dup'] = array_merge($tasks['dup'],$innertasks['dup']); $tasks['dup'] = array_merge($tasks['dup'],$innertasks['dup']);
} }
else if (($fieldname = $field->Name()) && array_key_exists($fieldname,$this->original_values)) { else if(($fieldname = $field->Name()) && array_key_exists($fieldname,$this->original_values)) {
// Get a copy of the original field to show the untranslated value // 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(),'','+','-'); if(is_subclass_of($field->class,'TextareaField')) {
else $nonEditableField = $field->performDisabledTransformation(); $nonEditableField = new ToggleField($fieldname,$field->Title(),'','+','-');
$nonEditableField->labelMore = '+';
$nonEditableField->labelLess = '-';
} else {
$nonEditableField = $field->performDisabledTransformation();
}
$nonEditableField_holder = new CompositeField($nonEditableField); $nonEditableField_holder = new CompositeField($nonEditableField);
$nonEditableField_holder->setName($fieldname.'_holder'); $nonEditableField_holder->setName($fieldname.'_holder');
@ -736,3 +741,4 @@ class Translatable extends DataObjectDecorator {
} }
} }
?>

View File

@ -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
View 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
View 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
&nbsp;<a href="#" class="triggerMore">$this->labelMore</a>
</div>
<div class="readonly typography contentMore">
$this->value
&nbsp;<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";
}
}
?>

View File

@ -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\"";
}
}
?>

View 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
View 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');

View File

@ -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');
}
}
}
});

View 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>

View File

@ -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>