1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-09 16:26:59 +02:00

Repeater updates continued with a complete refactoring of the InputfieldRepeater.js file. This update also corrects the depth-dragging issues that were present in the last commit (issues were especially noticable dragging when using AdminThemeReno). Also adds a repeater open all/close all function per processwire/processwire-requests#33 - to use it, double click the "on/off" toggle icon that appears next to the trash icon.

This commit is contained in:
Ryan Cramer
2016-12-08 14:02:43 -05:00
parent d92674fd4a
commit 6027e87a5e
9 changed files with 816 additions and 541 deletions

View File

@@ -69,7 +69,7 @@
* @property InputfieldWrapper|null $parent The parent InputfieldWrapper for this Inputfield or null if not set. #pw-internal * @property InputfieldWrapper|null $parent The parent InputfieldWrapper for this Inputfield or null if not set. #pw-internal
* @property null|bool|Fieldtype $hasFieldtype The Fieldtype using this Inputfield, or boolean false when known not to have a Fieldtype, or null when not known. #pw-group-other * @property null|bool|Fieldtype $hasFieldtype The Fieldtype using this Inputfield, or boolean false when known not to have a Fieldtype, or null when not known. #pw-group-other
* @property bool|null $useLanguages When multi-language support active, can be set to true to make it provide inputs for each language, where supported (default=false). #pw-group-behavior * @property bool|null $useLanguages When multi-language support active, can be set to true to make it provide inputs for each language, where supported (default=false). #pw-group-behavior
* @property null|bool $entityEncodeLabel Set to boolean false to specifically disable entity encoding of field header/label (default=true). #pw-group-output * @property null|bool|int $entityEncodeLabel Set to boolean false to specifically disable entity encoding of field header/label, or set to a Inputfield::textFormat constant. (default=true). #pw-group-output
* @property null|bool $entityEncodeText Set to boolean false to specifically disable entity encoding for other text: description, notes, etc. (default=true). #pw-group-output * @property null|bool $entityEncodeText Set to boolean false to specifically disable entity encoding for other text: description, notes, etc. (default=true). #pw-group-output
* @property int $renderValueFlags Options that can be applied to renderValue mode, see "renderValue" constants (default=0). #pw-group-output * @property int $renderValueFlags Options that can be applied to renderValue mode, see "renderValue" constants (default=0). #pw-group-output
* @property string $wrapClass Optional class name (CSS) to apply to the HTML element wrapping the Inputfield. #pw-group-other * @property string $wrapClass Optional class name (CSS) to apply to the HTML element wrapping the Inputfield. #pw-group-other

View File

@@ -504,7 +504,13 @@ class InputfieldWrapper extends Inputfield implements \Countable, \IteratorAggre
if($label || $quietMode) { if($label || $quietMode) {
$for = $inputfield->getSetting('skipLabel') || $quietMode ? '' : $inputfield->attr('id'); $for = $inputfield->getSetting('skipLabel') || $quietMode ? '' : $inputfield->attr('id');
// if $inputfield has a property of entityEncodeLabel with a value of boolean FALSE, we don't entity encode // if $inputfield has a property of entityEncodeLabel with a value of boolean FALSE, we don't entity encode
if($inputfield->getSetting('entityEncodeLabel') !== false) $label = $inputfield->entityEncode($label); $entityEncodeLabel = $inputfield->getSetting('entityEncodeLabel');
if(is_int($entityEncodeLabel) && $entityEncodeLabel >= Inputfield::textFormatBasic) {
// uses an Inputfield::textFormat constant
$label = $inputfield->entityEncode($label, $entityEncodeLabel);
} else if($entityEncodeLabel !== false) {
$label = $inputfield->entityEncode($label);
}
$icon = $inputfield->getSetting('icon'); $icon = $inputfield->getSetting('icon');
$icon = $icon ? str_replace('{name}', $this->wire('sanitizer')->name(str_replace(array('icon-', 'fa-'), '', $icon)), $markup['item_icon']) : ''; $icon = $icon ? str_replace('{name}', $this->wire('sanitizer')->name(str_replace(array('icon-', 'fa-'), '', $icon)), $markup['item_icon']) : '';
$toggle = $collapsed == Inputfield::collapsedNever ? '' : $markup['item_toggle']; $toggle = $collapsed == Inputfield::collapsedNever ? '' : $markup['item_toggle'];

View File

@@ -2,19 +2,39 @@
margin-bottom: 1em; } margin-bottom: 1em; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader { .Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader {
line-height: 1em; line-height: 1em;
padding: 0.5em; } padding: 0.5em 0 0.5em 0.4em;
white-space: nowrap;
overflow: hidden;
position: relative; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemLabel {
display: inline-block;
padding-left: 0.25em; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls {
padding-right: 0.5em;
padding-left: 0.5em;
margin-top: 0.5em;
position: absolute;
top: 0;
right: 0;
z-index: 1;
display: block;
white-space: nowrap;
height: 100%; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterClone,
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterToggle,
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterTrash,
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .toggle-icon {
cursor: pointer;
float: right; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterTrash {
padding-right: 3px; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterToggle {
margin-right: 1em; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .InputfieldRepeaterItemControls .InputfieldRepeaterClone {
margin-right: 1em; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .toggle-icon { .Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader .toggle-icon {
line-height: 1em; line-height: 1em;
margin-right: 0.5em; } margin-right: 0.5em; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader > .InputfieldRepeaterClone, .Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader > .InputfieldRepeaterToggle, .Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader > .InputfieldRepeaterTrash {
cursor: pointer;
float: right; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader > .InputfieldRepeaterTrash {
padding-right: 3px; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader > .InputfieldRepeaterToggle {
margin-right: 1em; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem > .InputfieldHeader > .InputfieldRepeaterClone {
margin-right: 1em; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldStateCollapsed > .InputfieldHeader { .Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldStateCollapsed > .InputfieldHeader {
opacity: 0.90; } opacity: 0.90; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldStateCollapsed > .InputfieldHeader:hover { .Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldStateCollapsed > .InputfieldHeader:hover {
@@ -23,7 +43,9 @@
opacity: 0.7; } opacity: 0.7; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldRepeaterOff > .InputfieldHeader { .Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldRepeaterOff > .InputfieldHeader {
opacity: 0.5; } opacity: 0.5; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldRepeaterOff > .InputfieldHeader:not(:hover) { .Inputfields .InputfieldRepeater .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending).InputfieldRepeaterOff > .InputfieldHeader:not(:hover) > .InputfieldRepeaterItemLabel {
text-decoration: line-through; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem.InputfieldRepeaterDeletePending > .InputfieldHeader > .InputfieldRepeaterItemLabel {
text-decoration: line-through; } text-decoration: line-through; }
.Inputfields .InputfieldRepeater .InputfieldRepeaterItem.InputfieldRepeaterItemLoading { .Inputfields .InputfieldRepeater .InputfieldRepeaterItem.InputfieldRepeaterItemLoading {
margin-bottom: 1em; } margin-bottom: 1em; }
@@ -44,6 +66,7 @@
.InputfieldRepeater ul.Inputfields + .InputfieldRepeaterAddItem { .InputfieldRepeater ul.Inputfields + .InputfieldRepeaterAddItem {
margin-top: 0; } margin-top: 0; }
.InputfieldRepeater .InputfieldRepeaterDrag { .InputfieldRepeater .InputfieldRepeaterDrag {
display: inline-block;
cursor: ns-resize; cursor: ns-resize;
opacity: 0.7; opacity: 0.7;
line-height: 1em; } line-height: 1em; }

View File

@@ -3,62 +3,108 @@
* *
* Maintains a collection of fields that are repeated for any number of times. * Maintains a collection of fields that are repeated for any number of times.
* *
* ProcessWire 3.x (development), Copyright 2015 by Ryan Cramer * ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* https://processwire.com * https://processwire.com
* *
*/ */
var InputfieldRepeaterDepthSize = 50;
function InputfieldRepeater($) {
/** /**
* Delete click event (single item) * When depth is used, this indicates the indent (in pixels) used to show a single depth increment
*
* @type {number}
* *
*/ */
function InputfieldRepeaterDeleteClick(e) { var depthSize = 50;
var $parent = $(this).parent('label').parent('li'); /**
* Whether or not AdminThemeReno is present
*
* @type {bool}
*
*/
var isReno = $('body').hasClass('AdminThemeReno');
if($parent.is('.InputfieldRepeaterNewItem')) { /*** EVENTS ********************************************************************************************/
/**
* Event handler for when an .InputfieldRepeater "reloaded" event is triggered
*
* @param event
* @param source
*
*/
var eventReloaded = function(event, source) {
if(typeof source != "undefined") {
if(source == 'InputfieldRepeaterItemEdit' || source == 'InputfieldRepeaterItemAdd') {
event.stopPropagation();
var $r = $(this).find(".InputfieldRepeater");
if($r.length) initRepeater($r);
return;
}
}
initRepeater($(this));
};
/**
* Event handler for when the "delete" action is clicked
*
* @param e
*
*/
var eventDeleteClick = function(e) {
var $header = $(this).closest('.InputfieldHeader');
var $item = $header.parent();
if($item.hasClass('InputfieldRepeaterNewItem')) {
// delete new item (noAjaxAdd mode) // delete new item (noAjaxAdd mode)
var $numAddInput = $parent.parent().parent().find('.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
$parent.remove(); $item.remove();
} else { } else {
// delete existing item // delete existing item
var $checkbox = $parent.find('.InputfieldRepeaterDelete'); var pageID = $item.attr('data-page');
var $checkbox = $item.find('#delete_repeater' + pageID);
if($checkbox.is(":checked")) { if($checkbox.is(":checked")) {
$checkbox.removeAttr('checked'); $checkbox.removeAttr('checked');
$parent.children('label').removeClass('ui-state-error').addClass('ui-state-default'); $header.removeClass('ui-state-error').addClass('ui-state-default');
//if($parent.is('.InputfieldStateCollapsed')) $parent.toggleClass('InputfieldStateCollapsed', 100); //if($parent.is('.InputfieldStateCollapsed')) $parent.toggleClass('InputfieldStateCollapsed', 100);
$parent.removeClass('InputfieldRepeaterDeletePending'); $item.removeClass('InputfieldRepeaterDeletePending');
} else { } else {
$checkbox.attr('checked', 'checked'); $checkbox.attr('checked', 'checked');
$parent.children('label').removeClass('ui-state-default').addClass('ui-state-error'); $header.removeClass('ui-state-default').addClass('ui-state-error');
if(!$parent.hasClass('InputfieldStateCollapsed')) $parent.toggleClass('InputfieldStateCollapsed', 100); if(!$item.hasClass('InputfieldStateCollapsed')) {
$parent.addClass('InputfieldRepeaterDeletePending'); $header.find('.toggle-icon').click();
//$item.toggleClass('InputfieldStateCollapsed', 100);
} }
$item.addClass('InputfieldRepeaterDeletePending');
}
$header.find('.InputfieldRepeaterItemControls').css('background-color', $header.css('background-color'));
} }
InputfieldRepeaterCheckMax($parent.closest('.InputfieldRepeater')); checkMax($item.closest('.InputfieldRepeater'));
e.stopPropagation(); e.stopPropagation();
} };
/** /**
* Delete double-click event (multi-item) * Event handler for when the "delete" link is double clicked
*
* @param e
* *
*/ */
function InputfieldRepeaterDeleteDblClick(e) { var eventDeleteDblClick = function(e) {
var $li = $(this).closest('li'); var $li = $(this).closest('li');
var undelete = $li.hasClass('InputfieldRepeaterDeletePending'); var undelete = $li.hasClass('InputfieldRepeaterDeletePending');
function selectAll() { function selectAll() {
$li.parent().children('li').each(function() { $li.parent().children('li').each(function() {
var $item = $(this); var $item = $(this);
var $trashLink = $item.children('.InputfieldHeader').children('.InputfieldRepeaterTrash'); var $trashLink = $item.children('.InputfieldHeader').find('.InputfieldRepeaterTrash');
if($item.hasClass('InputfieldRepeaterDeletePending')) { if($item.hasClass('InputfieldRepeaterDeletePending')) {
if(undelete) $trashLink.click(); if(undelete) $trashLink.click();
} else { } else {
@@ -72,9 +118,16 @@ function InputfieldRepeaterDeleteDblClick(e) {
} else { } else {
ProcessWire.confirm(ProcessWire.config.InputfieldRepeater.labels.removeAll, selectAll); ProcessWire.confirm(ProcessWire.config.InputfieldRepeater.labels.removeAll, selectAll);
} }
} };
function InputfieldRepeaterCloneClick(e) { /**
* Event handler for when the "clone" repeater item action is clicked
*
* @param e
* @returns {boolean}
*
*/
var eventCloneClick = function(e) {
var $item = $(this).closest('.InputfieldRepeaterItem'); 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');
@@ -84,18 +137,21 @@ function InputfieldRepeaterCloneClick(e) {
$('html, body').animate({ scrollTop: $addLink.offset().top - 100}, 250, 'swing'); $('html, body').animate({ scrollTop: $addLink.offset().top - 100}, 250, 'swing');
}); });
return false; return false;
} };
/** /**
* Event handler for the "publish" toggle in the header of each repeater item * Event handler for when the repeater item "on/off" toggle is clicked
*
* @param e
* *
*/ */
function InputfieldRepeaterToggleClick(e) { var eventToggleClick = function(e) {
var $this = $(this); var $this = $(this);
var toggleOn = $this.attr('data-on'); var toggleOn = $this.attr('data-on');
var toggleOff = $this.attr('data-off'); var toggleOff = $this.attr('data-off');
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($this.hasClass(toggleOn)) {
$this.removeClass(toggleOn).addClass(toggleOff); $this.removeClass(toggleOn).addClass(toggleOff);
$item.addClass('InputfieldRepeaterUnpublished InputfieldRepeaterOff'); $item.addClass('InputfieldRepeaterUnpublished InputfieldRepeaterOff');
@@ -105,85 +161,34 @@ function InputfieldRepeaterToggleClick(e) {
$item.removeClass('InputfieldRepeaterUnpublished InputfieldRepeaterOff'); $item.removeClass('InputfieldRepeaterUnpublished InputfieldRepeaterOff');
$input.val('1'); $input.val('1');
} }
e.stopPropagation(); e.stopPropagation();
} };
/** /**
* Prepares for open of ajax loaded item (Inputfields "openReady" event handler) * Event handler for when a repeater item is about to be opened
*
* @param e
* *
*/ */
function InputfieldRepeaterItemOpenReady(e) { var eventItemOpenReady = function(e) {
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
$item.addClass('InputfieldRepeaterItemLoading'); $item.addClass('InputfieldRepeaterItemLoading');
} };
/** /**
* Remember which repeater items are open * Event handler for when a repeater item is opened (primarily focused on ajax loaded items)
*
* @param e
* *
*/ */
function InputfieldRepeaterUpdateState($item) { var eventItemOpened = function(e) {
if($item.closest('.InputfieldRepeaterRememberOpen').length) {
var val = '';
$(".InputfieldRepeaterItem:not(.InputfieldStateCollapsed)").each(function() {
var id = parseInt($(this).attr('data-page'));
if(id > 0) {
val += id + '|';
}
});
$.cookie('repeaters_open', val);
}
}
function InputfieldRepeaterCheckMax($inputfield) {
if(!$inputfield.hasClass('InputfieldRepeaterMax')) return;
var max = parseInt($inputfield.attr('data-max'));
if(max <= 0) return;
var $content = $inputfield.children('.InputfieldContent');
var num = $content.children('.Inputfields').children('li:not(.InputfieldRepeaterDeletePending)').length;
var $addItem = $content.children('.InputfieldRepeaterAddItem');
if(num > max) {
$addItem.hide();
} else if(!$addItem.is(":visible")) {
$addItem.show();
}
}
function InputfieldRepeaterCheckDepths($inputfield) {
$inputfield.find('.InputfieldRepeaterDepth').each(function() {
var $depth = $(this);
var depth = $depth.val();
var $item = $depth.closest('.InputfieldRepeaterItem');
var currentLeft = $item.css('margin-left');
if(currentLeft == 'auto') currentLeft = 0;
currentLeft = parseInt(currentLeft);
var targetLeft = depth * InputfieldRepeaterDepthSize;
if(targetLeft != currentLeft) {
$item.css('margin-left', targetLeft + 'px');
$item.data('lastLeft', targetLeft);
}
});
}
/**
* Event called when repeater item is collapsed
*
*/
function InputfieldRepeaterItemClosed(e) {
InputfieldRepeaterUpdateState($(this));
}
/**
* Handles load of ajax editable items (Inputfields "opened" event handler)
*
*/
function InputfieldRepeaterItemOpened(e) {
var $item = $(this); var $item = $(this);
var $loaded = $item.find(".InputfieldRepeaterLoaded"); var $loaded = $item.find(".InputfieldRepeaterLoaded");
InputfieldRepeaterUpdateState($item); updateState($item);
if(parseInt($loaded.val()) > 0) return; // item already loaded if(parseInt($loaded.val()) > 0) return; // item already loaded
@@ -213,7 +218,7 @@ function InputfieldRepeaterItemOpened(e) {
var $repeaters = $inputs.filter('.InputfieldRepeater'); var $repeaters = $inputs.filter('.InputfieldRepeater');
if($repeaters.length) $repeaters.each(function() { if($repeaters.length) $repeaters.each(function() {
InputfieldRepeaterInit($(this)); initRepeater($(this));
}); });
$content.slideDown('fast', function() { $content.slideDown('fast', function() {
@@ -224,206 +229,32 @@ function InputfieldRepeaterItemOpened(e) {
}, 50); }, 50);
}); });
} };
/** /**
* Update a repeater label for the given repeater $item, optionally incrementing the index number * Event handler for when a repeater item is closed
*
* @param e
* *
*/ */
function InputfieldRepeaterAdjustLabel($item, doIncrement) { var eventItemClosed = function(e) {
updateState($(this));
var $label = $item.children('label'); };
var labelHTML = $label.html();
var _labelHTML = labelHTML;
if(doIncrement && labelHTML.indexOf('#') > -1) {
num = $item.siblings('.InputfieldRepeaterItem:visible').length + 1;
labelHTML = labelHTML.replace(/#[0-9]+/, '#' + num);
}
if(labelHTML.indexOf('{') > -1) {
// 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>');
}
if(labelHTML != _labelHTML) {
$label.html(labelHTML);
}
}
/** /**
* Initialize a repeater field * Event handler for "add" link clicks
*
* Handles adding repeater items and initializing them
*
* @param e
* @returns {boolean}
* *
*/ */
function InputfieldRepeaterInit($this) { var eventAddLinkClick = function(e) {
if($this.hasClass('InputfieldRepeaterItem')) {
// single repeater item
var $inputfields = $this;
var $inputfieldRepeater = $this.closest('.InputfieldRepeater');
var isItem = true;
} else {
// enter repeater
var $inputfields = $this.find('.Inputfields:eq(0)');
var $inputfieldRepeater = $this;
var isItem = false;
}
//if(!$inputfields.length) return;
if($inputfields.hasClass('InputfieldRepeaterInit')) return;
$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();
function setupRepeaterHeaders($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));
$t.prepend($toggle.clone(true).addClass($t.parent().hasClass('InputfieldRepeaterOff') ? 'fa-toggle-off' : 'fa-toggle-on'));
$t.prepend($delete.clone(true));
}
InputfieldRepeaterAdjustLabel($item, false);
});
}
if(isItem) {
setupRepeaterHeaders($this.children('.InputfieldHeader'));
} else {
setupRepeaterHeaders($(".InputfieldRepeaterItem > .InputfieldHeader", $this));
}
if(renderValueMode) return;
$(".InputfieldRepeaterDrag", $this).hover(function() {
$(this).parent('label').addClass('ui-state-focus');
}, function() {
$(this).parent('label').removeClass('ui-state-focus');
});
$(".InputfieldRepeaterTrash", $this).hover(function() {
var $label = $(this).parent('label');
if(!$label.parent().is('.InputfieldRepeaterDeletePending')) $label.addClass('ui-state-error');
}, function() {
var $label = $(this).parent('label');
if(!$label.parent().is('.InputfieldRepeaterDeletePending')) $label.removeClass('ui-state-error');
});
if(isItem) {
// if we only init'd a single item, now make $inputfields refer to all repeater items for sortable init
$inputfields = $this.closest('.InputfieldRepeater').find('.Inputfields:eq(0)');
}
var sortableOptions = {
items: '> li:not(.InputfieldRepeaterNewItem)',
handle: '.InputfieldRepeaterDrag',
start: function(e, ui) {
ui.item.find('.InputfieldHeader').addClass("ui-state-highlight");
// CKEditor doesn't like being sorted, do destroy when sort starts, and reload after sort
ui.item.find('textarea.InputfieldCKEditorNormal.InputfieldCKEditorLoaded').each(function() {
$(this).removeClass('InputfieldCKEditorLoaded');
var editor = CKEDITOR.instances[$(this).attr('id')];
editor.destroy();
CKEDITOR.remove($(this).attr('id'));
});
// TinyMCE instances don't like to be dragged, so we disable them temporarily
ui.item.find('.InputfieldTinyMCE textarea').each(function() {
tinyMCE.execCommand('mceRemoveControl', false, $(this).attr('id'));
});
},
stop: function(e, ui) {
ui.item.find('.InputfieldHeader').removeClass("ui-state-highlight");
$(this).children().each(function(n) {
$(this).find('.InputfieldRepeaterSort').slice(0,1).attr('value', n);
});
// Re-enable CKEditor instances
ui.item.find('textarea.InputfieldCKEditorNormal:not(.InputfieldCKEditorLoaded)').each(function() {
$(this).closest('.InputfieldCKEditor').trigger('reloaded', [ 'InputfieldRepeaterSort' ]);
});
// Re-enable the TinyMCE instances
ui.item.find('.InputfieldTinyMCE textarea').each(function() {
tinyMCE.execCommand('mceAddControl', false, $(this).attr('id'));
});
}
};
var maxDepth = parseInt($inputfieldRepeater.attr('data-depth'));
if(maxDepth > 0) {
InputfieldRepeaterCheckDepths($inputfieldRepeater);
sortableOptions.grid = [ InputfieldRepeaterDepthSize, 1 ];
sortableOptions.beforeStop = function(event, ui) {
var lastLeft = ui.item.data('lastLeft');
if(!lastLeft) lastLeft = 0;
var left = lastLeft + ui.position.left;
left -= InputfieldRepeaterDepthSize / 2;
if(left > 25 && left < InputfieldRepeaterDepthSize) left = InputfieldRepeaterDepthSize;
var depth = Math.round(left / InputfieldRepeaterDepthSize);
if(depth < 1) depth = 0;
if(depth > maxDepth) depth = maxDepth;
if(depth) {
ui.item.css('margin-left', (depth * InputfieldRepeaterDepthSize) + 'px');
} else {
ui.item.css('margin-left', 0);
}
ui.item.find('.InputfieldRepeaterDepth').val(depth);
ui.item.data('lastLeft', left);
ui.item.children('.InputfieldHeader').removeClass('ui-state-error');
};
sortableOptions.sort = function(event, ui) {
var lastLeft = ui.item.data('lastLeft');
if(!lastLeft) lastLeft = 0;
var left = lastLeft + ui.position.left;
var $header = ui.item.children('.InputfieldHeader');
if(left > (InputfieldRepeaterDepthSize * maxDepth) + (InputfieldRepeaterDepthSize / 2)) {
// beyond max depth allowed
$header.addClass('ui-state-error');
} else if($header.hasClass('ui-state-error')) {
$header.removeClass('ui-state-error');
}
};
} else {
sortableOptions.axis = 'y';
}
$inputfields.sortable(sortableOptions);
var $addLinks = $(".InputfieldRepeaterAddLink:not(.InputfieldRepeaterAddLinkInit)", $this);
$addLinks.addClass('InputfieldRepeaterAddLinkInit');
$addLinks.click(function() {
var $addLink = $(this); var $addLink = $(this);
var $inputfields = $(this).parent('p').prev('ul.Inputfields'); var $inputfields = $addLink.parent('p').prev('ul.Inputfields');
var $numAddInput = $(this).parent().children('input'); var $inputfieldRepeater = $addLink.closest('.InputfieldRepeater');
var $numAddInput = $addLink.parent().children('input');
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');
@@ -435,8 +266,7 @@ function InputfieldRepeaterInit($this) {
$addItem.attr('id', id); $addItem.attr('id', id);
$inputfields.append($addItem); $inputfields.append($addItem);
$addItem.css('display', 'block'); $addItem.css('display', 'block');
//$addItem.find('.InputfieldRepeaterTrash').click(InputfieldRepeaterDeleteClick); adjustItemLabel($addItem, true);
InputfieldRepeaterAdjustLabel($addItem, true);
$addLink.trigger('repeateradd', [ $addItem ]); $addLink.trigger('repeateradd', [ $addItem ]);
} }
@@ -451,13 +281,14 @@ function InputfieldRepeaterInit($this) {
var $addItem = $newItem.clone(true) var $addItem = $newItem.clone(true)
addRepeaterItem($addItem); addRepeaterItem($addItem);
$numAddInput.attr('value', newItemTotal); $numAddInput.attr('value', newItemTotal);
InputfieldRepeaterCheckMax($inputfieldRepeater); checkMax($inputfieldRepeater);
}
return false;
} }
} else {
// get addItem from ajax // get addItem from ajax
var pageID = $addLink.closest('.InputfieldRepeater').attr('data-page'); // $("#Inputfield_id").val(); var pageID = $inputfieldRepeater.attr('data-page');
var fieldName = $addLink.closest('.InputfieldRepeater').attr('id').replace('wrap_Inputfield_', ''); var fieldName = $inputfieldRepeater.attr('id').replace('wrap_Inputfield_', '');
var $spinner = $addLink.parent().find('.InputfieldRepeaterSpinner'); var $spinner = $addLink.parent().find('.InputfieldRepeaterSpinner');
var ajaxURL = ProcessWire.config.InputfieldRepeater.editorUrl + '?id=' + pageID + '&field=' + fieldName; var ajaxURL = ProcessWire.config.InputfieldRepeater.editorUrl + '?id=' + pageID + '&field=' + fieldName;
@@ -484,65 +315,424 @@ function InputfieldRepeaterInit($this) {
var $addItem = $(data).find(".InputfieldRepeaterItemRequested"); var $addItem = $(data).find(".InputfieldRepeaterItemRequested");
if(!$addItem.length) { if(!$addItem.length) {
// error // error
// console.log("Can't find item: .InputfieldRepeaterItem.InputfieldRepeaterUnpublished");
return; return;
} }
addRepeaterItem($addItem); addRepeaterItem($addItem);
$addItem.wrap("<div />"); // wrap for inputfields.js $target $addItem.wrap("<div />"); // wrap for inputfields.js $target
InputfieldsInit($addItem.parent()); InputfieldsInit($addItem.parent());
InputfieldRepeaterInit($addItem); initRepeater($addItem);
$addItem.unwrap(); // unwrap div once item initialized $addItem.unwrap(); // unwrap div once item initialized
//$addItem.find('input.InputfieldRepeaterPublish').attr('value', 1);
$addItem.find('.Inputfield').trigger('reloaded', [ 'InputfieldRepeaterItemAdd' ]); $addItem.find('.Inputfield').trigger('reloaded', [ 'InputfieldRepeaterItemAdd' ]);
$addItem.find('.InputfieldRepeaterSort').val($inputfields.children().length); $addItem.find('.InputfieldRepeaterSort').val($inputfields.children().length);
$('html, body').animate({ $('html, body').animate({
scrollTop: $addItem.offset().top scrollTop: $addItem.offset().top
}, 500, 'swing'); }, 500, 'swing');
InputfieldRepeaterUpdateState($addItem); updateState($addItem);
InputfieldRepeaterCheckMax($inputfieldRepeater); checkMax($inputfieldRepeater);
$nestedRepeaters = $addItem.find('.InputfieldRepeater'); $nestedRepeaters = $addItem.find('.InputfieldRepeater');
if($nestedRepeaters.length) { if($nestedRepeaters.length) {
$nestedRepeaters.each(function() { $nestedRepeaters.each(function() {
InputfieldRepeaterInit($(this)); initRepeater($(this));
}); });
} }
}); });
}
return false; return false;
};
/**
* Event handler for the "open all" or "collapse all" functions
*
* @param e
* @returns {boolean}
*
*/
var eventOpenAllClick = function(e) {
e.stopPropagation();
e.preventDefault();
var $repeater = $(this).closest('.InputfieldRepeater');
var $items = $repeater.children('.InputfieldContent').children('.Inputfields').children('.InputfieldRepeaterItem');
if(!$items.length) return false;
var $item = $items.eq(0);
if($item.hasClass('InputfieldStateCollapsed')) {
var label = ProcessWire.config.InputfieldRepeater.labels.openAll;
var selector = '.InputfieldStateCollapsed';
} else {
var label = ProcessWire.config.InputfieldRepeater.labels.collapseAll;
var selector = '.InputfieldRepeaterItem:not(.InputfieldStateCollapsed)';
}
ProcessWire.confirm(label, function() {
$items.filter(selector).each(function() {
$(this).children('.InputfieldHeader').find('.toggle-icon').click();
}); });
});
return false;
};
//$(".InputfieldRepeaterUnpublished").children('.InputfieldHeader').addClass('ui-priority-secondary'); /*** GENERAL FUNCTIONS **********************************************************************************/
if($inputfieldRepeater.hasClass('InputfieldRepeaterMax')) InputfieldRepeaterCheckMax($inputfieldRepeater); /**
* Given an InputfieldRepeaterItem update the label consistent with any present formatting sting
*
* Primarily adjusts item count(s) and allowed for {secondary} text appearance
*
* @param $item An .InputfieldRepeaterItem
* @param bool doIncrement Specify true to increment the item count value (like for new items)
*
*/
function adjustItemLabel($item, doIncrement) {
var $label = $item.children('label');
var labelHTML = $label.html();
var _labelHTML = labelHTML;
if(doIncrement && labelHTML.indexOf('#') > -1) {
num = $item.siblings('.InputfieldRepeaterItem:visible').length + 1;
labelHTML = labelHTML.replace(/#[0-9]+/, '#' + num);
} }
$(document).ready(function() { if(labelHTML.indexOf('{') > -1) {
// 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>');
}
$(".InputfieldRepeater").each(function() { if(labelHTML != _labelHTML) {
InputfieldRepeaterInit($(this)); $label.html(labelHTML);
}
}
/**
* Determine the sortable depth of a repeater item and either return it or apply it
*
* @param ui The 'ui' argument provided to jQuery UI sortable events
* @param maxDepth Maximum allowed depth
* @param updateNow Specify true to apply the determined depth now or false to just return it.
* @returns {number} Depth integer value between 0 and maxDepth
*
*/
function sortableDepth(ui, maxDepth, updateNow) {
var $depth = ui.item.find('.InputfieldRepeaterDepth');
var depth = -1;
var prevDepth = parseInt($depth.val());
var left = ui.position.left;
// AdminThemeReno has something different going on with the left positions, so we adjust for that here
if(isReno) left -= depthSize;
if(left < 0) {
depth = prevDepth - Math.round(Math.abs(left) / depthSize);
// console.log('decrease depth to: ' + depth);
} else {
depth = Math.round(left / depthSize) + prevDepth;
// console.log('increase depth to: ' + depth);
}
if(depth < 1) {
depth = 0;
} else if(depth > maxDepth) {
depth = maxDepth;
}
if(updateNow) {
if(depth) {
ui.item.css('margin-left', (depth * depthSize) + 'px');
} else {
ui.item.css('margin-left', 0);
}
$depth.val(depth);
ui.item.children('.InputfieldHeader').removeClass('ui-state-error');
}
return depth;
}
/*** INIT FUNCTIONS **********************************************************************************/
/**
* Initialize repeater item depths
*
* Applies a left-margin to repeater items consistent with with value in
* each item's input.InputfieldRepeaterDepth hidden input.
*
* @param $inputfieldRepeater
*
*/
function initDepths($inputfieldRepeater) {
$inputfieldRepeater.find('.InputfieldRepeaterDepth').each(function() {
var $depth = $(this);
var depth = $depth.val();
var $item = $depth.closest('.InputfieldRepeaterItem');
var currentLeft = $item.css('margin-left');
if(currentLeft == 'auto') currentLeft = 0;
currentLeft = parseInt(currentLeft);
var targetLeft = depth * depthSize;
if(targetLeft != currentLeft) {
$item.css('margin-left', targetLeft + 'px');
}
});
}
/**
* Make a repeater sortable
*
* @param $inputfieldRepeater The parent .InputfieldRepeater
* @param $inputfields The .Inputfields parent of the sortable items
*
*/
function initSortable($inputfieldRepeater, $inputfields) {
var maxDepth = parseInt($inputfieldRepeater.attr('data-depth'));
var sortableOptions = {
items: '> li:not(.InputfieldRepeaterNewItem)',
handle: '.InputfieldRepeaterDrag',
start: function(e, ui) {
ui.item.find('.InputfieldHeader').addClass("ui-state-highlight");
// CKEditor doesn't like being sorted, do destroy when sort starts, and reload after sort
ui.item.find('textarea.InputfieldCKEditorNormal.InputfieldCKEditorLoaded').each(function() {
$(this).removeClass('InputfieldCKEditorLoaded');
var editor = CKEDITOR.instances[$(this).attr('id')];
editor.destroy();
CKEDITOR.remove($(this).attr('id'));
}); });
$(document).on('reloaded', '.InputfieldRepeater', function(event, source) { // TinyMCE instances don't like to be dragged, so we disable them temporarily
if(typeof source != "undefined") { ui.item.find('.InputfieldTinyMCE textarea').each(function() {
if(source == 'InputfieldRepeaterItemEdit' || source == 'InputfieldRepeaterItemAdd') { tinyMCE.execCommand('mceRemoveControl', false, $(this).attr('id'));
event.stopPropagation(); });
var $r = $(this).find(".InputfieldRepeater"); },
if($r.length) InputfieldRepeaterInit($r);
stop: function(e, ui) {
if(maxDepth > 0) {
sortableDepth(ui, maxDepth, true);
}
ui.item.find('.InputfieldHeader').removeClass("ui-state-highlight");
$(this).children().each(function(n) {
$(this).find('.InputfieldRepeaterSort').slice(0,1).attr('value', n);
});
// Re-enable CKEditor instances
ui.item.find('textarea.InputfieldCKEditorNormal:not(.InputfieldCKEditorLoaded)').each(function() {
$(this).closest('.InputfieldCKEditor').trigger('reloaded', [ 'InputfieldRepeaterSort' ]);
});
// Re-enable the TinyMCE instances
ui.item.find('.InputfieldTinyMCE textarea').each(function() {
tinyMCE.execCommand('mceAddControl', false, $(this).attr('id'));
});
}
};
if(maxDepth > 0) {
initDepths($inputfieldRepeater);
sortableOptions.grid = [ depthSize, 1 ];
sortableOptions.sort = function(event, ui) {
var depth = sortableDepth(ui, 99, false);
var $header = ui.item.children('.InputfieldHeader');
if(depth > maxDepth) {
// beyond max depth allowed
$header.addClass('ui-state-error');
} else if($header.hasClass('ui-state-error')) {
// no problems
$header.removeClass('ui-state-error');
}
};
} else {
sortableOptions.axis = 'y';
}
// apply "ui-state-focus" class when an item is being dragged
$(".InputfieldRepeaterDrag", $inputfields).hover(function() {
$(this).parent('label').addClass('ui-state-focus');
}, function() {
$(this).parent('label').removeClass('ui-state-focus');
});
$inputfields.sortable(sortableOptions);
}
/**
* Initialize a repeater
*
* @param $this Can be an .InputfieldRepeater or an .InputfieldRepeaterItem
*
*/
function initRepeater($this) {
if($this.hasClass('InputfieldRepeaterItem')) {
// single repeater item
var $inputfields = $this;
var $inputfieldRepeater = $this.closest('.InputfieldRepeater');
var isItem = true;
} else {
// enter repeater
var $inputfields = $this.find('.Inputfields:eq(0)');
var $inputfieldRepeater = $this;
var isItem = false;
}
if($inputfields.hasClass('InputfieldRepeaterInit')) return;
$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();
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) {
initHeaders($this.children('.InputfieldHeader'));
} else {
initHeaders($(".InputfieldRepeaterItem > .InputfieldHeader", $this));
}
if(renderValueMode) {
// nothing further needed if only rendering the value
initDepths($inputfieldRepeater);
return; return;
} }
// hovering the trash gives a preview of what clicking it would do
$(".InputfieldRepeaterTrash", $this).hover(function() {
var $label = $(this).closest('label');
if(!$label.parents().hasClass('InputfieldRepeaterDeletePending')) $label.addClass('ui-state-error');
$label.find('.InputfieldRepeaterItemControls').css('background-color', $label.css('background-color'));
}, function() {
var $label = $(this).closest('label');
if(!$label.parent().hasClass('InputfieldRepeaterDeletePending')) $label.removeClass('ui-state-error');
$label.find('.InputfieldRepeaterItemControls').css('background-color', $label.css('background-color'));
});
// if we only init'd a single item, now make $inputfields refer to all repeater items for sortable init
if(isItem) $inputfields = $inputfieldRepeater.find('.Inputfields:eq(0)');
// setup the sortable
initSortable($inputfieldRepeater, $inputfields);
// setup the add links
$(".InputfieldRepeaterAddLink:not(.InputfieldRepeaterAddLinkInit)", $inputfieldRepeater)
.addClass('InputfieldRepeaterAddLinkInit')
.click(eventAddLinkClick);
// check for maximum items
if($inputfieldRepeater.hasClass('InputfieldRepeaterMax')) {
checkMax($inputfieldRepeater);
} }
InputfieldRepeaterInit($(this)); }
/**
* 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
*
*/
function checkMax($inputfieldRepeater) {
if(!$inputfieldRepeater.hasClass('InputfieldRepeaterMax')) return;
var max = parseInt($inputfieldRepeater.attr('data-max'));
if(max <= 0) return;
var $content = $inputfieldRepeater.children('.InputfieldContent');
var num = $content.children('.Inputfields').children('li:not(.InputfieldRepeaterDeletePending)').length;
var $addItem = $content.children('.InputfieldRepeaterAddItem');
if(num > max) {
$addItem.hide();
} else if(!$addItem.is(":visible")) {
$addItem.show();
}
}
/**
* Update state of the remembered open repeaters
*
* Note: this records state for all repeaters on the page in cookie 'repeaters_open'
* that are configured to be remembered.
*
* @param $item .InputfieldRepeaterItem
*
*/
function updateState($item) {
if($item.closest('.InputfieldRepeaterRememberOpen').length < 1) return;
var val = '';
$(".InputfieldRepeaterItem:not(.InputfieldStateCollapsed)").each(function() {
var id = parseInt($(this).attr('data-page'));
if(id > 0) {
val += id + '|';
}
});
$.cookie('repeaters_open', val);
}
/**
* Initialization for document.ready
*
*/
function init() {
$('.InputfieldRepeater').each(function() {
initRepeater($(this));
}); });
$(document) $(document)
.on('click', '.InputfieldRepeaterTrash', InputfieldRepeaterDeleteClick) .on('reloaded', '.InputfieldRepeater', eventReloaded)
.on('dblclick', '.InputfieldRepeaterTrash', InputfieldRepeaterDeleteDblClick) .on('click', '.InputfieldRepeaterTrash', eventDeleteClick)
.on('click', '.InputfieldRepeaterClone', InputfieldRepeaterCloneClick) .on('dblclick', '.InputfieldRepeaterTrash', eventDeleteDblClick)
.on('click', '.InputfieldRepeaterToggle', InputfieldRepeaterToggleClick) .on('click', '.InputfieldRepeaterClone', eventCloneClick)
.on('opened', '.InputfieldRepeaterItem', InputfieldRepeaterItemOpened) .on('dblclick', '.InputfieldRepeaterToggle', eventOpenAllClick)
.on('closed', '.InputfieldRepeaterItem', InputfieldRepeaterItemClosed) .on('click', '.InputfieldRepeaterToggle', eventToggleClick)
.on('openReady', '.InputfieldRepeaterItem', InputfieldRepeaterItemOpenReady); .on('opened', '.InputfieldRepeaterItem', eventItemOpened)
}); .on('closed', '.InputfieldRepeaterItem', eventItemClosed)
.on('openReady', '.InputfieldRepeaterItem', eventItemOpenReady);
}
init();
}
jQuery(document).ready(function($) {
InputfieldRepeater($);
});

File diff suppressed because one or more lines are too long

View File

@@ -335,8 +335,12 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
$loaded->attr('value', $isLoaded ? 1 : 0); $loaded->attr('value', $isLoaded ? 1 : 0);
$wrap = $this->wire('modules')->get('InputfieldFieldset'); $wrap = $this->wire('modules')->get('InputfieldFieldset');
$wrap->addClass('InputfieldRepeaterItem'); $wrap->addClass('InputfieldRepeaterItem InputfieldNoFocus');
$wrap->label = $this->renderRepeaterLabel($label, ++$cnt, $page); $wrap->entityEncodeLabel = false;
$wrap->label =
"<span class='InputfieldRepeaterItemLabel'>" .
$this->entityEncode($this->renderRepeaterLabel($label, ++$cnt, $page)) .
"</span>";
$wrap->name = "repeater_item_{$page->id}"; $wrap->name = "repeater_item_{$page->id}";
$wrap->wrapAttr('data-page', $page->id); $wrap->wrapAttr('data-page', $page->id);
$wrap->wrapAttr('data-type', $this->getRepeaterItemType($page)); $wrap->wrapAttr('data-type', $this->getRepeaterItemType($page));
@@ -400,6 +404,7 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
// 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
if(!$itemID) { if(!$itemID) {
/** @var InputfieldWrapper $wrap */
$wrap = $this->wire('modules')->get('InputfieldFieldset'); $wrap = $this->wire('modules')->get('InputfieldFieldset');
$wrap->label = $this->renderRepeaterLabel($label, ++$cnt, new NullPage()); $wrap->label = $this->renderRepeaterLabel($label, ++$cnt, new NullPage());
$wrap->class = 'InputfieldRepeaterItem InputfieldRepeaterNewItem'; $wrap->class = 'InputfieldRepeaterItem InputfieldRepeaterNewItem';
@@ -540,10 +545,12 @@ class InputfieldRepeater extends Inputfield implements InputfieldItemList {
$this->wire('config')->js('InputfieldRepeater', array( $this->wire('config')->js('InputfieldRepeater', array(
'editorUrl' => $editorUrl, 'editorUrl' => $editorUrl,
'labels' => array( 'labels' => array(
'remove' => $this->_x('Delete this item', 'repeater-item-action'), 'remove' => $this->_x('Click to delete this item, or double-click to delete all', 'repeater-item-action'),
'removeAll' => $this->_x('Delete all items?', 'repeater-item-action'), 'removeAll' => $this->_x('Delete all items?', 'repeater-item-action'),
'toggle' => $this->_x('Toggle published/unpublished', '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'),
'collapseAll' => $this->_x('Collapse all items?', 'repeater-item-action')
) )
)); ));

View File

@@ -7,28 +7,50 @@
.InputfieldRepeaterItem > .InputfieldHeader { .InputfieldRepeaterItem > .InputfieldHeader {
line-height: 1em; line-height: 1em;
padding: 0.5em; padding: 0.5em 0 0.5em 0.4em;
white-space: nowrap;
overflow: hidden;
position: relative;
.InputfieldRepeaterItemLabel {
display: inline-block;
padding-left: 0.25em;
}
.InputfieldRepeaterItemControls {
padding-right: 0.5em;
padding-left: 0.5em;
margin-top: 0.5em;
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;
}
}
.toggle-icon { .toggle-icon {
line-height: 1em; line-height: 1em;
margin-right: 0.5em; margin-right: 0.5em;
} }
& > .InputfieldRepeaterClone,
& > .InputfieldRepeaterToggle,
& > .InputfieldRepeaterTrash {
cursor: pointer;
float: right;
}
& > .InputfieldRepeaterTrash {
padding-right: 3px;
}
& > .InputfieldRepeaterToggle {
margin-right: 1em;
}
& > .InputfieldRepeaterClone {
margin-right: 1em;
}
} }
.InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending) { .InputfieldRepeaterItem:not(.InputfieldRepeaterDeletePending) {
@@ -48,12 +70,16 @@
&.InputfieldRepeaterOff > .InputfieldHeader { &.InputfieldRepeaterOff > .InputfieldHeader {
// off items faded yet even more // off items faded yet even more
opacity: 0.5; opacity: 0.5;
&:not(:hover) { &:not(:hover) > .InputfieldRepeaterItemLabel {
text-decoration: line-through; text-decoration: line-through;
} }
} }
} }
.InputfieldRepeaterItem.InputfieldRepeaterDeletePending > .InputfieldHeader > .InputfieldRepeaterItemLabel {
text-decoration: line-through;
}
.InputfieldRepeaterItem.InputfieldRepeaterItemLoading { .InputfieldRepeaterItem.InputfieldRepeaterItemLoading {
// override the margin-bottom 1.25em from admin theme stylesheet, so margin isn't showing while item is loading // override the margin-bottom 1.25em from admin theme stylesheet, so margin isn't showing while item is loading
margin-bottom: 1em; margin-bottom: 1em;
@@ -82,6 +108,7 @@
position: relative; position: relative;
} }
.InputfieldRepeaterAddItem input { .InputfieldRepeaterAddItem input {
// count of added items hidden // count of added items hidden
position: absolute; position: absolute;
@@ -96,6 +123,7 @@
.InputfieldRepeaterDrag { .InputfieldRepeaterDrag {
// draggable icon for repeater items // draggable icon for repeater items
display: inline-block;
cursor: ns-resize; cursor: ns-resize;
opacity: 0.7; opacity: 0.7;
line-height: 1em; line-height: 1em;

View File

@@ -351,4 +351,11 @@
top: -1.1em; top: -1.1em;
bottom: auto; } bottom: auto; }
.AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input textarea:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="date"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="datetime"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="datetime-local"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="email"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="month"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="number"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="password"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="search"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="tel"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="text"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="time"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="url"]:focus, .AdminThemeReno .vex.vex-theme-default .vex-dialog-form .vex-dialog-input input[type="week"]:focus {
-moz-box-shadow: inset 0 0 0 2px #86d7c1;
-webkit-box-shadow: inset 0 0 0 2px #86d7c1;
box-shadow: inset 0 0 0 2px #86d7c1; }
.AdminThemeReno .vex.vex-theme-default .vex-dialog-button.vex-dialog-button-primary {
background: #3eb998; }
/*# sourceMappingURL=vex-theme-default.css.map */ /*# sourceMappingURL=vex-theme-default.css.map */

View File

@@ -7,6 +7,7 @@
$blue: #3288e6 $blue: #3288e6
$green: #93BF0D // RJC $green: #93BF0D // RJC
$reno-green: #3eb998 // RJC
.vex.vex-theme-default .vex.vex-theme-default
padding-top: 160px padding-top: 160px
@@ -128,3 +129,16 @@ $green: #93BF0D // RJC
border-top-color: #bbb border-top-color: #bbb
top: -1.1em top: -1.1em
bottom: auto bottom: auto
.AdminThemeReno
.vex.vex-theme-default
.vex-dialog-form
.vex-dialog-input
textarea, input[type="date"], input[type="datetime"], input[type="datetime-local"], input[type="email"], input[type="month"], input[type="number"], input[type="password"], input[type="search"], input[type="tel"], input[type="text"], input[type="time"], input[type="url"], input[type="week"]
&:focus
+box-shadow(inset 0 0 0 2px lighten($reno-green, 20%)) // RJC
.vex-dialog-button
&.vex-dialog-button-primary
background: $reno-green // RJC