Skip to content

Commit

Permalink
Added feature "Inline Editation" [Closed #126]
Browse files Browse the repository at this point in the history
  • Loading branch information
koprivajakub authored and o5 committed Apr 29, 2014
1 parent 7c30bc7 commit 8fee6af
Show file tree
Hide file tree
Showing 14 changed files with 1,085 additions and 7 deletions.
2 changes: 1 addition & 1 deletion Grido/Components/Columns/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public function getCellPrototype($row = NULL)
*/
public function getHeaderPrototype()
{
if (!$this->headerPrototype) {
if ($this->headerPrototype === NULL) {
$this->headerPrototype = \Nette\Utils\Html::el('th')
->setClass(array('column', 'grid-header-' . $this->getName()));
}
Expand Down
2 changes: 1 addition & 1 deletion Grido/Components/Columns/Date.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @property string $dateFormat
*/
class Date extends Column
class Date extends Editable
{
const FORMAT_TEXT = 'd M Y';
const FORMAT_DATE = 'd.m.Y';
Expand Down
258 changes: 258 additions & 0 deletions Grido/Components/Columns/Editable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
<?php

/**
* This file is part of the Grido (http://grido.bugyik.cz)
*
* Copyright (c) 2014 Petr Bugyík (http://petr.bugyik.cz)
*
* For the full copyright and license information, please view
* the file LICENSE.md that was distributed with this source code.
*/

namespace Grido\Components\Columns;

/**
* An inline editable column.
*
* @package Grido
* @subpackage Components\Columns
* @author Jakub Kopřiva <kopriva.jakub@gmail.com>
* @author Petr Bugyík
*
* @property \Nette\Forms\IControl $editableControl
* @property callback $editableCallback
* @property callback $editableValueCallback
*/
abstract class Editable extends Column
{
/** @var bool */
protected $editable = FALSE;

/** @var bool */
protected $editableDisabled = FALSE;

/** @var \Nette\Forms\IControl Custom control for inline editing */
protected $editableControl;

/** @var callback for custom handling with edited data; function($id, $newValue, $oldValue, Editable $column) {} */
protected $editableCallback;

/** @var callback for custom value; function($row, Columns\Editable $column) {} */
protected $editableValueCallback;

/**
* Sets column as editable.
* @param callback $callback function($id, $newValue, $oldValue, Columns\Editable $column) {} {}
* @param \Nette\Forms\IControl $control
* @return Editable
*/
public function setEditable($callback = NULL, \Nette\Forms\IControl $control = NULL)
{
$this->editable = TRUE;
$this->setClientSideOptions();

$callback && $this->setEditableCallback($callback);
$control && $this->setEditableControl($control);

return $this;
}

/**
* Sets control for inline editation.
* @param \Nette\Forms\IControl $control
* @return Editable
*/
public function setEditableControl(\Nette\Forms\IControl $control)
{
$this->isEditable() ?: $this->setEditable();
$this->editableControl = $control;

return $this;
}

/**
* Sets editable callback.
* @param callback $callback function($id, $newValue, $oldValue, Columns\Editable $column) {}
* @return Editable
*/
public function setEditableCallback($callback)
{
$this->isEditable() ?: $this->setEditable();
$this->editableCallback = $callback;

return $this;
}

/**
* Sets editable value callback.
* @param callback $callback for custom value; function($row, Columns\Editable $column) {}
* @return Editable
*/
public function setEditableValueCallback($callback)
{
$this->isEditable() ?: $this->setEditable();
$this->editableValueCallback = $callback;

return $this;
}

/**
* @return Editable
*/
public function disableEditable()
{
$this->editable = FALSE;
$this->editableDisabled = TRUE;

return $this;
}

protected function setClientSideOptions()
{
$options = $this->grid->getClientSideOptions();
if (!isset($options['editable'])) { //only once
$this->grid->setClientSideOptions(array('editable' => TRUE));
$this->grid->onRender[] = function(\Grido\Grid $grid)
{
foreach ($grid->getComponent(Column::ID)->getComponents() as $column) {
if (!$column instanceof Editable) {
continue;
}

$columnName = $column->getColumn();
$callbackNotSet = $column->isEditable() && $column->editableCallback === NULL;
if (($callbackNotSet && (!is_string($columnName) || strpos($columnName, '.'))) ||
($callbackNotSet && !method_exists($grid->model->dataSource, 'update')))
{
throw new \Exception("Column '{$column->name}' has error: You must define an own editable callback.");
}
}
};
}
}

/**********************************************************************************************/

/**
* Returns header cell prototype (<th> html tag).
* @return \Nette\Utils\Html
*/
public function getHeaderPrototype()
{
$th = parent::getHeaderPrototype();

if ($this->isEditable()) {
$th->data['grido-editable-handler'] = $this->link('editable!');
$th->data['grido-editableControl-handler'] = $this->link('editableControl!');
}

return $th;
}

/**
* Returns cell prototype (<td> html tag).
* @param mixed $row
* @return \Nette\Utils\Html
*/
public function getCellPrototype($row = NULL)
{
$td = parent::getCellPrototype($row);

if ($this->isEditable() && $row !== NULL) {
$td->data['grido-editable-value'] = $this->editableValueCallback === NULL
? parent::getValue($row)
: callback($this->editableValueCallback)->invokeArgs(array($row, $this));
}

return $td;
}

/**
* Returns control for editation.
* @returns \Nette\Forms\Controls\TextInput
*/
public function getEditableControl()
{
if ($this->editableControl === NULL) {
$this->editableControl = new \Nette\Forms\Controls\TextInput;
$this->editableControl->controlPrototype->class[] = 'form-control';
}

return $this->editableControl;
}

/**
* @return callback
* @internal
*/
public function getEditableCallback()
{
return $this->editableCallback;
}

/**
* @return callback
* @internal
*/
public function getEditableValueCallback()
{
return $this->editableValueCallback;
}

/**
* @return bool
* @internal
*/
public function isEditable()
{
return $this->editable;
}

/**
* @return bool
* @internal
*/
public function isEditableDisabled()
{
return $this->editableDisabled;
}

/**********************************************************************************************/

/**
* @internal
*/
public function handleEditable($id, $newValue, $oldValue)
{
$this->grid->onRegistered($this->grid);

if (!$this->presenter->isAjax() || !$this->isEditable()) {
$this->presenter->terminate();
}

$success = $this->editableCallback
? callback($this->editableCallback)->invokeArgs(array($id, $newValue, $oldValue, $this))
: $this->grid->model->update($id, array($this->getColumn() => $newValue), $this->grid->primaryKey);

$response = new \Nette\Application\Responses\JsonResponse(array('updated' => $success));
$this->presenter->sendResponse($response);
}

/**
* @internal
*/
public function handleEditableControl($value)
{
$this->grid->onRegistered($this->grid);

if (!$this->presenter->isAjax() || !$this->isEditable()) {
$this->presenter->terminate();
}

$control = $this->getEditableControl()->setValue($value);
$this->getForm()->addComponent($control, 'edit' . $this->getName());

$response = new \Nette\Application\Responses\TextResponse($control->getControl()->render());
$this->presenter->sendResponse($response);
}
}
2 changes: 1 addition & 1 deletion Grido/Components/Columns/Number.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*
* @property array $numberFormat
*/
class Number extends Column
class Number extends Editable
{
/** @var array */
protected $numberFormat = array(
Expand Down
2 changes: 1 addition & 1 deletion Grido/Components/Columns/Text.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* @subpackage Components\Columns
* @author Petr Bugyík
*/
class Text extends Column
class Text extends Editable
{
/** @var Closure */
protected $truncate;
Expand Down
26 changes: 25 additions & 1 deletion Grido/Components/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ abstract class Container extends \Nette\Application\UI\Control
* Returns column component.
* @param string $name
* @param bool $need
* @return Column
* @return Columns\Editable
*/
public function getColumn($name, $need = TRUE)
{
Expand Down Expand Up @@ -362,4 +362,28 @@ public function setExport($label = NULL)
{
return new Export($this, $label);
}

/**
* Sets all columns as editable.
* Callback is optional for user implementation of method for saving modified data.
* @param callback $callback
* @return \Grido\Grid
*/
public function setEditableColumns($callback = NULL)
{
$this->onRegistered[] = function(\Grido\Grid $grid) use ($callback)
{
if (!$grid->hasColumns()) {
return;
}

foreach ($grid->getComponent(Column::ID)->getComponents() as $column) {
if ($column instanceof Columns\Editable && !$column->isEditable() && !$column->isEditableDisabled()) {
$column->setEditable($callback);
}
}
};

return $this;
}
}
13 changes: 13 additions & 0 deletions Grido/DataSources/NetteDatabase.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,17 @@ public function suggest($column, array $conditions, $limit)

return array_values($items);
}

/**
* @param mixed $id
* @param array $values
* @param string $idCol
* @return bool
*/
public function update($id, array $values, $idCol)
{
return (bool) $this->getSelection()
->where("$idCol = ?", $id)
->update($values);
}
}
13 changes: 12 additions & 1 deletion Grido/Grid.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class Grid extends Components\Container
/***** DEFAULTS ****/
const BUTTONS = 'buttons';

const CLIENT_SIDE_OPTIONS = 'grido-options';

/** @var int @persistent */
public $page = 1;

Expand Down Expand Up @@ -297,7 +299,7 @@ public function setRowCallback($callback)
*/
public function setClientSideOptions(array $options)
{
$this->getTablePrototype()->data['grido-options'] = json_encode($options);
$this->getTablePrototype()->data[self::CLIENT_SIDE_OPTIONS] = json_encode($options);
return $this;
}

Expand Down Expand Up @@ -579,6 +581,15 @@ public function getRowPrototype($row)
return $tr;
}

/**
* Returns client-side options.
* @return array
*/
public function getClientSideOptions()
{
return (array) json_decode($this->getTablePrototype()->data[self::CLIENT_SIDE_OPTIONS]);
}

/**********************************************************************************************/

/**
Expand Down
6 changes: 6 additions & 0 deletions client-side/grido.css
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@
width: auto;
}

.grido tbody .form-control {
width: 100%;
height: 23px;
padding: 0 0 0 3px;
}

.grido tfoot .form-control {
display: inline;
}
Expand Down

0 comments on commit 8fee6af

Please sign in to comment.