parent
eaf2c9e4d0
commit
46fcdbc636
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use Backend\Classes\FormField;
|
||||
use Backend\Classes\FormWidgetBase;
|
||||
use ApplicationException;
|
||||
|
||||
/**
|
||||
* Repeater Form Widget
|
||||
|
|
@ -9,6 +10,7 @@ use Backend\Classes\FormWidgetBase;
|
|||
class Repeater extends FormWidgetBase
|
||||
{
|
||||
const INDEX_PREFIX = '___index_';
|
||||
const GROUP_PREFIX = '___group_';
|
||||
|
||||
//
|
||||
// Configurable properties
|
||||
|
|
@ -29,6 +31,11 @@ class Repeater extends FormWidgetBase
|
|||
*/
|
||||
public $sortable = false;
|
||||
|
||||
/**
|
||||
* @var int Maximum repeated items allowable.
|
||||
*/
|
||||
public $maxItems = null;
|
||||
|
||||
//
|
||||
// Object properties
|
||||
//
|
||||
|
|
@ -38,11 +45,21 @@ class Repeater extends FormWidgetBase
|
|||
*/
|
||||
protected $defaultAlias = 'repeater';
|
||||
|
||||
/**
|
||||
* @var string Form field name for capturing an index.
|
||||
*/
|
||||
protected $indexInputName;
|
||||
|
||||
/**
|
||||
* @var int Count of repeated items.
|
||||
*/
|
||||
protected $indexCount = 0;
|
||||
|
||||
/**
|
||||
* @var array Meta data associated to each field, organised by index
|
||||
*/
|
||||
protected $indexMeta = [];
|
||||
|
||||
/**
|
||||
* @var array Collection of form widgets.
|
||||
*/
|
||||
|
|
@ -53,10 +70,14 @@ class Repeater extends FormWidgetBase
|
|||
*/
|
||||
protected static $onAddItemCalled = false;
|
||||
|
||||
protected $useGroups = false;
|
||||
|
||||
/**
|
||||
* @var int Maximum repeated items allowable.
|
||||
* @var string Form field name for capturing an index.
|
||||
*/
|
||||
protected $maxItems = null;
|
||||
protected $groupInputName;
|
||||
|
||||
protected $groupDefinitions = [];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
|
|
@ -70,6 +91,12 @@ class Repeater extends FormWidgetBase
|
|||
'maxItems',
|
||||
]);
|
||||
|
||||
$fieldName = $this->formField->getName(false);
|
||||
$this->indexInputName = self::INDEX_PREFIX.$fieldName.'[]';
|
||||
$this->groupInputName = self::GROUP_PREFIX.$fieldName.'[]';
|
||||
|
||||
$this->processGroupMode();
|
||||
|
||||
if (!self::$onAddItemCalled) {
|
||||
$this->processExistingItems();
|
||||
}
|
||||
|
|
@ -89,10 +116,15 @@ class Repeater extends FormWidgetBase
|
|||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
$this->vars['indexName'] = self::INDEX_PREFIX.$this->formField->getName(false).'[]';
|
||||
$this->vars['indexInputName'] = $this->indexInputName;
|
||||
$this->vars['groupInputName'] = $this->groupInputName;
|
||||
|
||||
$this->vars['prompt'] = $this->prompt;
|
||||
$this->vars['formWidgets'] = $this->formWidgets;
|
||||
$this->vars['maxItems'] = $this->maxItems;
|
||||
|
||||
$this->vars['useGroups'] = $this->useGroups;
|
||||
$this->vars['groupDefinitions'] = $this->groupDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -109,9 +141,21 @@ class Repeater extends FormWidgetBase
|
|||
*/
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
// traceLog($value);
|
||||
// return '';
|
||||
return (array) $value;
|
||||
}
|
||||
|
||||
// Format the save value to index _index and _group and strip the array keys
|
||||
protected function processSaveValue()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes existing form data and applies it to the form widgets.
|
||||
* @return void
|
||||
*/
|
||||
protected function processExistingItems()
|
||||
{
|
||||
$loadValue = $this->getLoadValue();
|
||||
|
|
@ -119,26 +163,42 @@ class Repeater extends FormWidgetBase
|
|||
$loadValue = array_keys($loadValue);
|
||||
}
|
||||
|
||||
$itemIndexes = post(self::INDEX_PREFIX.$this->formField->getName(false), $loadValue);
|
||||
$itemIndexes = post($this->indexInputName, $loadValue);
|
||||
$itemGroups = post($this->groupInputName, $loadValue);
|
||||
|
||||
if (!is_array($itemIndexes)) {
|
||||
$items = array_combine(
|
||||
(array) $itemIndexes,
|
||||
(array) ($this->useGroups ? $itemGroups : $itemIndexes)
|
||||
);
|
||||
|
||||
if (!is_array($items)) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($itemIndexes as $itemIndex) {
|
||||
$this->makeItemFormWidget($itemIndex);
|
||||
foreach ($items as $itemIndex => $groupCode) {
|
||||
$this->makeItemFormWidget($itemIndex, $groupCode);
|
||||
$this->indexCount = max((int) $itemIndex, $this->indexCount);
|
||||
}
|
||||
}
|
||||
|
||||
protected function makeItemFormWidget($index = 0)
|
||||
/**
|
||||
* Creates a form widget based on a field index and optional group code.
|
||||
* @param int $index
|
||||
* @param string $index
|
||||
* @return \Backend\Widgets\Form
|
||||
*/
|
||||
protected function makeItemFormWidget($index = 0, $groupCode = null)
|
||||
{
|
||||
$loadValue = $this->getLoadValue();
|
||||
if (!is_array($loadValue)) {
|
||||
$loadValue = [];
|
||||
}
|
||||
|
||||
$config = $this->makeConfig($this->form);
|
||||
$configDefinition = $this->useGroups
|
||||
? $this->getGroupFormFieldConfig($groupCode)
|
||||
: $this->form;
|
||||
|
||||
$config = $this->makeConfig($configDefinition);
|
||||
$config->model = $this->model;
|
||||
$config->data = array_get($loadValue, $index, []);
|
||||
$config->alias = $this->alias . 'Form'.$index;
|
||||
|
|
@ -148,17 +208,29 @@ class Repeater extends FormWidgetBase
|
|||
$widget = $this->makeWidget('Backend\Widgets\Form', $config);
|
||||
$widget->bindToController();
|
||||
|
||||
$this->indexMeta[$index] = [
|
||||
'groupCode' => $groupCode
|
||||
];
|
||||
|
||||
return $this->formWidgets[$index] = $widget;
|
||||
}
|
||||
|
||||
//
|
||||
// AJAX handlers
|
||||
//
|
||||
|
||||
public function onAddItem()
|
||||
{
|
||||
self::$onAddItemCalled = true;
|
||||
|
||||
$this->indexCount++;
|
||||
|
||||
traceLog($this->indexCount);
|
||||
|
||||
$groupCode = post('_repeater_group');
|
||||
|
||||
$this->prepareVars();
|
||||
$this->vars['widget'] = $this->makeItemFormWidget($this->indexCount);
|
||||
$this->vars['widget'] = $this->makeItemFormWidget($this->indexCount, $groupCode);
|
||||
$this->vars['indexValue'] = $this->indexCount;
|
||||
|
||||
$itemContainer = '@#'.$this->getId('items');
|
||||
|
|
@ -173,9 +245,71 @@ class Repeater extends FormWidgetBase
|
|||
public function onRefresh()
|
||||
{
|
||||
$index = post('_repeater_index');
|
||||
$group = post('_repeater_group');
|
||||
|
||||
$widget = $this->makeItemFormWidget($index);
|
||||
$widget = $this->makeItemFormWidget($index, $group);
|
||||
|
||||
return $widget->onRefresh();
|
||||
}
|
||||
|
||||
//
|
||||
// Group mode
|
||||
//
|
||||
|
||||
/**
|
||||
* Returns the form field configuration for a group, identified by code.
|
||||
* @param string $code
|
||||
* @return array|null
|
||||
*/
|
||||
protected function getGroupFormFieldConfig($code)
|
||||
{
|
||||
if (!$code) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$fields = array_get($this->groupDefinitions, $code.'.fields');
|
||||
|
||||
if (!$fields) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ['fields' => $fields];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process features related to group mode.
|
||||
* @return void
|
||||
*/
|
||||
protected function processGroupMode()
|
||||
{
|
||||
$palette = [];
|
||||
|
||||
if (!$group = $this->getConfig('groups', [])) {
|
||||
$this->useGroups = false;
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($group as $code => $config) {
|
||||
$palette[$code] = [
|
||||
'code' => $code,
|
||||
'name' => array_get($config, 'name'),
|
||||
'icon' => array_get($config, 'icon', 'icon-square-o'),
|
||||
'description' => array_get($config, 'description'),
|
||||
'fields' => array_get($config, 'fields')
|
||||
];
|
||||
}
|
||||
|
||||
$this->groupDefinitions = $palette;
|
||||
$this->useGroups = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a field group code from its index.
|
||||
* @param $index int
|
||||
* @return string
|
||||
*/
|
||||
public function getGroupCodeFromIndex($index)
|
||||
{
|
||||
return array_get($this->indexMeta, $index.'.groupCode');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
this.$el.on('ajaxDone', '> .field-repeater-items > .field-repeater-item > .repeater-item-remove > [data-repeater-remove]', this.proxy(this.onRemoveItemSuccess))
|
||||
this.$el.on('ajaxDone', '> .field-repeater-add-item > [data-repeater-add]', this.proxy(this.onAddItemSuccess))
|
||||
this.$el.on('click', '> ul > li > .repeater-item-collapse .repeater-item-collapse-one', this.proxy(this.toggleCollapse))
|
||||
this.$el.on('click', '> .field-repeater-add-item > [data-repeater-add-group]', this.proxy(this.clickAddGroupButton))
|
||||
|
||||
this.$el.one('dispose-control', this.proxy(this.dispose))
|
||||
|
||||
|
|
@ -55,6 +56,7 @@
|
|||
this.$el.off('ajaxDone', '> .field-repeater-items > .field-repeater-item > .repeater-item-remove > [data-repeater-remove]', this.proxy(this.onRemoveItemSuccess))
|
||||
this.$el.off('ajaxDone', '> .field-repeater-add-item > [data-repeater-add]', this.proxy(this.onAddItemSuccess))
|
||||
this.$el.off('click', '> .field-repeater-items > .field-repeater-item > .repeater-item-collapse .repeater-item-collapse-one', this.proxy(this.toggleCollapse))
|
||||
this.$el.off('click', '> .field-repeater-add-item > [data-repeater-add-group]', this.proxy(this.clickAddGroupButton))
|
||||
|
||||
this.$el.off('dispose-control', this.proxy(this.dispose))
|
||||
this.$el.removeData('oc.repeater')
|
||||
|
|
@ -80,6 +82,14 @@
|
|||
this.$sortable.sortable(sortableOptions)
|
||||
}
|
||||
|
||||
Repeater.prototype.clickAddGroupButton = function(ev) {
|
||||
var templateHtml = $('> [data-group-palette-template]', this.$el).html()
|
||||
|
||||
$(ev.target).ocPopover({
|
||||
content: templateHtml
|
||||
})
|
||||
}
|
||||
|
||||
Repeater.prototype.onRemoveItemSuccess = function(ev) {
|
||||
$(ev.target).closest('.field-repeater-item').remove()
|
||||
this.togglePrompt()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
data-sortable-handle=".<?= $this->getId('items') ?>-handle">
|
||||
|
||||
<ul id="<?= $this->getId('items') ?>" class="field-repeater-items">
|
||||
<?php foreach ($this->formWidgets as $index => $widget): ?>
|
||||
<?php foreach ($formWidgets as $index => $widget): ?>
|
||||
<?= $this->makePartial('repeater_item', [
|
||||
'widget' => $widget,
|
||||
'indexValue' => $index
|
||||
|
|
@ -14,13 +14,58 @@
|
|||
</ul>
|
||||
|
||||
<div class="field-repeater-add-item loading-indicator-container indicator-center">
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-repeater-add
|
||||
data-request="<?= $this->getEventHandler('onAddItem') ?>"
|
||||
data-load-indicator>
|
||||
<?= e(trans($prompt)) ?>
|
||||
</a>
|
||||
<?php if ($useGroups): ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-repeater-add-group
|
||||
data-load-indicator>
|
||||
<?= e(trans($prompt)) ?>
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a
|
||||
href="javascript:;"
|
||||
data-repeater-add
|
||||
data-request="<?= $this->getEventHandler('onAddItem') ?>"
|
||||
data-load-indicator>
|
||||
<?= e(trans($prompt)) ?>
|
||||
</a>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<script type="text/template" data-group-palette-template>
|
||||
<div class="popover-head">
|
||||
<h3><?= e(trans($prompt)) ?></h3>
|
||||
<button type="button" class="close"
|
||||
data-dismiss="popover"
|
||||
aria-hidden="true">×</button>
|
||||
</div>
|
||||
<div class="popover-fixed-height w-300">
|
||||
<div class="control-scrollpad" data-control="scrollpad">
|
||||
<div class="scroll-wrapper">
|
||||
|
||||
<div class="control-filelist filelist-hero" data-control="filelist">
|
||||
<ul>
|
||||
<?php foreach ($groupDefinitions as $item): ?>
|
||||
<li>
|
||||
<a
|
||||
href="javascript:;"
|
||||
onclick="$(this).trigger('close.oc.popover')"
|
||||
data-repeater-add
|
||||
data-request="<?= $this->getEventHandler('onAddItem') ?>"
|
||||
data-request-data="_repeater_group: '<?= $item['code'] ?>'">
|
||||
<i class="list-icon <?= $item['icon'] ?>"></i>
|
||||
<span class="title"><?= $item['name'] ?></span>
|
||||
<span class="description"><?= $item['description'] ?></span>
|
||||
<span class="borders"></span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
</div>
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
<?php
|
||||
$groupCode = $useGroups ? $this->getGroupCodeFromIndex($indexValue) : '';
|
||||
?>
|
||||
<li class="field-repeater-item">
|
||||
|
||||
<div class="repeater-item-handle <?= $this->getId('items') ?>-handle">
|
||||
|
|
@ -11,7 +14,7 @@
|
|||
aria-label="Remove"
|
||||
data-repeater-remove
|
||||
data-request="<?= $this->getEventHandler('onRemoveItem') ?>"
|
||||
data-request-data="'index': '<?= $indexValue ?>'"
|
||||
data-request-data="'_repeater_index': '<?= $indexValue ?>', '_repeater_group': '<?= $groupCode ?>'"
|
||||
data-request-confirm="<?= e(trans('backend::lang.form.action_confirm')) ?>">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
|
|
@ -28,12 +31,15 @@
|
|||
<div class="field-repeater-form"
|
||||
data-control="formwidget"
|
||||
data-refresh-handler="<?= $this->getEventHandler('onRefresh') ?>"
|
||||
data-refresh-data="'_repeater_index': '<?= $indexValue ?>'">
|
||||
data-refresh-data="'_repeater_index': '<?= $indexValue ?>', '_repeater_group': '<?= $groupCode ?>'">
|
||||
<?php foreach ($widget->getFields() as $field): ?>
|
||||
<?= $widget->renderField($field) ?>
|
||||
<?php endforeach ?>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="<?= $indexName ?>" value="<?= $indexValue ?>" />
|
||||
<input type="hidden" name="<?= $indexInputName ?>" value="<?= $indexValue ?>" />
|
||||
<?php if ($useGroups): ?>
|
||||
<input type="hidden" name="<?= $groupInputName ?>" value="<?= $groupCode ?>" />
|
||||
<?php endif ?>
|
||||
|
||||
</li>
|
||||
|
|
|
|||
Loading…
Reference in New Issue