Progress on repeater field groups

Refs #2772
This commit is contained in:
Samuel Georges 2017-04-22 21:04:37 +10:00
parent eaf2c9e4d0
commit 46fcdbc636
4 changed files with 218 additions and 23 deletions

View File

@ -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');
}
}

View File

@ -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()

View File

@ -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">&times;</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>

View File

@ -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">&times;</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>