mirror of
https://github.com/processwire/processwire.git
synced 2025-08-18 20:41:16 +02:00
Add option to repeaters enabling you to specify whether repeater item controls (delete, insert, clone, etc.) should be always visible or visible only when the header is hovered. Updated the default to be visible when only hovered as this reduces clutter. This commit also fixes an issue with custom header background colors (in matrix) not supporting matrix item type names that started with a number (i.e. "1-column-text"). This commit also fixes processwire/processwire-issues#1472 where the usual left/right outlines weren't showing for opened repeater items.
This commit is contained in:
@@ -33,7 +33,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
||||
return array(
|
||||
'title' => __('Repeater', __FILE__), // Module Title
|
||||
'summary' => __('Maintains a collection of fields that are repeated for any number of times.', __FILE__), // Module Summary
|
||||
'version' => 110,
|
||||
'version' => 111,
|
||||
'autoload' => true,
|
||||
'installs' => 'InputfieldRepeater'
|
||||
);
|
||||
|
File diff suppressed because one or more lines are too long
@@ -934,9 +934,11 @@ function InputfieldRepeater($) {
|
||||
$item.attr('data-depth', depth);
|
||||
|
||||
if(depth > 0) {
|
||||
$item.css('margin-left', (depth * depthSize) + 'px');
|
||||
$item.css('padding-left', (depth * depthSize) + 'px');
|
||||
$item.addClass('InputfieldRepeaterItemHasDepth');
|
||||
} else {
|
||||
$item.css('margin-left', 0);
|
||||
$item.css('padding-left', 0);
|
||||
$item.removeClass('InputfieldRepeaterItemHasDepth');
|
||||
}
|
||||
|
||||
return depth;
|
||||
@@ -1044,12 +1046,17 @@ function InputfieldRepeater($) {
|
||||
var $depth = $wrap.find('input');
|
||||
var depth = $depth.val();
|
||||
var $item = $depth.closest('.InputfieldRepeaterItem');
|
||||
var currentLeft = $item.css('margin-left');
|
||||
var currentLeft = $item.css('padding-left');
|
||||
if(currentLeft == 'auto') currentLeft = 0;
|
||||
currentLeft = parseInt(currentLeft);
|
||||
var targetLeft = depth * depthSize;
|
||||
if(targetLeft != currentLeft) {
|
||||
$item.css('margin-left', targetLeft + 'px');
|
||||
$item.css('padding-left', targetLeft + 'px');
|
||||
}
|
||||
if(targetLeft > 0) {
|
||||
$item.addClass('InputfieldRepeaterItemHasDepth');
|
||||
} else {
|
||||
$item.removeClass('InputfieldRepeaterItemHasDepth');
|
||||
}
|
||||
});
|
||||
$inputfieldRepeater.children('.InputfieldContent').css('position', 'relative');
|
||||
@@ -1149,10 +1156,10 @@ function InputfieldRepeater($) {
|
||||
var $header = ui.item.children('.InputfieldHeader');
|
||||
if(depth > maxDepth) {
|
||||
// beyond max depth allowed
|
||||
$header.addClass('ui-state-error');
|
||||
$header.addClass('ui-state-error InputfieldRepeaterItemOOB'); // OOB: Out Of Bounds
|
||||
} else if($header.hasClass('ui-state-error')) {
|
||||
// no problems
|
||||
$header.removeClass('ui-state-error');
|
||||
$header.removeClass('ui-state-error InputfieldRepeaterItemOOB');
|
||||
}
|
||||
};
|
||||
} else {
|
||||
@@ -1266,6 +1273,7 @@ function InputfieldRepeater($) {
|
||||
}
|
||||
|
||||
if($inputfields.hasClass('InputfieldRepeaterInit')) return;
|
||||
if($('body').hasClass('touch-device')) $inputfieldRepeater.addClass('InputfieldRepeaterLoudControls');
|
||||
|
||||
var renderValueMode = $inputfields.closest('.InputfieldRenderValueMode').length > 0;
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@@ -12,8 +12,9 @@
|
||||
* @property int $repeaterMinItems
|
||||
* @property int $repeaterDepth
|
||||
* @property bool|int $familyFriendly
|
||||
* @property bool $accordionMode
|
||||
* @property bool $singleMode
|
||||
* @property bool|int $accordionMode
|
||||
* @property bool|int $singleMode
|
||||
* @property bool|int $loudControls Always show controls regardless of hover?
|
||||
*
|
||||
* @method string renderRepeaterLabel($label, $cnt, Page $page)
|
||||
*
|
||||
@@ -26,7 +27,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
||||
return array(
|
||||
'title' => __('Repeater', __FILE__), // Module Title
|
||||
'summary' => __('Repeats fields from another template. Provides the input for FieldtypeRepeater.', __FILE__), // Module Summary
|
||||
'version' => 110,
|
||||
'version' => 111,
|
||||
'requires' => 'FieldtypeRepeater',
|
||||
);
|
||||
}
|
||||
@@ -104,6 +105,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
||||
$this->set('familyFriendly', 0);
|
||||
$this->set('accordionMode', false);
|
||||
$this->set('singleMode', false);
|
||||
$this->set('loudControls', false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -479,10 +481,10 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
||||
// background-color definition
|
||||
if(strlen($matches[1]) === 3 || strlen($matches[1]) === 6) {
|
||||
$wrapLabel = str_replace($matches[0], '', $wrapLabel);
|
||||
$typeStyles[] =
|
||||
$typeStyles[$itemTypeName] =
|
||||
".Inputfield_$this->name .InputfieldContent " .
|
||||
".InputfieldRepeaterItem[data-typeName=$itemTypeName] > .InputfieldHeader " .
|
||||
"{ background-color: #$matches[1] }";
|
||||
".InputfieldRepeaterItem[data-typeName=\"$itemTypeName\"] > .InputfieldHeader " .
|
||||
"{ background-color: #$matches[1]; outline-color: #$matches[1] }";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -773,6 +775,9 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
||||
if($this->accordionMode) {
|
||||
$this->addClass('InputfieldRepeaterAccordion', 'wrapClass');
|
||||
}
|
||||
if($this->loudControls || $this->wire()->session->get('touch')) {
|
||||
$this->addClass('InputfieldRepeaterLoudControls', 'wrapClass');
|
||||
}
|
||||
if(!empty($_COOKIE[$this->copyPasteCookieName()])) {
|
||||
$this->addClass('InputfieldRepeaterCanPaste', 'wrapClass');
|
||||
}
|
||||
|
@@ -16,10 +16,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
// prevents longer item headers from going outside Inputfield box horizontally
|
||||
.InputfieldRepeaterItem {
|
||||
max-width: 100%;
|
||||
overflow-x: hidden;
|
||||
.InputfieldRepeaterItemHasDepth {
|
||||
// avoids white left background where indent is
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.InputfieldRepeaterItem > .InputfieldHeader {
|
||||
@@ -41,6 +40,7 @@
|
||||
}
|
||||
.InputfieldRepeaterItemControls {
|
||||
display: block;
|
||||
opacity: 1.0;
|
||||
padding-right: 0.5em;
|
||||
padding-left: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
@@ -48,10 +48,8 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
height: 100%;
|
||||
|
||||
|
||||
.InputfieldRepeaterSettingsToggle,
|
||||
.InputfieldRepeaterClone,
|
||||
@@ -88,15 +86,54 @@
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&:hover .InputfieldRepeaterItemControls {
|
||||
opacity: 1.0;
|
||||
transition: opacity 300ms;
|
||||
}
|
||||
|
||||
&.InputfieldRepeaterItemOOB .InputfieldRepeaterItemControls {
|
||||
// items out of bounds (OOB) hide controls
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toggle-icon {
|
||||
line-height: 1em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.InputfieldRepeaterItem {
|
||||
// prevents longer item headers from going outside Inputfield box horizontally
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
// repeater item that has DELETE pending
|
||||
&.InputfieldRepeaterDeletePending {
|
||||
> .InputfieldRepeaterHeaderInit > .InputfieldRepeaterItemControls {
|
||||
// keep just the trash icon visible when item marked for deletion
|
||||
display: block;
|
||||
opacity: 1.0;
|
||||
*:not(.InputfieldRepeaterTrash):not(.toggle-icon) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.InputfieldRepeaterLoudControls) > .InputfieldContent > .Inputfields > .InputfieldRepeaterItem {
|
||||
// repeater item that does NOT have delete pending
|
||||
&:not(.InputfieldRepeaterDeletePending) {
|
||||
> .InputfieldRepeaterHeaderInit:not(:hover) > .InputfieldRepeaterItemControls {
|
||||
opacity: 0;
|
||||
transition: opacity 500ms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
& > .InputfieldContent > .Inputfields > .InputfieldRepeaterInsertItem {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
@@ -260,32 +260,41 @@ class FieldtypeRepeaterConfigHelper extends Wire {
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
/** @var InputfieldCheckbox $f */
|
||||
$f = $modules->get('InputfieldCheckbox');
|
||||
/** @var InputfieldToggle $f */
|
||||
$f = $modules->get('InputfieldToggle');
|
||||
$f->attr('name', 'rememberOpen');
|
||||
$f->label = $this->_('Remember which repeater items are open?');
|
||||
$f->description = $this->_('When checked, opened repeater items remain open after saving or reloading from the page editor (unless the user closes them).');
|
||||
$f->description = $this->_('When enabled, opened repeater items remain open after saving or reloading from the page editor (unless the user closes them).');
|
||||
$f->icon = 'lightbulb-o';
|
||||
if((int) $field->get('rememberOpen')) {
|
||||
$f->attr('checked', 'checked');
|
||||
}
|
||||
$f->columnWidth = 50;
|
||||
$f->val((int) $field->get('rememberOpen'));
|
||||
$fs->add($f);
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
/** @var InputfieldCheckbox $f */
|
||||
$f = $modules->get('InputfieldCheckbox');
|
||||
/** @var InputfieldToggle $f */
|
||||
$f = $modules->get('InputfieldToggle');
|
||||
$f->attr('name', 'accordionMode');
|
||||
$f->label = $this->_('Use accordion mode?');
|
||||
$f->description = $this->_('When checked, only one repeater item will be open at a time.');
|
||||
$f->description = $this->_('When enabled, only one repeater item will be open at a time.');
|
||||
$f->icon = 'map-o';
|
||||
if((int) $field->get('accordionMode')) {
|
||||
$f->attr('checked', 'checked');
|
||||
}
|
||||
$f->columnWidth = 50;
|
||||
$f->val((int) $field->get('accordionMode'));
|
||||
$fs->add($f);
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
/** @var InputfieldToggle $f */
|
||||
$f = $modules->get('InputfieldToggle');
|
||||
$f->attr('name', 'loudControls');
|
||||
$f->label = $this->_('When to show repeater item controls/actions');
|
||||
$f->labelType = InputfieldToggle::labelTypeCustom;
|
||||
$f->yesLabel = $this->_('Always');
|
||||
$f->noLabel = $this->_('Hover');
|
||||
$f->description = $this->_('The hover option can reduce clutter in the interface by showing the repeater item actions/controls (clone, insert, delete, etc.) only when the item header is hovered.');
|
||||
$f->notes = $this->_('Note that controls are always shown for touch devices regardless of this setting.');
|
||||
$f->icon = 'sliders';
|
||||
$f->val((int) $field->get('loudControls'));
|
||||
$fs->add($f);
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
$maxItems = (int) $field->get('repeaterMaxItems');
|
||||
|
Reference in New Issue
Block a user