mirror of
https://github.com/processwire/processwire.git
synced 2025-08-12 09:44:38 +02:00
Upgrade Repeaters with ability to insert before/after existing items
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -33,6 +33,18 @@ function InputfieldRepeater($) {
|
|||||||
*/
|
*/
|
||||||
var doubleClickTimer = null;
|
var doubleClickTimer = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when an ajax request is currently processing a newly added item
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var currentlyAddingItem = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeout tracker for insert actions
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var insertTimeout = null;
|
||||||
|
|
||||||
|
|
||||||
/*** EVENTS ********************************************************************************************/
|
/*** EVENTS ********************************************************************************************/
|
||||||
|
|
||||||
@@ -313,6 +325,8 @@ function InputfieldRepeater($) {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
var eventAddLinkClick = function() {
|
var eventAddLinkClick = function() {
|
||||||
|
currentlyAddingItem = true;
|
||||||
|
|
||||||
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');
|
||||||
@@ -320,13 +334,25 @@ function InputfieldRepeater($) {
|
|||||||
var newItemTotal = 0; // for noAjaxAdd mode
|
var newItemTotal = 0; // for noAjaxAdd mode
|
||||||
var useAjax = $addLink.attr('data-noajax').length == 0;
|
var useAjax = $addLink.attr('data-noajax').length == 0;
|
||||||
var cloneID = $addLink.attr('data-clone');
|
var cloneID = $addLink.attr('data-clone');
|
||||||
|
var depth = 0;
|
||||||
|
var redoSortAll = false;
|
||||||
|
|
||||||
function addRepeaterItem($addItem) {
|
function addRepeaterItem($addItem) {
|
||||||
// make sure it has a unique ID
|
// make sure it has a unique ID
|
||||||
var id = $addItem.attr('id') + '_';
|
var id = $addItem.attr('id') + '_';
|
||||||
while($('#' + id).length > 0) id += '_';
|
while($('#' + id).length > 0) id += '_';
|
||||||
$addItem.attr('id', id);
|
$addItem.attr('id', id);
|
||||||
|
var $insertItem = $inputfields.children('.InputfieldRepeaterInsertItem');
|
||||||
|
if($insertItem.length) {
|
||||||
|
depth = getItemDepth($insertItem);
|
||||||
|
$addItem.addClass('InputfieldStateCollapsed')
|
||||||
|
var $toggleIcon = $addItem.children('.InputfieldHeader').find('.toggle-icon');
|
||||||
|
$toggleIcon.toggleClass($toggleIcon.attr('data-to'));
|
||||||
|
$insertItem.replaceWith($addItem);
|
||||||
|
redoSortAll = true;
|
||||||
|
} else {
|
||||||
$inputfields.append($addItem);
|
$inputfields.append($addItem);
|
||||||
|
}
|
||||||
$addItem.css('display', 'block');
|
$addItem.css('display', 'block');
|
||||||
adjustItemLabel($addItem, true);
|
adjustItemLabel($addItem, true);
|
||||||
$addLink.trigger('repeateradd', [ $addItem ]);
|
$addLink.trigger('repeateradd', [ $addItem ]);
|
||||||
@@ -341,10 +367,12 @@ function InputfieldRepeater($) {
|
|||||||
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);
|
||||||
|
if(depth) setItemDepth($addItem, depth);
|
||||||
addRepeaterItem($addItem);
|
addRepeaterItem($addItem);
|
||||||
$numAddInput.attr('value', newItemTotal);
|
$numAddInput.attr('value', newItemTotal);
|
||||||
checkMinMax($inputfieldRepeater);
|
checkMinMax($inputfieldRepeater);
|
||||||
}
|
}
|
||||||
|
currentlyAddingItem = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,10 +418,21 @@ function InputfieldRepeater($) {
|
|||||||
// next line can remove 9/2019, as 'cloned' support will have been in InputfieldTable for awhile
|
// next line can remove 9/2019, as 'cloned' support will have been in InputfieldTable for awhile
|
||||||
$addItem.find('.InputfieldTableRowID').val(0);
|
$addItem.find('.InputfieldTableRowID').val(0);
|
||||||
}
|
}
|
||||||
$addItem.find('.InputfieldRepeaterSort').val($inputfields.children().length);
|
if(redoSortAll) {
|
||||||
|
$inputfields.children('.InputfieldRepeaterItem').each(function(n) {
|
||||||
|
setItemSort($(this), n);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setItemSort($addItem, $inputfields.children().length);
|
||||||
|
}
|
||||||
|
if(depth) setItemDepth($addItem, depth);
|
||||||
|
if($addItem.hasClass('InputfieldStateCollapsed')) {
|
||||||
|
// ok
|
||||||
|
} else {
|
||||||
$('html, body').animate({
|
$('html, body').animate({
|
||||||
scrollTop: $addItem.offset().top
|
scrollTop: $addItem.offset().top
|
||||||
}, 500, 'swing');
|
}, 500, 'swing');
|
||||||
|
}
|
||||||
updateState($addItem);
|
updateState($addItem);
|
||||||
checkMinMax($inputfieldRepeater);
|
checkMinMax($inputfieldRepeater);
|
||||||
updateAccordion($addItem);
|
updateAccordion($addItem);
|
||||||
@@ -404,6 +443,8 @@ function InputfieldRepeater($) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
runScripts(data);
|
runScripts(data);
|
||||||
|
|
||||||
|
setTimeout(function() { currentlyAddingItem = false; }, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -446,6 +487,131 @@ function InputfieldRepeater($) {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click the "insert before" button event
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var eventInsertBeforeClick = function(e) {
|
||||||
|
var $item = $(this).closest('.InputfieldRepeaterItem');
|
||||||
|
eventInsertClick($item, true);
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click the "insert after" button event
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var eventInsertAfterClick = function(e) {
|
||||||
|
var $item = $(this).closest('.InputfieldRepeaterItem');
|
||||||
|
eventInsertClick($item, false);
|
||||||
|
e.stopPropagation();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for either insert before or insert after click events
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function eventInsertClick($item, insertBefore) {
|
||||||
|
if(currentlyAddingItem) return false;
|
||||||
|
currentlyAddingItem = true;
|
||||||
|
if(insertTimeout) clearTimeout(insertTimeout);
|
||||||
|
var depth = getInsertItemDepth($item, insertBefore);
|
||||||
|
var $insertItem = $item.siblings('.InputfieldRepeaterInsertItem');
|
||||||
|
if($insertItem.length) {
|
||||||
|
$insertItem.remove();
|
||||||
|
}
|
||||||
|
var $placeholder = $item.siblings('.InputfieldRepeaterNewItem').clone()
|
||||||
|
.removeClass('.InputfieldRepeaterNewItem').addClass('InputfieldRepeaterInsertItem');
|
||||||
|
$placeholder.attr('id', $placeholder.attr('id') + '-placeholder');
|
||||||
|
$placeholder.find('.InputfieldHeader').html("<i class='fa fa-spin fa-spinner'></i>");
|
||||||
|
if(insertBefore) {
|
||||||
|
$placeholder.insertBefore($item);
|
||||||
|
} else {
|
||||||
|
$placeholder.insertAfter($item);
|
||||||
|
}
|
||||||
|
if(depth > 0) setItemDepth($placeholder, depth);
|
||||||
|
$placeholder.show();
|
||||||
|
if(!insertBefore && !$item.hasClass('InputfieldStateCollapsed')) scrollToItem($placeholder);
|
||||||
|
$placeholder.children('.InputfieldHeader').effect('highlight', {}, 500);
|
||||||
|
// @todo the following line will need to be updated for matrix support
|
||||||
|
$item.parent('.Inputfields').siblings('.InputfieldRepeaterAddItem').find('.InputfieldRepeaterAddLink:eq(0)').click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event when mouseout of insert before/after action
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var eventInsertMouseout = function(e) {
|
||||||
|
if(currentlyAddingItem) return;
|
||||||
|
if(insertTimeout) clearTimeout(insertTimeout);
|
||||||
|
var $action = $(this);
|
||||||
|
var $newItem = $action.data('newItem');
|
||||||
|
$action.removeClass('hov');
|
||||||
|
// var $newItem = $action.closest('.Inputfields').children('.InputfieldRepeaterInsertItem');
|
||||||
|
if($newItem && $newItem.length) {
|
||||||
|
if($newItem.hasClass('hov')) return;
|
||||||
|
$newItem.remove();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event when mouseover of insert before/after action
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
var eventInsertMouseover = function(e) {
|
||||||
|
|
||||||
|
if(currentlyAddingItem) return;
|
||||||
|
if(insertTimeout) clearTimeout(insertTimeout);
|
||||||
|
|
||||||
|
var $action = $(this);
|
||||||
|
var insertBefore = $action.hasClass('InputfieldRepeaterInsertBefore');
|
||||||
|
var $item = $(this).closest('.InputfieldRepeaterItem');
|
||||||
|
var depth = 0;
|
||||||
|
|
||||||
|
$item.siblings('.InputfieldRepeaterInsertItem').remove();
|
||||||
|
|
||||||
|
var $newItem = $item.siblings('.InputfieldRepeaterNewItem').clone();
|
||||||
|
$newItem.addClass('InputfieldRepeaterInsertItem').attr('id', $newItem.attr('id') + '-insert');
|
||||||
|
|
||||||
|
if(insertBefore) {
|
||||||
|
depth = getInsertBeforeItemDepth($item);
|
||||||
|
$newItem.addClass('InputfieldRepeaterInsertItemBefore');//.insertBefore($item);
|
||||||
|
} else {
|
||||||
|
depth = getInsertAfterItemDepth($item);
|
||||||
|
$newItem.addClass('InputfieldRepeaterInsertItemAfter');//.insertAfter($item);
|
||||||
|
}
|
||||||
|
|
||||||
|
$newItem.find('.InputfieldRepeaterItemControls').hide();
|
||||||
|
$newItem.find('.InputfieldRepeaterItemLabel').text(ProcessWire.config.InputfieldRepeater.labels.insertHere);
|
||||||
|
|
||||||
|
$action.addClass('hov').data('newItem', $newItem);
|
||||||
|
|
||||||
|
setItemDepth($newItem, depth);
|
||||||
|
|
||||||
|
insertTimeout = setTimeout(function() {
|
||||||
|
insertTimeout = null;
|
||||||
|
if(!$action.hasClass('hov')) {
|
||||||
|
$newItem.remove();
|
||||||
|
return;
|
||||||
|
} else if(insertBefore) {
|
||||||
|
$newItem.insertBefore($item);
|
||||||
|
} else {
|
||||||
|
$newItem.addClass('hov').insertAfter($item);
|
||||||
|
}
|
||||||
|
//$newItem.addClass('hov');
|
||||||
|
$newItem.on('mouseover', function() {
|
||||||
|
$(this).addClass('hov');
|
||||||
|
}).on('click', function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
eventInsertClick($item, insertBefore);
|
||||||
|
}).on('mouseout', function() {
|
||||||
|
$(this).removeClass('hov').remove();
|
||||||
|
});
|
||||||
|
$newItem.slideDown();
|
||||||
|
}, 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*** GENERAL FUNCTIONS **********************************************************************************/
|
/*** GENERAL FUNCTIONS **********************************************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -487,7 +653,7 @@ function InputfieldRepeater($) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if(useScroll && hasOpen) {
|
if(useScroll && hasOpen) {
|
||||||
$('html, body').animate({scrollTop: $item.offset().top - 10}, 0);
|
scrollToItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -528,6 +694,25 @@ function InputfieldRepeater($) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** SORT FUNCTIONS ***********************************************************************************/
|
||||||
|
|
||||||
|
function setItemSort($item, sort) {
|
||||||
|
var $input = getItemSortInput($item);
|
||||||
|
if($input.length) $input.val(sort);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItemSort($item) {
|
||||||
|
var $input = getItemSortInput($item);
|
||||||
|
if($input.length) return parseInt($input.val());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getItemSortInput($item) {
|
||||||
|
if(!$item.hasClass('InputfieldRepeaterItem')) $item = $item.closest('.InputfieldRepeaterItem');
|
||||||
|
return $item.children('.InputfieldContent').children('.Inputfields')
|
||||||
|
.children('.InputfieldRepeaterItemSort').find('.InputfieldRepeaterSort');
|
||||||
|
}
|
||||||
|
|
||||||
/*** DEPTH FUNCTIONS **********************************************************************************/
|
/*** DEPTH FUNCTIONS **********************************************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -586,7 +771,7 @@ function InputfieldRepeater($) {
|
|||||||
var $depthInput = $item.children('.InputfieldContent').children('.Inputfields')
|
var $depthInput = $item.children('.InputfieldContent').children('.Inputfields')
|
||||||
.children('.InputfieldRepeaterItemDepth').find('input');
|
.children('.InputfieldRepeaterItemDepth').find('input');
|
||||||
|
|
||||||
if(!$depthInput.length) {
|
if(!$depthInput.length && !$item.hasClass('InputfieldRepeaterNewItem')) {
|
||||||
console.log('Cannot find depth input for ' + $item.attr('id'));
|
console.log('Cannot find depth input for ' + $item.attr('id'));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -625,6 +810,39 @@ function InputfieldRepeater($) {
|
|||||||
return parseInt($item.attr('data-depth'));
|
return parseInt($item.attr('data-depth'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get depth for a new item if it were to be inserted before/after given $contextItem
|
||||||
|
*
|
||||||
|
* @param $contextItem
|
||||||
|
* @param insertBefore
|
||||||
|
* @returns {Number}
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function getInsertItemDepth($contextItem, insertBefore) {
|
||||||
|
var depth = 0;
|
||||||
|
if(insertBefore) {
|
||||||
|
depth = getItemDepth($contextItem);
|
||||||
|
} else {
|
||||||
|
var $nextItem = $contextItem.next('.InputfieldRepeaterItem');
|
||||||
|
depth = getItemDepth($contextItem);
|
||||||
|
if($nextItem.hasClass('InputfieldRepeaterNewItem')) {
|
||||||
|
// the default hidden new item is not useful for identifying depth
|
||||||
|
if(!$nextItem.hasClass('InputfieldRepeaterInsertItem')) $nextItem = null;
|
||||||
|
}
|
||||||
|
var nextDepth = $nextItem && $nextItem.length ? getItemDepth($nextItem) : depth;
|
||||||
|
if(nextDepth > depth) depth = nextDepth;
|
||||||
|
}
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInsertBeforeItemDepth($item) {
|
||||||
|
return getInsertItemDepth($item, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInsertAfterItemDepth($item) {
|
||||||
|
return getInsertItemDepth($item, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all depth children for given repeater item
|
* Get all depth children for given repeater item
|
||||||
*
|
*
|
||||||
@@ -815,6 +1033,8 @@ function InputfieldRepeater($) {
|
|||||||
var $clone = $("<i class='fa fa-copy InputfieldRepeaterClone'></i>").css('display', 'block');
|
var $clone = $("<i class='fa fa-copy InputfieldRepeaterClone'></i>").css('display', 'block');
|
||||||
var $delete = $("<i class='fa fa-trash InputfieldRepeaterTrash'></i>");
|
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 $toggle = $("<i class='fa InputfieldRepeaterToggle' data-on='fa-toggle-on' data-off='fa-toggle-off'></i>");
|
||||||
|
var $insertAfter = $("<i class='fa fa-download xfa-arrow-circle-down InputfieldRepeaterInsertAfter'></i>");
|
||||||
|
var $insertBefore = $("<i class='fa fa-upload xfa-arrow-circle-up InputfieldRepeaterInsertBefore'></i>");
|
||||||
var cfg = ProcessWire.config.InputfieldRepeater;
|
var cfg = ProcessWire.config.InputfieldRepeater;
|
||||||
var allowClone = !$inputfieldRepeater.hasClass('InputfieldRepeaterNoAjaxAdd');
|
var allowClone = !$inputfieldRepeater.hasClass('InputfieldRepeaterNoAjaxAdd');
|
||||||
var allowSettings = $inputfieldRepeater.hasClass('InputfieldRepeaterHasSettings');
|
var allowSettings = $inputfieldRepeater.hasClass('InputfieldRepeaterHasSettings');
|
||||||
@@ -823,6 +1043,8 @@ function InputfieldRepeater($) {
|
|||||||
$toggle.attr('title', cfg.labels.toggle);
|
$toggle.attr('title', cfg.labels.toggle);
|
||||||
$delete.attr('title', cfg.labels.remove);
|
$delete.attr('title', cfg.labels.remove);
|
||||||
$clone.attr('title', cfg.labels.clone);
|
$clone.attr('title', cfg.labels.clone);
|
||||||
|
$insertBefore.attr('title', cfg.labels.insertBefore);
|
||||||
|
$insertAfter.attr('title', cfg.labels.insertAfter);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(allowSettings) {
|
if(allowSettings) {
|
||||||
@@ -836,7 +1058,7 @@ function InputfieldRepeater($) {
|
|||||||
var $item = $t.parent();
|
var $item = $t.parent();
|
||||||
if($item.hasClass('InputfieldRepeaterNewItem')) {
|
if($item.hasClass('InputfieldRepeaterNewItem')) {
|
||||||
// noAjaxAdd mode
|
// noAjaxAdd mode
|
||||||
icon = 'fa-plus';
|
icon = 'fa-plus-circle';
|
||||||
$t.addClass('ui-priority-secondary');
|
$t.addClass('ui-priority-secondary');
|
||||||
}
|
}
|
||||||
$t.addClass('ui-state-default InputfieldRepeaterHeaderInit');
|
$t.addClass('ui-state-default InputfieldRepeaterHeaderInit');
|
||||||
@@ -847,7 +1069,11 @@ function InputfieldRepeater($) {
|
|||||||
.addClass($t.parent().hasClass('InputfieldRepeaterOff') ? 'fa-toggle-off' : 'fa-toggle-on');
|
.addClass($t.parent().hasClass('InputfieldRepeaterOff') ? 'fa-toggle-off' : 'fa-toggle-on');
|
||||||
var $deleteControl = $delete.clone(true);
|
var $deleteControl = $delete.clone(true);
|
||||||
var $collapseControl = $t.find('.toggle-icon');
|
var $collapseControl = $t.find('.toggle-icon');
|
||||||
|
var $insertBeforeControl = $insertBefore.clone(true);
|
||||||
|
var $insertAfterControl = $insertAfter.clone(true);
|
||||||
$controls.prepend($collapseControl);
|
$controls.prepend($collapseControl);
|
||||||
|
$controls.prepend($insertBeforeControl);
|
||||||
|
$controls.prepend($insertAfterControl);
|
||||||
if($t.closest('.InputfieldRepeater').hasClass('InputfieldRepeaterHasSettings')) { // intentionally not using allowSettings var
|
if($t.closest('.InputfieldRepeater').hasClass('InputfieldRepeaterHasSettings')) { // intentionally not using allowSettings var
|
||||||
var $settingsToggle = $("<i class='fa fa-gear InputfieldRepeaterSettingsToggle ui-priority-secondary'></i>")
|
var $settingsToggle = $("<i class='fa fa-gear InputfieldRepeaterSettingsToggle ui-priority-secondary'></i>")
|
||||||
.attr('title', cfg.labels.settings);
|
.attr('title', cfg.labels.settings);
|
||||||
@@ -858,7 +1084,6 @@ function InputfieldRepeater($) {
|
|||||||
$controls.prepend($deleteControl);
|
$controls.prepend($deleteControl);
|
||||||
$t.prepend($controls);
|
$t.prepend($controls);
|
||||||
$controls.css('background-color', $t.css('background-color'));
|
$controls.css('background-color', $t.css('background-color'));
|
||||||
|
|
||||||
}
|
}
|
||||||
adjustItemLabel($item, false);
|
adjustItemLabel($item, false);
|
||||||
});
|
});
|
||||||
@@ -1013,6 +1238,15 @@ function InputfieldRepeater($) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scroll to repeater item
|
||||||
|
*
|
||||||
|
* @param $item
|
||||||
|
*/
|
||||||
|
function scrollToItem($item) {
|
||||||
|
$('html, body').animate({scrollTop: $item.offset().top - 10}, 250, 'swing');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run any scripts in the given HTML ajax data since jQuery will strip them
|
* Run any scripts in the given HTML ajax data since jQuery will strip them
|
||||||
*
|
*
|
||||||
@@ -1071,7 +1305,12 @@ function InputfieldRepeater($) {
|
|||||||
.on('click', '.InputfieldRepeaterToggle', eventToggleClick)
|
.on('click', '.InputfieldRepeaterToggle', eventToggleClick)
|
||||||
.on('opened', '.InputfieldRepeaterItem', eventItemOpened)
|
.on('opened', '.InputfieldRepeaterItem', eventItemOpened)
|
||||||
.on('closed', '.InputfieldRepeaterItem', eventItemClosed)
|
.on('closed', '.InputfieldRepeaterItem', eventItemClosed)
|
||||||
.on('openReady', '.InputfieldRepeaterItem', eventItemOpenReady);
|
.on('openReady', '.InputfieldRepeaterItem', eventItemOpenReady)
|
||||||
|
.on('click', '.InputfieldRepeaterInsertBefore', eventInsertBeforeClick)
|
||||||
|
.on('click', '.InputfieldRepeaterInsertAfter', eventInsertAfterClick)
|
||||||
|
.on('mouseover', '.InputfieldRepeaterInsertBefore', eventInsertMouseover)
|
||||||
|
.on('mouseover', '.InputfieldRepeaterInsertAfter', eventInsertMouseover)
|
||||||
|
.on('mouseout', '.InputfieldRepeaterInsertAfter', eventInsertMouseout);
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init();
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -26,7 +26,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' => 107,
|
'version' => 108,
|
||||||
'requires' => 'FieldtypeRepeater',
|
'requires' => 'FieldtypeRepeater',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -112,8 +112,9 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
*/
|
*/
|
||||||
public function init() {
|
public function init() {
|
||||||
parent::init();
|
parent::init();
|
||||||
if(is_null($this->page)) $this->page = $this->wire('pages')->newNullPage();
|
$pages = $this->wire()->pages;
|
||||||
$this->attr('value', $this->wire('pages')->newPageArray());
|
if(is_null($this->page)) $this->page = $pages->newNullPage();
|
||||||
|
$this->attr('value', $pages->newPageArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,11 +129,11 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
public function ___renderRepeaterLabel($label, $cnt, Page $page) {
|
public function ___renderRepeaterLabel($label, $cnt, Page $page) {
|
||||||
|
|
||||||
// situations where we skip the render of repeater label because it is not needed
|
// situations where we skip the render of repeater label because it is not needed
|
||||||
$repeaterEditID = (int) $this->wire('input')->get('repeater_edit');
|
$repeaterEditID = (int) $this->wire()->input->get('repeater_edit');
|
||||||
if($repeaterEditID && $repeaterEditID == $page->id) {
|
if($repeaterEditID && $repeaterEditID === $page->id) {
|
||||||
// edit of item requested in URL that matches given $page
|
// edit of item requested in URL that matches given $page
|
||||||
return $label;
|
return $label;
|
||||||
} else if(count($_POST) && !$this->wire('config')->ajax) {
|
} else if(count($_POST) && !$this->wire()->config->ajax) {
|
||||||
// POST request that is not ajax
|
// POST request that is not ajax
|
||||||
return $label;
|
return $label;
|
||||||
}
|
}
|
||||||
@@ -154,7 +155,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
// formatted {label}
|
// formatted {label}
|
||||||
$out = $page->getMarkup($repeaterTitle);
|
$out = $page->getMarkup($repeaterTitle);
|
||||||
|
|
||||||
} else if(!$hasCnt && $this->wire('sanitizer')->fieldName($repeaterTitle) === $repeaterTitle) {
|
} else if(!$hasCnt && $this->wire()->sanitizer->fieldName($repeaterTitle) === $repeaterTitle) {
|
||||||
// just a single field name
|
// just a single field name
|
||||||
$value = $page->getFormatted($repeaterTitle);
|
$value = $page->getFormatted($repeaterTitle);
|
||||||
if(is_object($value)) {
|
if(is_object($value)) {
|
||||||
@@ -322,6 +323,10 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
*/
|
*/
|
||||||
protected function buildForm($itemID = 0, $loadInputsForIDs = null) {
|
protected function buildForm($itemID = 0, $loadInputsForIDs = null) {
|
||||||
|
|
||||||
|
$input = $this->wire()->input;
|
||||||
|
$session = $this->wire()->session;
|
||||||
|
$modules = $this->wire()->modules;
|
||||||
|
|
||||||
// if it's already been built, then return the cached version
|
// if it's already been built, then return the cached version
|
||||||
if(!is_null($this->form)) return $this->form;
|
if(!is_null($this->form)) return $this->form;
|
||||||
|
|
||||||
@@ -352,12 +357,12 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
$openIDs = array();
|
$openIDs = array();
|
||||||
if((int) $this->field->get('rememberOpen')) {
|
if((int) $this->field->get('rememberOpen')) {
|
||||||
$this->addClass('InputfieldRepeaterRememberOpen', 'wrapClass');
|
$this->addClass('InputfieldRepeaterRememberOpen', 'wrapClass');
|
||||||
$openIDs = $this->wire('input')->cookie('repeaters_open');
|
$openIDs = $input->cookie('repeaters_open');
|
||||||
if($openIDs) $openIDs = explode('|', trim($openIDs, '|'));
|
if($openIDs) $openIDs = explode('|', trim($openIDs, '|'));
|
||||||
if(!is_array($openIDs)) $openIDs = array();
|
if(!is_array($openIDs)) $openIDs = array();
|
||||||
}
|
}
|
||||||
// merge with any open IDs in session
|
// merge with any open IDs in session
|
||||||
$_openIDs = $this->wire('session')->getFor($this, 'openIDs');
|
$_openIDs = $session->getFor($this, 'openIDs');
|
||||||
if(is_array($_openIDs) && !empty($_openIDs)) {
|
if(is_array($_openIDs) && !empty($_openIDs)) {
|
||||||
$openIDs = array_merge($openIDs, array_values($_openIDs));
|
$openIDs = array_merge($openIDs, array_values($_openIDs));
|
||||||
}
|
}
|
||||||
@@ -378,7 +383,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
$cnt = 0;
|
$cnt = 0;
|
||||||
$numVisible = 0;
|
$numVisible = 0;
|
||||||
$numOpen = 0;
|
$numOpen = 0;
|
||||||
$isPost = $this->wire('input')->requestMethod('POST');
|
$isPost = $input->requestMethod('POST');
|
||||||
$isSingle = $this->singleMode;
|
$isSingle = $this->singleMode;
|
||||||
|
|
||||||
// create field for each repeater iteration
|
// create field for each repeater iteration
|
||||||
@@ -413,20 +418,24 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
$depth = null;
|
$depth = null;
|
||||||
} else {
|
} else {
|
||||||
// also add a delete checkbox to the repeater page fields
|
// also add a delete checkbox to the repeater page fields
|
||||||
$delete = $this->wire('modules')->get('InputfieldCheckbox');
|
/** @var InputfieldCheckbox $delete */
|
||||||
|
$delete = $modules->get('InputfieldCheckbox');
|
||||||
$delete->attr('id+name', "delete_repeater{$page->id}");
|
$delete->attr('id+name', "delete_repeater{$page->id}");
|
||||||
$delete->addClass('InputfieldRepeaterDelete', 'wrapClass');
|
$delete->addClass('InputfieldRepeaterDelete', 'wrapClass');
|
||||||
$delete->label = $this->_('Delete');
|
$delete->label = $this->_('Delete');
|
||||||
$delete->attr('value', $page->id);
|
$delete->attr('value', $page->id);
|
||||||
|
|
||||||
$sort = $this->wire('modules')->get('InputfieldHidden');
|
/** @var InputfieldHidden $sort */
|
||||||
|
$sort = $modules->get('InputfieldHidden');
|
||||||
$sort->attr('id+name', "sort_repeater{$page->id}");
|
$sort->attr('id+name', "sort_repeater{$page->id}");
|
||||||
$sort->class = 'InputfieldRepeaterSort';
|
$sort->class = 'InputfieldRepeaterSort';
|
||||||
|
$sort->addClass('InputfieldRepeaterItemSort', 'wrapClass');
|
||||||
$sort->label = $this->_('Sort');
|
$sort->label = $this->_('Sort');
|
||||||
$sort->attr('value', $cnt);
|
$sort->attr('value', $cnt);
|
||||||
|
|
||||||
if($this->repeaterDepth > 0) {
|
if($this->repeaterDepth > 0) {
|
||||||
$depth = $this->wire('modules')->get('InputfieldHidden');
|
/** @var InputfieldHidden $depth */
|
||||||
|
$depth = $modules->get('InputfieldHidden');
|
||||||
$depth->attr('id+name', "depth_repeater{$page->id}");
|
$depth->attr('id+name', "depth_repeater{$page->id}");
|
||||||
$depth->addClass('InputfieldRepeaterItemDepth', 'wrapClass');
|
$depth->addClass('InputfieldRepeaterItemDepth', 'wrapClass');
|
||||||
$depth->label = $this->_('Depth');
|
$depth->label = $this->_('Depth');
|
||||||
@@ -438,13 +447,15 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$loaded = $this->wire('modules')->get('InputfieldHidden');
|
/** @var InputfieldHidden $loaded */
|
||||||
|
$loaded = $modules->get('InputfieldHidden');
|
||||||
$loaded->attr('id+name', "loaded_repeater{$page->id}");
|
$loaded->attr('id+name', "loaded_repeater{$page->id}");
|
||||||
$loaded->attr('value', $isLoaded ? 1 : 0);
|
$loaded->attr('value', $isLoaded ? 1 : 0);
|
||||||
$loaded->set('renderValueAsInput', true);
|
$loaded->set('renderValueAsInput', true);
|
||||||
$loaded->class = 'InputfieldRepeaterLoaded';
|
$loaded->class = 'InputfieldRepeaterLoaded';
|
||||||
|
|
||||||
$wrap = $this->wire('modules')->get('InputfieldFieldset');
|
/** @var InputfieldFieldset $wrap */
|
||||||
|
$wrap = $modules->get('InputfieldFieldset');
|
||||||
$wrap->addClass('InputfieldRepeaterItem InputfieldNoFocus');
|
$wrap->addClass('InputfieldRepeaterItem InputfieldNoFocus');
|
||||||
if(!$isPost) {
|
if(!$isPost) {
|
||||||
$wrap->entityEncodeLabel = false;
|
$wrap->entityEncodeLabel = false;
|
||||||
@@ -495,7 +506,8 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
if(!$isSingle) {
|
if(!$isSingle) {
|
||||||
// add a hidden field that will be populated with a positive value for all visible repeater items
|
// add a hidden field that will be populated with a positive value for all visible repeater items
|
||||||
// this is so that processInput can see this item should be a published item
|
// this is so that processInput can see this item should be a published item
|
||||||
$f = $this->wire('modules')->get('InputfieldHidden');
|
/** @var InputfieldHidden $f */
|
||||||
|
$f = $modules->get('InputfieldHidden');
|
||||||
$f->attr('name', "publish_repeater{$page->id}");
|
$f->attr('name', "publish_repeater{$page->id}");
|
||||||
$f->attr('class', 'InputfieldRepeaterPublish');
|
$f->attr('class', 'InputfieldRepeaterPublish');
|
||||||
|
|
||||||
@@ -547,7 +559,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
foreach($form->getAll() as $inputfield) {
|
foreach($form->getAll() as $inputfield) {
|
||||||
$idAttr = $inputfield->attr('id');
|
$idAttr = $inputfield->attr('id');
|
||||||
$this->renderReadyInputfield($inputfield, $form, $this->renderValueMode);
|
$this->renderReadyInputfield($inputfield, $form, $this->renderValueMode);
|
||||||
$jsValue = $this->wire('config')->js($idAttr);
|
$jsValue = $this->wire()->config->js($idAttr);
|
||||||
if(!empty($jsValue)) {
|
if(!empty($jsValue)) {
|
||||||
$inputfield->appendMarkup .= "<script>ProcessWire.config['$idAttr'] = " . json_encode($jsValue) . ';</script>';
|
$inputfield->appendMarkup .= "<script>ProcessWire.config['$idAttr'] = " . json_encode($jsValue) . ';</script>';
|
||||||
}
|
}
|
||||||
@@ -555,9 +567,12 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
} else if(!$isSingle) {
|
} else if(!$isSingle) {
|
||||||
// create a new/blank item to be used as a template for any new items added
|
// create a new/blank item to be used as a template for any new items added
|
||||||
/** @var InputfieldWrapper $wrap */
|
/** @var InputfieldWrapper $wrap */
|
||||||
$wrap = $this->wire('modules')->get('InputfieldFieldset');
|
$wrap = $modules->get('InputfieldFieldset');
|
||||||
$wrap->label = $this->renderRepeaterLabel($label, ++$cnt, new NullPage());
|
$wrap->entityEncodeLabel = false;
|
||||||
|
$label = $this->entityEncode($this->renderRepeaterLabel($label, ++$cnt, new NullPage()));
|
||||||
|
$wrap->label = "<span class='InputfieldRepeaterItemLabel'>$label</span>";
|
||||||
$wrap->class = 'InputfieldRepeaterItem InputfieldRepeaterNewItem';
|
$wrap->class = 'InputfieldRepeaterItem InputfieldRepeaterNewItem';
|
||||||
|
$wrap->attr('data-depth', 0);
|
||||||
$wrap->collapsed = Inputfield::collapsedNo;
|
$wrap->collapsed = Inputfield::collapsedNo;
|
||||||
$form->add($wrap);
|
$form->add($wrap);
|
||||||
}
|
}
|
||||||
@@ -618,7 +633,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
if($clonePage && $clonePage->id) {
|
if($clonePage && $clonePage->id) {
|
||||||
/** @var FieldtypeRepeater $fieldtype */
|
/** @var FieldtypeRepeater $fieldtype */
|
||||||
$fieldtype = $this->field->type;
|
$fieldtype = $this->field->type;
|
||||||
$readyPage = $this->wire('pages')->clone($clonePage, null, true,
|
$readyPage = $this->wire()->pages->clone($clonePage, null, true,
|
||||||
array('set' => array(
|
array('set' => array(
|
||||||
'name' => $fieldtype->getUniqueRepeaterPageName() . 'c', // trailing "c" indicates clone
|
'name' => $fieldtype->getUniqueRepeaterPageName() . 'c', // trailing "c" indicates clone
|
||||||
'sort' => count($value)+1,
|
'sort' => count($value)+1,
|
||||||
@@ -628,7 +643,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
);
|
);
|
||||||
$readyPage->set('_repeater_clone', $clonePage->id);
|
$readyPage->set('_repeater_clone', $clonePage->id);
|
||||||
} else if(!$cloneItemID) {
|
} else if(!$cloneItemID) {
|
||||||
$notIDs = $this->wire('sanitizer')->intArray(explode(',', trim($this->wire('input')->get('repeater_not'), ',')));
|
$notIDs = $this->wire()->sanitizer->intArray(explode(',', trim($this->wire()->input->get('repeater_not'), ',')));
|
||||||
$readyPage = $this->getNextReadyPage($notIDs);
|
$readyPage = $this->getNextReadyPage($notIDs);
|
||||||
$readyPage->removeStatus(Page::statusHidden);
|
$readyPage->removeStatus(Page::statusHidden);
|
||||||
}
|
}
|
||||||
@@ -652,8 +667,8 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
*/
|
*/
|
||||||
protected function renderAddLabel() {
|
protected function renderAddLabel() {
|
||||||
$addLabel = $this->field->get('repeaterAddLabel');
|
$addLabel = $this->field->get('repeaterAddLabel');
|
||||||
if($this->wire('languages')) {
|
if($this->wire()->languages) {
|
||||||
$language = $this->wire('user')->language;
|
$language = $this->wire()->user->language;
|
||||||
if(!$language->isDefault()) {
|
if(!$language->isDefault()) {
|
||||||
$addLabel = $this->field->get("repeaterAddLabel$language");
|
$addLabel = $this->field->get("repeaterAddLabel$language");
|
||||||
}
|
}
|
||||||
@@ -672,11 +687,17 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
*/
|
*/
|
||||||
public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
|
public function renderReady(Inputfield $parent = null, $renderValueMode = false) {
|
||||||
|
|
||||||
/** @var User $user */
|
$user = $this->wire()->user;
|
||||||
$user = $this->wire('user');
|
$modules = $this->wire()->modules;
|
||||||
|
|
||||||
|
/** @var JqueryCore $jQueryCore */
|
||||||
|
$jQueryCore = $modules->get('JqueryCore');
|
||||||
|
$jQueryCore->use('cookie');
|
||||||
|
|
||||||
|
/** @var JqueryUI $jQueryUI */
|
||||||
|
$jQueryUI = $modules->get('JqueryUI');
|
||||||
|
$jQueryUI->use('vex');
|
||||||
|
|
||||||
$this->wire('modules')->get('JqueryCore')->use('cookie');
|
|
||||||
$this->wire('modules')->get('JqueryUI')->use('vex');
|
|
||||||
$this->preloadInputfieldAssets();
|
$this->preloadInputfieldAssets();
|
||||||
|
|
||||||
$min = (int) $this->repeaterMinItems;
|
$min = (int) $this->repeaterMinItems;
|
||||||
@@ -714,7 +735,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
|
|
||||||
if($queryString) {}
|
if($queryString) {}
|
||||||
|
|
||||||
$this->wire('config')->js('InputfieldRepeater', array(
|
$this->wire()->config->js('InputfieldRepeater', array(
|
||||||
'editorUrl' => $editorUrl,
|
'editorUrl' => $editorUrl,
|
||||||
'labels' => array(
|
'labels' => array(
|
||||||
'remove' => $this->_x('Click to delete this item, or double-click to delete all', 'repeater-item-action'),
|
'remove' => $this->_x('Click to delete this item, or double-click to delete all', 'repeater-item-action'),
|
||||||
@@ -724,6 +745,9 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
'settings' => $this->_x('Show settings?', 'repeater-item-action'),
|
'settings' => $this->_x('Show settings?', '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'),
|
||||||
|
'insertBefore' => $this->_x('Insert new item before this one', 'repeater-item-action'),
|
||||||
|
'insertAfter' => $this->_x('Insert new item after this one', 'repeater-item-action'),
|
||||||
|
'insertHere' => $this->_x('Insert new item here', 'repeater-item-action'),
|
||||||
'disabledMinMax' => $this->_('This action is disabled per min and/or max item settings.')
|
'disabledMinMax' => $this->_('This action is disabled per min and/or max item settings.')
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
@@ -761,11 +785,11 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
*/
|
*/
|
||||||
public function ___render() {
|
public function ___render() {
|
||||||
|
|
||||||
$input = $this->wire('input');
|
$input = $this->wire()->input;
|
||||||
$noAjaxAdd = $this->field->get('repeaterLoading') == FieldtypeRepeater::loadingOff ? '1' : '';
|
$noAjaxAdd = $this->field->get('repeaterLoading') == FieldtypeRepeater::loadingOff ? '1' : '';
|
||||||
$ajax = $this->wire('config')->ajax;
|
$ajax = $this->wire()->config->ajax;
|
||||||
|
|
||||||
if($ajax && $input->get('field') == $this->attr('name')) {
|
if($ajax && $input->get('field') === $this->attr('name')) {
|
||||||
$repeaterAdd = $input->get('repeater_add');
|
$repeaterAdd = $input->get('repeater_add');
|
||||||
$repeaterEdit = (int) $input->get('repeater_edit');
|
$repeaterEdit = (int) $input->get('repeater_edit');
|
||||||
$repeaterClone = (int) $input->get('repeater_clone');
|
$repeaterClone = (int) $input->get('repeater_clone');
|
||||||
@@ -841,7 +865,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
$sortChanged = false;
|
$sortChanged = false;
|
||||||
$value->setTrackChanges(true);
|
$value->setTrackChanges(true);
|
||||||
$pageIDs = array();
|
$pageIDs = array();
|
||||||
$_openIDs = $this->wire('session')->getFor($this, 'openIDs');
|
$_openIDs = $this->wire()->session->getFor($this, 'openIDs');
|
||||||
if(!is_array($_openIDs)) $_openIDs = array();
|
if(!is_array($_openIDs)) $_openIDs = array();
|
||||||
$openIDs = $_openIDs; // these two are compared with each other at the end
|
$openIDs = $_openIDs; // these two are compared with each other at the end
|
||||||
$this->numRequiredEmpty = 0;
|
$this->numRequiredEmpty = 0;
|
||||||
@@ -962,7 +986,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if openIDs value changed, update the session variable
|
// if openIDs value changed, update the session variable
|
||||||
if($_openIDs !== $openIDs) $this->wire('session')->setFor($this, 'openIDs', $openIDs);
|
if($_openIDs !== $openIDs) $this->wire()->session->setFor($this, 'openIDs', $openIDs);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@@ -979,7 +1003,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
*/
|
*/
|
||||||
protected function formToPage(InputfieldWrapper $wrapper, Page $page, $level = 0) {
|
protected function formToPage(InputfieldWrapper $wrapper, Page $page, $level = 0) {
|
||||||
|
|
||||||
$languages = $this->wire('languages');
|
$languages = $this->wire()->languages;
|
||||||
|
|
||||||
foreach($wrapper as $inputfield) {
|
foreach($wrapper as $inputfield) {
|
||||||
|
|
||||||
@@ -1064,9 +1088,13 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function set($key, $value) {
|
public function set($key, $value) {
|
||||||
if($key == 'page') $this->page = $value;
|
if($key === 'page') {
|
||||||
else if($key == 'field') $this->field = $value;
|
$this->page = $value;
|
||||||
else return parent::set($key, $value);
|
} else if($key === 'field') {
|
||||||
|
$this->field = $value;
|
||||||
|
} else {
|
||||||
|
return parent::set($key, $value);
|
||||||
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,9 +42,14 @@
|
|||||||
.InputfieldRepeaterClone,
|
.InputfieldRepeaterClone,
|
||||||
.InputfieldRepeaterToggle,
|
.InputfieldRepeaterToggle,
|
||||||
.InputfieldRepeaterTrash,
|
.InputfieldRepeaterTrash,
|
||||||
|
.InputfieldRepeaterInsertBefore,
|
||||||
|
.InputfieldRepeaterInsertAfter,
|
||||||
.toggle-icon {
|
.toggle-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
float: right;
|
float: right;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.InputfieldRepeaterTrash {
|
.InputfieldRepeaterTrash {
|
||||||
padding-right: 3px;
|
padding-right: 3px;
|
||||||
@@ -55,6 +60,12 @@
|
|||||||
.InputfieldRepeaterClone {
|
.InputfieldRepeaterClone {
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
|
.InputfieldRepeaterInsertBefore {
|
||||||
|
margin-right: 0.75em;
|
||||||
|
}
|
||||||
|
.InputfieldRepeaterInsertAfter {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
.InputfieldRepeaterSettingsToggle {
|
.InputfieldRepeaterSettingsToggle {
|
||||||
margin-right: 1em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
@@ -71,6 +82,10 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > .InputfieldContent > .Inputfields > .InputfieldRepeaterInsertItem {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
.InputfieldStateCollapsed .InputfieldRepeaterSettingsToggle {
|
.InputfieldStateCollapsed .InputfieldRepeaterSettingsToggle {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
/**
|
/**
|
||||||
* RepeaterPage represents an individual repeater page item
|
* RepeaterPage represents an individual repeater page item
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2020 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -114,7 +114,7 @@ class RepeaterPage extends Page {
|
|||||||
$grandparentName = $grandparent->name;
|
$grandparentName = $grandparent->name;
|
||||||
$prefix = FieldtypeRepeater::fieldPageNamePrefix; // for-field-
|
$prefix = FieldtypeRepeater::fieldPageNamePrefix; // for-field-
|
||||||
$forField = null;
|
$forField = null;
|
||||||
$fields = $this->wire('fields'); /** @var Fields $fields */
|
$fields = $this->wire()->fields;
|
||||||
|
|
||||||
if(strpos($grandparentName, $prefix) === 0) {
|
if(strpos($grandparentName, $prefix) === 0) {
|
||||||
// determine field from grandparent name in format: for-field-1234
|
// determine field from grandparent name in format: for-field-1234
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
*
|
*
|
||||||
* Special PageArray for use by repeaters that includes a getNewItem() method
|
* Special PageArray for use by repeaters that includes a getNewItem() method
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -28,15 +28,49 @@ class RepeaterPageArray extends PageArray {
|
|||||||
*/
|
*/
|
||||||
protected $field = null;
|
protected $field = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct
|
||||||
|
*
|
||||||
|
* @param Page $parent
|
||||||
|
* @param Field $field
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function __construct(Page $parent, Field $field) {
|
public function __construct(Page $parent, Field $field) {
|
||||||
$this->setParent($parent);
|
$this->setParent($parent);
|
||||||
$this->setField($field);
|
$this->setField($field);
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set parent
|
||||||
|
*
|
||||||
|
* @param Page $parent
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function setParent(Page $parent) { $this->parent = $parent; }
|
public function setParent(Page $parent) { $this->parent = $parent; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get parent
|
||||||
|
*
|
||||||
|
* @return Page
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function getParent() { return $this->parent; }
|
public function getParent() { return $this->parent; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set field
|
||||||
|
*
|
||||||
|
* @param Field $field
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function setField(Field $field) { $this->field = $field; }
|
public function setField(Field $field) { $this->field = $field; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get field
|
||||||
|
*
|
||||||
|
* @return Field
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function getField() { return $this->field; }
|
public function getField() { return $this->field; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -132,6 +166,12 @@ class RepeaterPageArray extends PageArray {
|
|||||||
parent::trackRemove($item, $key);
|
parent::trackRemove($item, $key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debug info
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*
|
||||||
|
*/
|
||||||
public function __debugInfo() {
|
public function __debugInfo() {
|
||||||
$info = array(
|
$info = array(
|
||||||
'field' => $this->field ? $this->field->debugInfoSmall() : '',
|
'field' => $this->field ? $this->field->debugInfoSmall() : '',
|
||||||
|
Reference in New Issue
Block a user