mirror of
https://github.com/processwire/processwire.git
synced 2025-08-10 16:54:44 +02:00
Additional repeater updates including addition of a "minimum items" option, and support for an accordion mode.
This commit is contained in:
@@ -33,7 +33,7 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
return array(
|
return array(
|
||||||
'title' => __('Repeater', __FILE__), // Module Title
|
'title' => __('Repeater', __FILE__), // Module Title
|
||||||
'summary' => __('Maintains a collection of fields that are repeated for any number of times.', __FILE__), // Module Summary
|
'summary' => __('Maintains a collection of fields that are repeated for any number of times.', __FILE__), // Module Summary
|
||||||
'version' => 105,
|
'version' => 106,
|
||||||
'autoload' => true,
|
'autoload' => true,
|
||||||
'installs' => 'InputfieldRepeater'
|
'installs' => 'InputfieldRepeater'
|
||||||
);
|
);
|
||||||
@@ -496,7 +496,8 @@ class FieldtypeRepeater extends Fieldtype implements ConfigurableModule {
|
|||||||
$inputfield = $this->wire('modules')->get($this->getInputfieldClass());
|
$inputfield = $this->wire('modules')->get($this->getInputfieldClass());
|
||||||
$inputfield->set('page', $page);
|
$inputfield->set('page', $page);
|
||||||
$inputfield->set('field', $field);
|
$inputfield->set('field', $field);
|
||||||
$inputfield->set('repeaterMaxItems', (int) $field->get('repeaterMaxItems'));
|
$inputfield->set('repeaterMaxItems', (int) $field->get('repeaterMaxItems'));
|
||||||
|
$inputfield->set('repeaterMinItems', (int) $field->get('repeaterMinItems'));
|
||||||
$inputfield->set('repeaterDepth', (int) $field->get('repeaterDepth'));
|
$inputfield->set('repeaterDepth', (int) $field->get('repeaterDepth'));
|
||||||
$inputfield->set('repeaterReadyItems', 0); // ready items deprecated
|
$inputfield->set('repeaterReadyItems', 0); // ready items deprecated
|
||||||
|
|
||||||
|
@@ -6,10 +6,14 @@
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative; }
|
position: relative; }
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemLabel {
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemLabel,
|
||||||
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls {
|
||||||
|
display: none; }
|
||||||
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemLabel {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-left: 0.25em; }
|
padding-left: 0.25em; }
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls {
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemControls {
|
||||||
|
display: block;
|
||||||
padding-right: 0.5em;
|
padding-right: 0.5em;
|
||||||
padding-left: 0.5em;
|
padding-left: 0.5em;
|
||||||
margin-top: 0.5em;
|
margin-top: 0.5em;
|
||||||
@@ -20,19 +24,21 @@
|
|||||||
display: block;
|
display: block;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
height: 100%; }
|
height: 100%; }
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterClone,
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemControls .InputfieldRepeaterClone,
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterToggle,
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemControls .InputfieldRepeaterToggle,
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterTrash,
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemControls .InputfieldRepeaterTrash,
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .toggle-icon {
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemControls .toggle-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
float: right; }
|
float: right; }
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterTrash {
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemControls .InputfieldRepeaterTrash {
|
||||||
padding-right: 3px; }
|
padding-right: 3px; }
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterToggle {
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemControls .InputfieldRepeaterToggle {
|
||||||
margin-right: 1em; }
|
margin-right: 1em; }
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterClone {
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemControls .InputfieldRepeaterClone {
|
||||||
margin-right: 1em; }
|
margin-right: 1em; }
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .toggle-icon {
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .InputfieldRepeaterItemControls .pw-icon-disabled {
|
||||||
|
opacity: 0.3; }
|
||||||
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader.InputfieldRepeaterHeaderInit .toggle-icon {
|
||||||
line-height: 1em;
|
line-height: 1em;
|
||||||
margin-right: 0.5em; }
|
margin-right: 0.5em; }
|
||||||
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldStateCollapsed > .InputfieldHeader {
|
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldStateCollapsed > .InputfieldHeader {
|
||||||
|
@@ -26,6 +26,13 @@ function InputfieldRepeater($) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var isReno = $('body').hasClass('AdminThemeReno');
|
var isReno = $('body').hasClass('AdminThemeReno');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event timer for double clicks
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var doubleClickTimer = null;
|
||||||
|
|
||||||
|
|
||||||
/*** EVENTS ********************************************************************************************/
|
/*** EVENTS ********************************************************************************************/
|
||||||
|
|
||||||
@@ -55,14 +62,17 @@ function InputfieldRepeater($) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var eventDeleteClick = function(e) {
|
var eventDeleteClick = function(e) {
|
||||||
|
|
||||||
var $header = $(this).closest('.InputfieldHeader');
|
var $this = $(this);
|
||||||
|
var $header = $this.closest('.InputfieldHeader');
|
||||||
var $item = $header.parent();
|
var $item = $header.parent();
|
||||||
|
|
||||||
|
if(isActionDisabled($this)) return false;
|
||||||
|
|
||||||
if($item.hasClass('InputfieldRepeaterNewItem')) {
|
if($item.hasClass('InputfieldRepeaterNewItem')) {
|
||||||
// delete new item (noAjaxAdd mode)
|
// delete new item (noAjaxAdd mode)
|
||||||
var $numAddInput = $item.children('.InputfieldContent').children('.InputfieldRepeaterAddItem').children('input');
|
var $numAddInput = $item.children('.InputfieldContent').children('.InputfieldRepeaterAddItem').children('input');
|
||||||
$numAddInput.attr('value', parseInt($numAddInput.attr('value')-1)); // total number of new items to add, minus 1
|
$numAddInput.attr('value', parseInt($numAddInput.attr('value') - 1)); // total number of new items to add, minus 1
|
||||||
$item.remove();
|
$item.remove();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@@ -87,20 +97,22 @@ function InputfieldRepeater($) {
|
|||||||
$header.find('.InputfieldRepeaterItemControls').css('background-color', $header.css('background-color'));
|
$header.find('.InputfieldRepeaterItemControls').css('background-color', $header.css('background-color'));
|
||||||
}
|
}
|
||||||
|
|
||||||
checkMax($item.closest('.InputfieldRepeater'));
|
checkMinMax($item.closest('.InputfieldRepeater'));
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event handler for when the "delete" link is double clicked
|
* Event handler for when the "delete" link is double clicked
|
||||||
*
|
*
|
||||||
* @param e
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
var eventDeleteDblClick = function(e) {
|
var eventDeleteDblClick = function() {
|
||||||
|
|
||||||
|
var $this = $(this);
|
||||||
var $li = $(this).closest('li');
|
var $li = $(this).closest('li');
|
||||||
var undelete = $li.hasClass('InputfieldRepeaterDeletePending');
|
var undelete = $li.hasClass('InputfieldRepeaterDeletePending');
|
||||||
|
|
||||||
|
if(isActionDisabled($this)) return false;
|
||||||
|
|
||||||
function selectAll() {
|
function selectAll() {
|
||||||
$li.parent().children('li').each(function() {
|
$li.parent().children('li').each(function() {
|
||||||
var $item = $(this);
|
var $item = $(this);
|
||||||
@@ -123,12 +135,13 @@ function InputfieldRepeater($) {
|
|||||||
/**
|
/**
|
||||||
* Event handler for when the "clone" repeater item action is clicked
|
* Event handler for when the "clone" repeater item action is clicked
|
||||||
*
|
*
|
||||||
* @param e
|
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var eventCloneClick = function(e) {
|
var eventCloneClick = function() {
|
||||||
var $item = $(this).closest('.InputfieldRepeaterItem');
|
var $this = $(this);
|
||||||
|
if(isActionDisabled($this)) return false;
|
||||||
|
var $item = $this.closest('.InputfieldRepeaterItem');
|
||||||
ProcessWire.confirm(ProcessWire.config.InputfieldRepeater.labels.clone, function() {
|
ProcessWire.confirm(ProcessWire.config.InputfieldRepeater.labels.clone, function() {
|
||||||
var itemID = $item.attr('data-page');
|
var itemID = $item.attr('data-page');
|
||||||
var $addLink = $item.closest('.InputfieldRepeater').children('.InputfieldContent')
|
var $addLink = $item.closest('.InputfieldRepeater').children('.InputfieldContent')
|
||||||
@@ -152,26 +165,29 @@ function InputfieldRepeater($) {
|
|||||||
var $item = $this.closest('.InputfieldRepeaterItem');
|
var $item = $this.closest('.InputfieldRepeaterItem');
|
||||||
var $input = $item.find('.InputfieldRepeaterPublish');
|
var $input = $item.find('.InputfieldRepeaterPublish');
|
||||||
|
|
||||||
if($this.hasClass(toggleOn)) {
|
if(doubleClickTimer) clearTimeout(doubleClickTimer);
|
||||||
$this.removeClass(toggleOn).addClass(toggleOff);
|
doubleClickTimer = setTimeout(function() {
|
||||||
$item.addClass('InputfieldRepeaterUnpublished InputfieldRepeaterOff');
|
if(isActionDisabled($this)) return false;
|
||||||
$input.val('-1');
|
if($this.hasClass(toggleOn)) {
|
||||||
} else {
|
$this.removeClass(toggleOn).addClass(toggleOff);
|
||||||
$this.removeClass(toggleOff).addClass(toggleOn);
|
$item.addClass('InputfieldRepeaterUnpublished InputfieldRepeaterOff');
|
||||||
$item.removeClass('InputfieldRepeaterUnpublished InputfieldRepeaterOff');
|
$input.val('-1');
|
||||||
$input.val('1');
|
} else {
|
||||||
}
|
$this.removeClass(toggleOff).addClass(toggleOn);
|
||||||
|
$item.removeClass('InputfieldRepeaterUnpublished InputfieldRepeaterOff');
|
||||||
|
$input.val('1');
|
||||||
|
}
|
||||||
|
checkMinMax($item.closest('.InputfieldRepeater'));
|
||||||
|
}, 250);
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event handler for when a repeater item is about to be opened
|
* Event handler for when a repeater item is about to be opened
|
||||||
*
|
*
|
||||||
* @param e
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
var eventItemOpenReady = function(e) {
|
var eventItemOpenReady = function() {
|
||||||
var $item = $(this);
|
var $item = $(this);
|
||||||
var $loaded = $item.find(".InputfieldRepeaterLoaded");
|
var $loaded = $item.find(".InputfieldRepeaterLoaded");
|
||||||
if(parseInt($loaded.val()) > 0) return; // item already loaded
|
if(parseInt($loaded.val()) > 0) return; // item already loaded
|
||||||
@@ -181,16 +197,18 @@ function InputfieldRepeater($) {
|
|||||||
/**
|
/**
|
||||||
* Event handler for when a repeater item is opened (primarily focused on ajax loaded items)
|
* Event handler for when a repeater item is opened (primarily focused on ajax loaded items)
|
||||||
*
|
*
|
||||||
* @param e
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
var eventItemOpened = function(e) {
|
var eventItemOpened = function() {
|
||||||
|
|
||||||
var $item = $(this);
|
var $item = $(this);
|
||||||
var $loaded = $item.find(".InputfieldRepeaterLoaded");
|
var $loaded = $item.find(".InputfieldRepeaterLoaded");
|
||||||
|
|
||||||
updateState($item);
|
updateState($item);
|
||||||
|
|
||||||
if(parseInt($loaded.val()) > 0) return; // item already loaded
|
if(parseInt($loaded.val()) > 0) {
|
||||||
|
updateAccordion($item);
|
||||||
|
return; // item already loaded
|
||||||
|
}
|
||||||
|
|
||||||
$loaded.val('1');
|
$loaded.val('1');
|
||||||
|
|
||||||
@@ -221,8 +239,10 @@ function InputfieldRepeater($) {
|
|||||||
initRepeater($(this));
|
initRepeater($(this));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
$content.slideDown('fast', function() {
|
$content.slideDown('fast', function() {
|
||||||
$spinner.removeClass('fa-spin fa-spinner').addClass('fa-arrows');
|
$spinner.removeClass('fa-spin fa-spinner').addClass('fa-arrows');
|
||||||
|
updateAccordion($item);
|
||||||
});
|
});
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
$inputfields.find('.Inputfield').trigger('reloaded', ['InputfieldRepeaterItemEdit']);
|
$inputfields.find('.Inputfield').trigger('reloaded', ['InputfieldRepeaterItemEdit']);
|
||||||
@@ -234,10 +254,8 @@ function InputfieldRepeater($) {
|
|||||||
/**
|
/**
|
||||||
* Event handler for when a repeater item is closed
|
* Event handler for when a repeater item is closed
|
||||||
*
|
*
|
||||||
* @param e
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
var eventItemClosed = function(e) {
|
var eventItemClosed = function() {
|
||||||
updateState($(this));
|
updateState($(this));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -246,11 +264,10 @@ function InputfieldRepeater($) {
|
|||||||
*
|
*
|
||||||
* Handles adding repeater items and initializing them
|
* Handles adding repeater items and initializing them
|
||||||
*
|
*
|
||||||
* @param e
|
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var eventAddLinkClick = function(e) {
|
var eventAddLinkClick = function() {
|
||||||
var $addLink = $(this);
|
var $addLink = $(this);
|
||||||
var $inputfields = $addLink.parent('p').prev('ul.Inputfields');
|
var $inputfields = $addLink.parent('p').prev('ul.Inputfields');
|
||||||
var $inputfieldRepeater = $addLink.closest('.InputfieldRepeater');
|
var $inputfieldRepeater = $addLink.closest('.InputfieldRepeater');
|
||||||
@@ -278,10 +295,10 @@ function InputfieldRepeater($) {
|
|||||||
newItemTotal = $newItem.length;
|
newItemTotal = $newItem.length;
|
||||||
if(newItemTotal > 0) {
|
if(newItemTotal > 0) {
|
||||||
if(newItemTotal > 1) $newItem = $newItem.slice(0, 1);
|
if(newItemTotal > 1) $newItem = $newItem.slice(0, 1);
|
||||||
var $addItem = $newItem.clone(true)
|
var $addItem = $newItem.clone(true);
|
||||||
addRepeaterItem($addItem);
|
addRepeaterItem($addItem);
|
||||||
$numAddInput.attr('value', newItemTotal);
|
$numAddInput.attr('value', newItemTotal);
|
||||||
checkMax($inputfieldRepeater);
|
checkMinMax($inputfieldRepeater);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -301,7 +318,7 @@ function InputfieldRepeater($) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// determine which page IDs we don't accept for new items (because we already have them rendered)
|
// determine which page IDs we don't accept for new items (because we already have them rendered)
|
||||||
var $unpublishedItems = $inputfields.find('.InputfieldRepeaterUnpublished');
|
var $unpublishedItems = $inputfields.find('.InputfieldRepeaterUnpublished:not(.InputfieldRepeaterMinItem)');
|
||||||
if($unpublishedItems.length) {
|
if($unpublishedItems.length) {
|
||||||
ajaxURL += '&repeater_not=';
|
ajaxURL += '&repeater_not=';
|
||||||
$unpublishedItems.each(function() {
|
$unpublishedItems.each(function() {
|
||||||
@@ -328,8 +345,9 @@ function InputfieldRepeater($) {
|
|||||||
scrollTop: $addItem.offset().top
|
scrollTop: $addItem.offset().top
|
||||||
}, 500, 'swing');
|
}, 500, 'swing');
|
||||||
updateState($addItem);
|
updateState($addItem);
|
||||||
checkMax($inputfieldRepeater);
|
checkMinMax($inputfieldRepeater);
|
||||||
$nestedRepeaters = $addItem.find('.InputfieldRepeater');
|
updateAccordion($addItem);
|
||||||
|
var $nestedRepeaters = $addItem.find('.InputfieldRepeater');
|
||||||
if($nestedRepeaters.length) {
|
if($nestedRepeaters.length) {
|
||||||
$nestedRepeaters.each(function() {
|
$nestedRepeaters.each(function() {
|
||||||
initRepeater($(this));
|
initRepeater($(this));
|
||||||
@@ -348,9 +366,14 @@ function InputfieldRepeater($) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var eventOpenAllClick = function(e) {
|
var eventOpenAllClick = function(e) {
|
||||||
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
if(doubleClickTimer) clearTimeout(doubleClickTimer);
|
||||||
|
|
||||||
|
if($(this).closest('.InputfieldRepeater').hasClass('InputfieldRepeaterAccordion')) return false;
|
||||||
|
|
||||||
var $repeater = $(this).closest('.InputfieldRepeater');
|
var $repeater = $(this).closest('.InputfieldRepeater');
|
||||||
var $items = $repeater.children('.InputfieldContent').children('.Inputfields').children('.InputfieldRepeaterItem');
|
var $items = $repeater.children('.InputfieldContent').children('.Inputfields').children('.InputfieldRepeaterItem');
|
||||||
if(!$items.length) return false;
|
if(!$items.length) return false;
|
||||||
@@ -372,6 +395,51 @@ function InputfieldRepeater($) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*** GENERAL FUNCTIONS **********************************************************************************/
|
/*** GENERAL FUNCTIONS **********************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not the given icon action is disabled
|
||||||
|
*
|
||||||
|
* @param $this The '.fa-' icon that represents the action
|
||||||
|
* @returns {boolean}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function isActionDisabled($this) {
|
||||||
|
if($this.hasClass('pw-icon-disabled')) {
|
||||||
|
ProcessWire.alert(ProcessWire.config.InputfieldRepeater.labels.disabledMinMax);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAccordion($item) {
|
||||||
|
|
||||||
|
if(!$item.closest('.InputfieldRepeater').hasClass('InputfieldRepeaterAccordion')) return false;
|
||||||
|
|
||||||
|
var itemID = $item.attr('id');
|
||||||
|
var useScroll = false;
|
||||||
|
var $siblings = $item.parent().children('.InputfieldRepeaterItem');
|
||||||
|
var itemHasPassed = false;
|
||||||
|
var hasOpen = false;
|
||||||
|
|
||||||
|
$siblings.each(function() {
|
||||||
|
var $sibling = $(this);
|
||||||
|
if($sibling.attr('id') == itemID) {
|
||||||
|
itemHasPassed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if($sibling.hasClass('InputfieldStateCollapsed')) return;
|
||||||
|
if(!$sibling.is(':visible')) return;
|
||||||
|
if(!itemHasPassed) useScroll = true;
|
||||||
|
$sibling.children('.InputfieldHeader').find('.toggle-icon').trigger('click', [ { duration: 0 }]);
|
||||||
|
hasOpen = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if(useScroll && hasOpen) {
|
||||||
|
$('html, body').animate({scrollTop: $item.offset().top - 10}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an InputfieldRepeaterItem update the label consistent with any present formatting sting
|
* Given an InputfieldRepeaterItem update the label consistent with any present formatting sting
|
||||||
@@ -379,28 +447,32 @@ function InputfieldRepeater($) {
|
|||||||
* Primarily adjusts item count(s) and allowed for {secondary} text appearance
|
* Primarily adjusts item count(s) and allowed for {secondary} text appearance
|
||||||
*
|
*
|
||||||
* @param $item An .InputfieldRepeaterItem
|
* @param $item An .InputfieldRepeaterItem
|
||||||
* @param bool doIncrement Specify true to increment the item count value (like for new items)
|
* @param {boolean} doIncrement Specify true to increment the item count value (like for new items)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function adjustItemLabel($item, doIncrement) {
|
function adjustItemLabel($item, doIncrement) {
|
||||||
|
|
||||||
var $label = $item.children('label');
|
var $label;
|
||||||
|
$label = $item.children('.InputfieldHeader').find('.InputfieldRepeaterItemLabel');
|
||||||
|
if(typeof $label == "undefined") $label = $item.children('label');
|
||||||
var labelHTML = $label.html();
|
var labelHTML = $label.html();
|
||||||
var _labelHTML = labelHTML;
|
var _labelHTML = labelHTML;
|
||||||
|
|
||||||
if(doIncrement && labelHTML.indexOf('#') > -1) {
|
if(typeof labelHTML != "undefined") {
|
||||||
num = $item.siblings('.InputfieldRepeaterItem:visible').length + 1;
|
if(doIncrement && labelHTML.indexOf('#') > -1) {
|
||||||
labelHTML = labelHTML.replace(/#[0-9]+/, '#' + num);
|
var num = $item.siblings('.InputfieldRepeaterItem:visible').length + 1;
|
||||||
}
|
labelHTML = labelHTML.replace(/#[0-9]+/, '#' + num);
|
||||||
|
}
|
||||||
|
|
||||||
if(labelHTML.indexOf('{') > -1) {
|
while(labelHTML.indexOf('}') > -1) {
|
||||||
// parts of the label wrapped in {brackets} get different appearance
|
// parts of the label wrapped in {brackets} get different appearance
|
||||||
labelHTML = labelHTML.replace(/\{/, '<span class="ui-priority-secondary" style="font-weight:normal">');
|
labelHTML = labelHTML.replace(/\{/, '<span class="ui-priority-secondary" style="font-weight:normal">');
|
||||||
labelHTML = labelHTML.replace(/}/, '</span>');
|
labelHTML = labelHTML.replace(/}/, '</span>');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(labelHTML != _labelHTML) {
|
if(labelHTML != _labelHTML) {
|
||||||
$label.html(labelHTML);
|
$label.html(labelHTML);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,6 +629,56 @@ function InputfieldRepeater($) {
|
|||||||
$inputfields.sortable(sortableOptions);
|
$inputfields.sortable(sortableOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the .InputfieldHeader for .InputfieldRepeaterItem elements
|
||||||
|
*
|
||||||
|
* @param $headers The .InputfieldHeader elements
|
||||||
|
* @param $inputfieldRepeater The parent .InputfieldRepeater
|
||||||
|
* @param {boolean} renderValueMode Whether or not this is value-only rendering mode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function initHeaders($headers, $inputfieldRepeater, renderValueMode) {
|
||||||
|
|
||||||
|
var $clone = $("<i class='fa fa-copy InputfieldRepeaterClone'></i>").css('display', 'block');
|
||||||
|
var $delete = $("<i class='fa fa-trash InputfieldRepeaterTrash'></i>");
|
||||||
|
var $toggle = $("<i class='fa InputfieldRepeaterToggle' data-on='fa-toggle-on' data-off='fa-toggle-off'></i>");
|
||||||
|
var cfg = ProcessWire.config.InputfieldRepeater;
|
||||||
|
var allowClone = !$inputfieldRepeater.hasClass('InputfieldRepeaterNoAjaxAdd');
|
||||||
|
|
||||||
|
if(cfg) {
|
||||||
|
$toggle.attr('title', cfg.labels.toggle);
|
||||||
|
$delete.attr('title', cfg.labels.remove);
|
||||||
|
$clone.attr('title', cfg.labels.clone);
|
||||||
|
}
|
||||||
|
|
||||||
|
$headers.each(function() {
|
||||||
|
var $t = $(this);
|
||||||
|
if($t.hasClass('InputfieldRepeaterHeaderInit')) return;
|
||||||
|
var icon = 'fa-arrows';
|
||||||
|
var $item = $t.parent();
|
||||||
|
if($item.hasClass('InputfieldRepeaterNewItem')) {
|
||||||
|
// noAjaxAdd mode
|
||||||
|
icon = 'fa-plus';
|
||||||
|
$t.addClass('ui-priority-secondary');
|
||||||
|
}
|
||||||
|
$t.addClass('ui-state-default InputfieldRepeaterHeaderInit');
|
||||||
|
$t.prepend("<i class='fa fa-fw " + icon + " InputfieldRepeaterDrag'></i>");
|
||||||
|
if(!renderValueMode) {
|
||||||
|
var $controls = $("<span class='InputfieldRepeaterItemControls'></span>");
|
||||||
|
var $toggleControl = $toggle.clone(true)
|
||||||
|
.addClass($t.parent().hasClass('InputfieldRepeaterOff') ? 'fa-toggle-off' : 'fa-toggle-on');
|
||||||
|
var $deleteControl = $delete.clone(true);
|
||||||
|
var $collapseControl = $t.find('.toggle-icon');
|
||||||
|
$controls.prepend($collapseControl);
|
||||||
|
if(allowClone) $controls.prepend($clone.clone(true));
|
||||||
|
$controls.prepend($toggleControl).prepend($deleteControl);
|
||||||
|
$t.prepend($controls);
|
||||||
|
$controls.css('background-color', $t.css('background-color'));
|
||||||
|
}
|
||||||
|
adjustItemLabel($item, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a repeater
|
* Initialize a repeater
|
||||||
*
|
*
|
||||||
@@ -578,58 +700,17 @@ function InputfieldRepeater($) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($inputfields.hasClass('InputfieldRepeaterInit')) return;
|
if($inputfields.hasClass('InputfieldRepeaterInit')) return;
|
||||||
|
|
||||||
|
var renderValueMode = $inputfields.closest('.InputfieldRenderValueMode').length > 0;
|
||||||
|
|
||||||
$inputfields.addClass('InputfieldRepeaterInit');
|
$inputfields.addClass('InputfieldRepeaterInit');
|
||||||
|
|
||||||
var renderValueMode = $inputfields.closest('.InputfieldRenderValueMode').length > 0;
|
|
||||||
var $clone = $("<i class='fa fa-copy InputfieldRepeaterClone'></i>").css('display', 'block');
|
|
||||||
var $delete = $("<i class='fa fa-trash InputfieldRepeaterTrash'></i>");
|
|
||||||
var $toggle = $("<i class='fa InputfieldRepeaterToggle' data-on='fa-toggle-on' data-off='fa-toggle-off'></i>");
|
|
||||||
var cfg = ProcessWire.config.InputfieldRepeater;
|
|
||||||
var allowClone = !$inputfieldRepeater.hasClass('InputfieldRepeaterNoAjaxAdd');
|
|
||||||
|
|
||||||
if(cfg) {
|
|
||||||
$toggle.attr('title', cfg.labels.toggle);
|
|
||||||
$delete.attr('title', cfg.labels.remove);
|
|
||||||
$clone.attr('title', cfg.labels.clone);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("input.InputfieldRepeaterDelete", $this).parents('.InputfieldCheckbox').hide();
|
$("input.InputfieldRepeaterDelete", $this).parents('.InputfieldCheckbox').hide();
|
||||||
|
|
||||||
function initHeaders($headers) {
|
|
||||||
$headers.each(function() {
|
|
||||||
var $t = $(this);
|
|
||||||
if($t.hasClass('InputfieldRepeaterHeaderInit')) return;
|
|
||||||
var icon = 'fa-arrows';
|
|
||||||
var $item = $t.parent();
|
|
||||||
if($item.hasClass('InputfieldRepeaterNewItem')) {
|
|
||||||
// noAjaxAdd mode
|
|
||||||
icon = 'fa-plus';
|
|
||||||
$t.addClass('ui-priority-secondary');
|
|
||||||
}
|
|
||||||
$t.addClass('ui-state-default InputfieldRepeaterHeaderInit');
|
|
||||||
$t.prepend("<i class='fa fa-fw " + icon + " InputfieldRepeaterDrag'></i>")
|
|
||||||
if(!renderValueMode) {
|
|
||||||
//if(allowClone) $t.prepend($clone.clone(true));
|
|
||||||
var $controls = $("<span class='InputfieldRepeaterItemControls'></span>");
|
|
||||||
var $toggleControl = $toggle.clone(true).addClass($t.parent().hasClass('InputfieldRepeaterOff') ? 'fa-toggle-off' : 'fa-toggle-on');
|
|
||||||
var $deleteControl = $delete.clone(true);
|
|
||||||
var $collapseControl = $t.find('.toggle-icon');
|
|
||||||
//$collapseControl.addClass('InputfieldRepeaterCollapse').removeClass('toggle-icon');
|
|
||||||
$controls.prepend($collapseControl);
|
|
||||||
if(allowClone) $controls.prepend($clone.clone(true));
|
|
||||||
$controls.prepend($toggleControl).prepend($deleteControl);
|
|
||||||
$t.prepend($controls);
|
|
||||||
$controls.css('background-color', $t.css('background-color'));
|
|
||||||
}
|
|
||||||
adjustItemLabel($item, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isItem) {
|
if(isItem) {
|
||||||
initHeaders($this.children('.InputfieldHeader'));
|
initHeaders($this.children('.InputfieldHeader'), $inputfieldRepeater, renderValueMode);
|
||||||
} else {
|
} else {
|
||||||
initHeaders($(".InputfieldRepeaterItem > .InputfieldHeader", $this));
|
initHeaders($(".InputfieldRepeaterItem > .InputfieldHeader", $this), $inputfieldRepeater, renderValueMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(renderValueMode) {
|
if(renderValueMode) {
|
||||||
@@ -662,28 +743,87 @@ function InputfieldRepeater($) {
|
|||||||
|
|
||||||
// check for maximum items
|
// check for maximum items
|
||||||
if($inputfieldRepeater.hasClass('InputfieldRepeaterMax')) {
|
if($inputfieldRepeater.hasClass('InputfieldRepeaterMax')) {
|
||||||
checkMax($inputfieldRepeater);
|
checkMinMax($inputfieldRepeater);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When "max items" setting is used, this toggles whether or not "add" links are visible
|
* When "max items" setting is used, this toggles whether or not "add" links are visible
|
||||||
*
|
*
|
||||||
* @todo Make this toggle the clone links as well
|
|
||||||
* @param $inputfieldRepeater .InputfieldRepeater
|
* @param $inputfieldRepeater .InputfieldRepeater
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function checkMax($inputfieldRepeater) {
|
function checkMinMax($inputfieldRepeater) {
|
||||||
if(!$inputfieldRepeater.hasClass('InputfieldRepeaterMax')) return;
|
|
||||||
|
if(!$inputfieldRepeater.hasClass('InputfieldRepeaterMax')
|
||||||
|
&& !$inputfieldRepeater.hasClass('InputfieldRepeaterMin')) return;
|
||||||
|
|
||||||
var max = parseInt($inputfieldRepeater.attr('data-max'));
|
var max = parseInt($inputfieldRepeater.attr('data-max'));
|
||||||
if(max <= 0) return;
|
var min = parseInt($inputfieldRepeater.attr('data-min'));
|
||||||
|
|
||||||
|
if(max <= 0 && min <= 0) return;
|
||||||
|
|
||||||
var $content = $inputfieldRepeater.children('.InputfieldContent');
|
var $content = $inputfieldRepeater.children('.InputfieldContent');
|
||||||
var num = $content.children('.Inputfields').children('li:not(.InputfieldRepeaterDeletePending)').length;
|
var num = $content.children('.Inputfields')
|
||||||
|
.children('li:not(.InputfieldRepeaterDeletePending):not(.InputfieldRepeaterOff):visible').length;
|
||||||
var $addItem = $content.children('.InputfieldRepeaterAddItem');
|
var $addItem = $content.children('.InputfieldRepeaterAddItem');
|
||||||
if(num > max) {
|
var cloneChange = '';
|
||||||
$addItem.hide();
|
var trashChange = '';
|
||||||
} else if(!$addItem.is(":visible")) {
|
|
||||||
$addItem.show();
|
if(max > 0) {
|
||||||
|
if(num >= max) {
|
||||||
|
$addItem.hide();
|
||||||
|
cloneChange = 'hide';
|
||||||
|
} else if(!$addItem.is(":visible")) {
|
||||||
|
$addItem.show();
|
||||||
|
cloneChange = 'show';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(min > 0) {
|
||||||
|
if(num <= min) {
|
||||||
|
trashChange = 'hide';
|
||||||
|
$content.addClass('InputfieldRepeaterTrashHidden');
|
||||||
|
} else if($content.hasClass('InputfieldRepeaterTrashHidden')) {
|
||||||
|
$content.removeClass('InputfieldRepeaterTrashHidden');
|
||||||
|
trashChange = 'show';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cloneChange.length || trashChange.length) {
|
||||||
|
var $items = $content.children('.Inputfields').children('.InputfieldRepeaterItem');
|
||||||
|
if(cloneChange.length) {
|
||||||
|
// update the visibility of clone actions
|
||||||
|
$items.each(function() {
|
||||||
|
var $clone = $(this).children('.InputfieldHeader').find('.InputfieldRepeaterClone');
|
||||||
|
if(cloneChange === 'show') {
|
||||||
|
$clone.removeClass('pw-icon-disabled');
|
||||||
|
} else {
|
||||||
|
$clone.addClass('pw-icon-disabled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(trashChange.length) {
|
||||||
|
// update visibility of trash actions
|
||||||
|
$items.each(function() {
|
||||||
|
var $header = $(this).children('.InputfieldHeader');
|
||||||
|
var $trash = $header.find('.InputfieldRepeaterTrash');
|
||||||
|
var $toggle = $header.find('.InputfieldRepeaterToggle.fa-toggle-on');
|
||||||
|
if(trashChange === 'show') {
|
||||||
|
$trash.removeClass('pw-icon-disabled');
|
||||||
|
$toggle.removeClass('pw-icon-disabled');
|
||||||
|
} else {
|
||||||
|
$trash.addClass('pw-icon-disabled');
|
||||||
|
$toggle.addClass('pw-icon-disabled');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if(trashChange == 'hide') {
|
||||||
|
$content.children('.Inputfields').children('li.InputfieldRepeaterDeletePending').each(function() {
|
||||||
|
var $trash = $(this).children('.InputfieldHeader').find('.InputfieldRepeaterTrash');
|
||||||
|
$trash.removeClass('pw-icon-disabled');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -9,7 +9,9 @@
|
|||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
* @property int $repeaterMaxItems
|
* @property int $repeaterMaxItems
|
||||||
|
* @property int $repeaterMinItems
|
||||||
* @property int $repeaterDepth
|
* @property int $repeaterDepth
|
||||||
|
* @property bool $accordionMode
|
||||||
*
|
*
|
||||||
* @method string renderRepeaterLabel($label, $cnt, Page $page)
|
* @method string renderRepeaterLabel($label, $cnt, Page $page)
|
||||||
*
|
*
|
||||||
@@ -22,7 +24,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
return array(
|
return array(
|
||||||
'title' => __('Repeater', __FILE__), // Module Title
|
'title' => __('Repeater', __FILE__), // Module Title
|
||||||
'summary' => __('Repeats fields from another template. Provides the input for FieldtypeRepeater.', __FILE__), // Module Summary
|
'summary' => __('Repeats fields from another template. Provides the input for FieldtypeRepeater.', __FILE__), // Module Summary
|
||||||
'version' => 105,
|
'version' => 106,
|
||||||
'requires' => 'FieldtypeRepeater',
|
'requires' => 'FieldtypeRepeater',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -86,8 +88,10 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
// these are part of the Fieldtype's config, and automatically set from it
|
// these are part of the Fieldtype's config, and automatically set from it
|
||||||
$this->set('repeaterMaxItems', 0);
|
$this->set('repeaterMaxItems', 0);
|
||||||
|
$this->set('repeaterMinItems', 0);
|
||||||
$this->set('repeaterDepth', 0);
|
$this->set('repeaterDepth', 0);
|
||||||
|
$this->set('accordionMode', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,9 +282,22 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
$openIDs = array();
|
$openIDs = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$minItems = $this->repeaterMinItems;
|
||||||
|
|
||||||
|
// if there are a minimum required number of items, set them up now
|
||||||
|
if(!$itemID && $minItems > 0) {
|
||||||
|
$notIDs = $value->explode('id');
|
||||||
|
while($value->count() < $this->repeaterMinItems) {
|
||||||
|
$item = $this->getNextReadyPage($notIDs);
|
||||||
|
$value->add($item);
|
||||||
|
$notIDs[] = $item->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$repeaterCollapse = (int) $this->field->get('repeaterCollapse');
|
$repeaterCollapse = (int) $this->field->get('repeaterCollapse');
|
||||||
$cnt = 0;
|
$cnt = 0;
|
||||||
$numVisible = 0;
|
$numVisible = 0;
|
||||||
|
$numOpen = 0;
|
||||||
|
|
||||||
// create field for each repeater iteration
|
// create field for each repeater iteration
|
||||||
foreach($value as $key => $page) {
|
foreach($value as $key => $page) {
|
||||||
@@ -293,6 +310,9 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
$isReadyItem = $isHidden && $isUnpublished;
|
$isReadyItem = $isHidden && $isUnpublished;
|
||||||
$isClone = $page->get('_repeater_clone');
|
$isClone = $page->get('_repeater_clone');
|
||||||
$isOpen = in_array($page->id, $openIDs) || $isClone;
|
$isOpen = in_array($page->id, $openIDs) || $isClone;
|
||||||
|
$isMinItem = $isReadyItem && $minItems && $cnt < $minItems;
|
||||||
|
|
||||||
|
if($isOpen && $numOpen > 0 && $this->accordionMode) $isOpen = false;
|
||||||
|
|
||||||
// get the inputfields for the repeater page
|
// get the inputfields for the repeater page
|
||||||
if(is_null($loadInputsForIDs) || in_array($page->id, $loadInputsForIDs) || $isOpen) {
|
if(is_null($loadInputsForIDs) || in_array($page->id, $loadInputsForIDs) || $isOpen) {
|
||||||
@@ -354,8 +374,11 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
|
|
||||||
if($isOpen) {
|
if($isOpen) {
|
||||||
$wrap->collapsed = Inputfield::collapsedNo;
|
$wrap->collapsed = Inputfield::collapsedNo;
|
||||||
|
$numOpen++;
|
||||||
} else if($repeaterCollapse == FieldtypeRepeater::collapseExisting && !$page->get('_repeater_new') && !$isHidden) {
|
} else if($repeaterCollapse == FieldtypeRepeater::collapseExisting && !$page->get('_repeater_new') && !$isHidden) {
|
||||||
$wrap->collapsed = Inputfield::collapsedYes;
|
$wrap->collapsed = Inputfield::collapsedYes;
|
||||||
|
} else if($repeaterCollapse == FieldtypeRepeater::collapseExisting && $isMinItem) {
|
||||||
|
$wrap->collapsed = Inputfield::collapsedYes;
|
||||||
} else if($repeaterCollapse == FieldtypeRepeater::collapseAll) {
|
} else if($repeaterCollapse == FieldtypeRepeater::collapseAll) {
|
||||||
$wrap->collapsed = Inputfield::collapsedYes;
|
$wrap->collapsed = Inputfield::collapsedYes;
|
||||||
}
|
}
|
||||||
@@ -393,6 +416,12 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
if($depth) $wrap->prepend($depth);
|
if($depth) $wrap->prepend($depth);
|
||||||
$wrap->prepend($loaded);
|
$wrap->prepend($loaded);
|
||||||
|
|
||||||
|
if($isMinItem) {
|
||||||
|
// allow this ready item to be added so that minimum is met
|
||||||
|
$wrap->addClass('InputfieldRepeaterMinItem');
|
||||||
|
$isReadyItem = false;
|
||||||
|
}
|
||||||
|
|
||||||
if(!$isReadyItem) {
|
if(!$isReadyItem) {
|
||||||
$form->add($wrap);
|
$form->add($wrap);
|
||||||
$numVisible++;
|
$numVisible++;
|
||||||
@@ -532,11 +561,18 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
if($this->repeaterMaxItems > 0) {
|
if($this->repeaterMaxItems > 0) {
|
||||||
$this->addClass('InputfieldRepeaterMax', 'wrapClass');
|
$this->addClass('InputfieldRepeaterMax', 'wrapClass');
|
||||||
}
|
}
|
||||||
|
if($this->repeaterMinItems > 0) {
|
||||||
|
$this->addClass('InputfieldRepeaterMin', 'wrapClass');
|
||||||
|
}
|
||||||
if($this->repeaterDepth > 0) {
|
if($this->repeaterDepth > 0) {
|
||||||
$this->addClass('InputfieldRepeaterDepth', 'wrapClass');
|
$this->addClass('InputfieldRepeaterDepth', 'wrapClass');
|
||||||
}
|
}
|
||||||
|
if($this->accordionMode) {
|
||||||
|
$this->addClass('InputfieldRepeaterAccordion', 'wrapClass');
|
||||||
|
}
|
||||||
$this->wrapAttr('data-page', $this->page->id);
|
$this->wrapAttr('data-page', $this->page->id);
|
||||||
$this->wrapAttr('data-max', (int) $this->repeaterMaxItems);
|
$this->wrapAttr('data-max', (int) $this->repeaterMaxItems);
|
||||||
|
$this->wrapAttr('data-min', (int) $this->repeaterMinItems);
|
||||||
$this->wrapAttr('data-depth', (int) $this->repeaterDepth);
|
$this->wrapAttr('data-depth', (int) $this->repeaterDepth);
|
||||||
|
|
||||||
list($editorUrl, $queryString) = explode('?', $this->page->editUrl());
|
list($editorUrl, $queryString) = explode('?', $this->page->editUrl());
|
||||||
@@ -550,7 +586,8 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
'toggle' => $this->_x('Click to turn item on/off, or double-click to open/collapse all items', 'repeater-item-action'),
|
'toggle' => $this->_x('Click to turn item on/off, or double-click to open/collapse all items', 'repeater-item-action'),
|
||||||
'clone' => $this->_x('Clone this item?', 'repeater-item-action'),
|
'clone' => $this->_x('Clone this item?', 'repeater-item-action'),
|
||||||
'openAll' => $this->_x('Open all items?', 'repeater-item-action'),
|
'openAll' => $this->_x('Open all items?', 'repeater-item-action'),
|
||||||
'collapseAll' => $this->_x('Collapse all items?', 'repeater-item-action')
|
'collapseAll' => $this->_x('Collapse all items?', 'repeater-item-action'),
|
||||||
|
'disabledMinMax' => $this->_('This action is disabled per min and/or max item settings.')
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@@ -12,45 +12,56 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.InputfieldRepeaterItemLabel {
|
.InputfieldRepeaterItemLabel,
|
||||||
display: inline-block;
|
|
||||||
padding-left: 0.25em;
|
|
||||||
}
|
|
||||||
.InputfieldRepeaterItemControls {
|
.InputfieldRepeaterItemControls {
|
||||||
padding-right: 0.5em;
|
display: none;
|
||||||
padding-left: 0.5em;
|
}
|
||||||
margin-top: 0.5em;
|
|
||||||
position: absolute;
|
&.InputfieldRepeaterHeaderInit {
|
||||||
top: 0;
|
.InputfieldRepeaterItemLabel {
|
||||||
right: 0;
|
display: inline-block;
|
||||||
z-index: 1;
|
padding-left: 0.25em;
|
||||||
display: block;
|
}
|
||||||
white-space: nowrap;
|
.InputfieldRepeaterItemControls {
|
||||||
height: 100%;
|
display: block;
|
||||||
|
padding-right: 0.5em;
|
||||||
.InputfieldRepeaterClone,
|
padding-left: 0.5em;
|
||||||
.InputfieldRepeaterToggle,
|
margin-top: 0.5em;
|
||||||
.InputfieldRepeaterTrash,
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 1;
|
||||||
|
display: block;
|
||||||
|
white-space: nowrap;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.InputfieldRepeaterClone,
|
||||||
|
.InputfieldRepeaterToggle,
|
||||||
|
.InputfieldRepeaterTrash,
|
||||||
|
.toggle-icon {
|
||||||
|
cursor: pointer;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.InputfieldRepeaterTrash {
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
.InputfieldRepeaterToggle {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
.InputfieldRepeaterClone {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
.pw-icon-disabled {
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.toggle-icon {
|
.toggle-icon {
|
||||||
cursor: pointer;
|
line-height: 1em;
|
||||||
float: right;
|
margin-right: 0.5em;
|
||||||
}
|
|
||||||
.InputfieldRepeaterTrash {
|
|
||||||
padding-right: 3px;
|
|
||||||
}
|
|
||||||
.InputfieldRepeaterToggle {
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
.InputfieldRepeaterClone {
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.toggle-icon {
|
|
||||||
line-height: 1em;
|
|
||||||
margin-right: 0.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending) {
|
.InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending) {
|
||||||
|
@@ -186,13 +186,21 @@ class FieldtypeRepeaterConfigHelper extends Wire {
|
|||||||
$f->label = $this->_('Remember which repeater items are open?');
|
$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 checked, opened repeater items remain open after saving or reloading from the page editor (unless the user closes them).');
|
||||||
$f->icon = 'lightbulb-o';
|
$f->icon = 'lightbulb-o';
|
||||||
if((int) $field->get('rememberOpen')) {
|
if((int) $field->get('rememberOpen')) $f->attr('checked', 'checked');
|
||||||
$f->attr('checked', 'checked');
|
$f->columnWidth = 50;
|
||||||
} else {
|
|
||||||
$f->collapsed = Inputfield::collapsedYes;
|
|
||||||
}
|
|
||||||
$inputfields->add($f);
|
$inputfields->add($f);
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
$f = $this->wire('modules')->get('InputfieldCheckbox');
|
||||||
|
$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->icon = 'map-o';
|
||||||
|
if((int) $field->get('accordionMode')) $f->attr('checked', 'checked');
|
||||||
|
$f->columnWidth = 50;
|
||||||
|
$inputfields->add($f);
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
|
||||||
$value = (int) $field->get('repeaterMaxItems');
|
$value = (int) $field->get('repeaterMaxItems');
|
||||||
@@ -200,9 +208,21 @@ class FieldtypeRepeaterConfigHelper extends Wire {
|
|||||||
$f->attr('name', 'repeaterMaxItems');
|
$f->attr('name', 'repeaterMaxItems');
|
||||||
$f->attr('value', $value > 0 ? $value : '');
|
$f->attr('value', $value > 0 ? $value : '');
|
||||||
$f->label = $this->_('Maximum number of items');
|
$f->label = $this->_('Maximum number of items');
|
||||||
$f->collapsed = Inputfield::collapsedBlank;
|
|
||||||
$f->description = $this->_('If you need to limit the number of items allowed, enter the limit here (0=no limit).');
|
$f->description = $this->_('If you need to limit the number of items allowed, enter the limit here (0=no limit).');
|
||||||
$f->icon = 'hand-stop-o';
|
$f->icon = 'hand-stop-o';
|
||||||
|
$f->columnWidth = 50;
|
||||||
|
$inputfields->add($f);
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
|
||||||
|
$value = (int) $field->get('repeaterMinItems');
|
||||||
|
$f = $this->wire('modules')->get('InputfieldInteger');
|
||||||
|
$f->attr('name', 'repeaterMinItems');
|
||||||
|
$f->attr('value', $value > 0 ? $value : '');
|
||||||
|
$f->label = $this->_('Minimum number of items');
|
||||||
|
$f->description = $this->_('This many items will always be open and ready-to-edit (0=no minimum).');
|
||||||
|
$f->icon = 'hand-peace-o';
|
||||||
|
$f->columnWidth = 50;
|
||||||
$inputfields->add($f);
|
$inputfields->add($f);
|
||||||
|
|
||||||
// -------------------------------------------------
|
// -------------------------------------------------
|
||||||
|
@@ -1035,7 +1035,7 @@ function InputfieldStates($target) {
|
|||||||
if($newTab.hasClass('collapsed10')) InputfieldStateAjaxClick($newTab);
|
if($newTab.hasClass('collapsed10')) InputfieldStateAjaxClick($newTab);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on('click', '.InputfieldStateToggle, .toggle-icon', function() {
|
$(document).on('click', '.InputfieldStateToggle, .toggle-icon', function(event, data) {
|
||||||
|
|
||||||
var $t = $(this);
|
var $t = $(this);
|
||||||
var $li = $t.closest('.Inputfield');
|
var $li = $t.closest('.Inputfield');
|
||||||
@@ -1043,7 +1043,13 @@ function InputfieldStates($target) {
|
|||||||
var $icon = isIcon ? $t : $li.children('.InputfieldHeader, .ui-widget-header').find('.toggle-icon');
|
var $icon = isIcon ? $t : $li.children('.InputfieldHeader, .ui-widget-header').find('.toggle-icon');
|
||||||
var isCollapsed = $li.hasClass("InputfieldStateCollapsed");
|
var isCollapsed = $li.hasClass("InputfieldStateCollapsed");
|
||||||
var wasCollapsed = $li.hasClass("InputfieldStateWasCollapsed");
|
var wasCollapsed = $li.hasClass("InputfieldStateWasCollapsed");
|
||||||
|
var duration = 100;
|
||||||
|
|
||||||
if($li.hasClass('InputfieldAjaxLoading')) return false;
|
if($li.hasClass('InputfieldAjaxLoading')) return false;
|
||||||
|
|
||||||
|
if(typeof data != "undefined") {
|
||||||
|
if(typeof data.duration != "undefined") duration = data.duration;
|
||||||
|
}
|
||||||
|
|
||||||
if(isCollapsed && ($li.hasClass('collapsed10') || $li.hasClass('collapsed11'))) {
|
if(isCollapsed && ($li.hasClass('collapsed10') || $li.hasClass('collapsed11'))) {
|
||||||
if(InputfieldStateAjaxClick($li)) return false;
|
if(InputfieldStateAjaxClick($li)) return false;
|
||||||
@@ -1052,7 +1058,7 @@ function InputfieldStates($target) {
|
|||||||
if(isCollapsed || wasCollapsed || isIcon) {
|
if(isCollapsed || wasCollapsed || isIcon) {
|
||||||
$li.addClass('InputfieldStateWasCollapsed'); // this class only used here
|
$li.addClass('InputfieldStateWasCollapsed'); // this class only used here
|
||||||
$li.trigger(isCollapsed ? 'openReady' : 'closeReady');
|
$li.trigger(isCollapsed ? 'openReady' : 'closeReady');
|
||||||
$li.toggleClass('InputfieldStateCollapsed', 100, function() {
|
$li.toggleClass('InputfieldStateCollapsed', duration, function() {
|
||||||
if(isCollapsed) {
|
if(isCollapsed) {
|
||||||
$li.trigger('opened');
|
$li.trigger('opened');
|
||||||
if($li.hasClass('InputfieldColumnWidth')) $li.children('.InputfieldContent').show();
|
if($li.hasClass('InputfieldColumnWidth')) $li.children('.InputfieldContent').show();
|
||||||
|
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user