stub of the row editor

editing
Ondřej Hruška 6 years ago
parent e17548e5e7
commit bb8bc459dc
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 17
      _json_typehints.php
  2. 29
      app/Http/Controllers/TableController.php
  3. 66
      app/Http/Controllers/TableEditController.php
  4. 4
      app/Models/BaseModel.php
  5. 4
      app/Models/Revision.php
  6. 2
      app/Models/Row.php
  7. 33
      app/Models/Table.php
  8. 11
      app/Providers/AppServiceProvider.php
  9. 124
      app/Tables/Changeset.php
  10. 96
      app/Tables/Column.php
  11. 4
      app/helpers.php
  12. 120
      public/fonts/fa-dtbl-1-preview.html
  13. 39
      public/fonts/fa-dtbl-1.css
  14. BIN
      public/fonts/fa-dtbl-1.eot
  15. 51
      public/fonts/fa-dtbl-1.svg
  16. BIN
      public/fonts/fa-dtbl-1.ttf
  17. BIN
      public/fonts/fa-dtbl-1.woff2
  18. 1
      resources/assets/js/app.js
  19. 86
      resources/assets/js/components/RowEditor.vue
  20. 9
      resources/assets/sass/_helpers.scss
  21. 2
      resources/assets/sass/app.scss
  22. 20
      resources/assets/sass/bootstrap-customizations/_border.scss
  23. 0
      resources/assets/sass/bootstrap-customizations/_nav.scss
  24. 9
      resources/assets/sass/bootstrap-customizations/_table.scss
  25. 7
      resources/views/table/_rows.blade.php
  26. 40
      resources/views/table/_view-action-buttons.blade.php
  27. 3
      resources/views/table/conf.blade.php
  28. 6
      resources/views/table/propose/add-rows.blade.php
  29. 43
      resources/views/table/propose/edit-rows.blade.php
  30. 58
      resources/views/table/propose/layout.blade.php
  31. 6
      resources/views/table/propose/manage-columns.blade.php
  32. 6
      resources/views/table/propose/review.blade.php
  33. 2
      resources/views/table/view.blade.php
  34. 3
      routes/web.php

@ -0,0 +1,17 @@
<?php
/**
* Interface RowData
*
* @property int $_id
*/
interface RowData {}
/**
* Interface DecoratedRow
*
* @property bool $_remove - marked to be removed
* @property mixed[] $_orig - original values before transformation, key by CID
* @property string[] $_changed - values that were changed
*/
interface DecoratedRow extends RowData {}

@ -55,35 +55,6 @@ class TableController extends Controller
]);
}
public function draftChange(Request $request, User $user, string $table)
{
/** @var Table $tableModel */
$tableModel = $user->tables()->where('name', $table)->first();
if ($tableModel === null) abort(404, "No such table.");
$session_key = "proposal_{$tableModel->id}";
/** @var Changeset $changeset */
$changeset = $request->session()->remember($session_key, function () use ($tableModel) {
$changeset = new Changeset();
$changeset->table = $tableModel;
$changeset->revision = $tableModel->revision;
return $changeset;
});
$revision = $changeset->revision;
$columns = Column::columnsFromJson($revision->columns);
$rows = $revision->rowsData($columns)->paginate(25, []);
return view('table.propose', [
'table' => $tableModel,
'revision' => $revision,
'columns' => $columns,
'rows' => $rows,
'changeset' => $changeset,
]);
}
public function delete(Request $request, User $user, string $table)
{
/** @var Table $tableModel */

@ -0,0 +1,66 @@
<?php
namespace App\Http\Controllers;
use App\Models\Table;
use App\Models\User;
use App\Tables\Changeset;
use App\Tables\Column;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Input;
class TableEditController extends Controller
{
/**
* Initialize the session-stored changeset, if not set yet
*
* @param Table $table
* @return Changeset
*/
private function getChangeset(Table $table)
{
$session_key = "proposal_{$table->id}";
if (Input::has('reset')) {
session()->forget($session_key);
}
/** @var Changeset $changeset */
return session()->remember($session_key, function () use ($table) {
$changeset = new Changeset();
$changeset->table = $table;
$changeset->revision = $table->revision;
return $changeset;
});
}
public function draft(User $user, string $table, $tab = null)
{
/** @var Table $tableModel */
$tableModel = $user->tables()->where('name', $table)->first();
if ($tableModel === null) abort(404, "No such table.");
if ($tab == null) $tab = 'edit-rows';
$tabs = ['edit-rows', 'add-rows', 'manage-columns', 'review'];
if (!in_array($tab, $tabs)) abort(404, "No such tab: $tab");
$changeset = $this->getChangeset($tableModel);
return $this->{camel_case($tab)}($changeset);
}
private function editRows(Changeset $changeset)
{
$revision = $changeset->revision;
$columns = $changeset->transformColumns();
$rows = $revision->rowsData($columns, true, false)->paginate(25, []);
return view('table.propose.edit-rows', [
'changeset' => $changeset,
'table' => $changeset->table,
'columns' => collect($columns),
'rows' => $rows,
]);
}
}

@ -26,7 +26,9 @@ class BaseModel extends Model
public function getRelationValue($key)
{
if ($this->exists && !method_exists($this, $key)) {
throw new \LogicException("No attribute or relation ".var_export($key, true));
if (!isset($this->original[$key])) {
throw new \LogicException("No attribute or relation " . var_export($key, true));
}
}
return parent::getRelationValue($key);

@ -40,12 +40,12 @@ class Revision extends BaseModel
* @param Column[] $columns
* @return \Illuminate\Database\Query\Builder|static
*/
public function rowsData($columns, $withId=true)
public function rowsData($columns, $withId=true, $named=true)
{
$selects = $withId ? ["data->>'_id' as _id"] : [];
foreach ($columns as $col) {
$selects[] = "data->>'$col->id' as $col->name";
$selects[] = "data->>'$col->id' as " . ($named ? $col->name : $col->id);
}
return $this->rows()->select([])->selectRaw(implode(', ', $selects));

@ -6,7 +6,7 @@ namespace App\Models;
* Row in a data table
*
* @property int $id
* @property string $data - JSONB, always containing _id
* @property object|\RowData $data - JSONB, always containing _id
*/
class Row extends BaseModel
{

@ -22,7 +22,9 @@ use Illuminate\Database\Eloquent\Collection;
* @property string $origin
* @property int $visits
* @property-read string $viewRoute
* @property-read string $draftRoute
* @property-read string $settingsRoute
* @property-read string $deleteRoute
* @property-read User $owner
* @property-read Table $parentTable
* @property-read Table[]|Collection $forks
@ -123,21 +125,32 @@ class Table extends BaseModel
public function __get($name)
{
if ($name == 'viewRoute') {
return route('table.view', ['user' => $this->cachedOwner()->name, 'table' => $this->name]);
}
if ($name == 'settingsRoute') {
return route('table.conf', ['user' => $this->cachedOwner()->name, 'table' => $this->name]);
}
if ($name == 'deleteRoute') {
return route('table.delete', ['user' => $this->cachedOwner()->name, 'table' => $this->name]);
if (ends_with($name, 'Route')) {
$arg = [
'user' => $this->cachedOwner()->name,
'table' => $this->name
];
switch ($name) {
case 'viewRoute': return route('table.view', $arg);
case 'settingsRoute': return route('table.conf', $arg);
case 'draftRoute': return route('table.draft', $arg);
case 'deleteRoute': return route('table.delete', $arg);
}
}
return parent::__get($name);
}
public function getDraftRoute($tab=null)
{
return route('table.draft', [
'user' => $this->cachedOwner()->name,
'table' => $this->name,
'tab' => $tab,
]);
}
public function scopeForList(Builder $query)
{
return $query->with('revision:id,row_count')->with('owner:id,name,title')

@ -29,8 +29,17 @@ class AppServiceProvider extends ServiceProvider
});
\Blade::directive('tooltip', function($arg) {
$arg = trim($arg);
$placement = '';
if (starts_with($arg, ['top,', 'bottom,', 'left,', 'right,'])) {
list($placement, $arg) = explode(',', $arg);
$arg = trim($arg);
}
$arge = e($arg);
return 'aria-label="' . $arge . '" title="' . $arge . '"';
$html = '';
if ($placement) $html .= 'data-placement="' . $placement . '" ';
return $html . 'data-toggle="tooltip" aria-label="' . $arge . '" title="' . $arge . '"';
});
\Blade::directive('sr', function($arg) {

@ -40,39 +40,43 @@ class Changeset
* Rows whose content changed, identified by _id.
* Only changed values are to be filled. Columns are identified by GCIDs
*
* @var array|null - [[_id:…, …], …]
* Key'd by _id
*
* @var array|null - [_id -> [_id:…, cid:…, cid:…], …]
*/
public $rowUpdates;
public $rowUpdates = [];
/**
* New rows in the full Row::data format, including GRIDs.
* Values are identified by GCIDs from previously defined, or new columns.
*
* @var array|null - [[_id:…, …], …]
* @var array|null - [[_id:…, cid:…, cid:…], …]
*/
public $newRows;
public $newRows = [];
/**
* Rows to be removed
*
* @var int[]|null - GRIDs
*/
public $removedRows;
public $removedRows = [];
/**
* Values changed in column specifications, such as name, title, etc.
* This does not affect the table rows in any way.
*
* @var array[] - column specification objects, with GCIDs
* Key'd by id
*
* @var array[] - column specification objects, with GCIDs, key'd by CID
*/
public $columnUpdates;
public $columnUpdates = [];
/**
* New columns in the full format, including GCIDs
*
* @var array|null - [[id:…, …], …]
*/
public $newColumns;
public $newColumns = [];
/**
* When reordering columns, here is the column IDs array
@ -84,7 +88,7 @@ class Changeset
*
* @var string[]|null - GCIDs
*/
public $columnOrder;
public $columnOrder = [];
/**
* Columns to be removed
@ -94,7 +98,10 @@ class Changeset
*
* @var int[]|null - GCIDs
*/
public $removedColumns;
public $removedColumns = [];
/** @var Column[] - loaded and transformed columns, cached from previous call to transformColumns() */
private $cachedColumns;
private function walkProps()
{
@ -146,6 +153,11 @@ class Changeset
return $object;
}
/**
* Check if there is any change in this changeset
*
* @return bool - any found
*/
public function hasAnyChanges()
{
foreach ($this->walkProps() as $prop) {
@ -156,4 +168,96 @@ class Changeset
return false;
}
/**
* Decorate / transform a single row for the editor view
*
* @param object|\DecoratedRow $row - row, must be key'd by column ids
* @param bool $decorate - to add extra underscored info for the editor
* @return \DecoratedRow|object|null - null if not decorating and the row was removed
*/
public function transformRow($row, $decorate)
{
if ($decorate) {
$row->_remove = false;
$row->_changed = [];
$row->_orig = [];
}
// Removed rows
if (in_array($row->_id, $this->removedRows)) {
if ($decorate) {
$row->_remove = true;
} else {
return null;
}
}
// Changed values
if (isset($this->rowUpdates[$row->_id])) {
$newVals = $this->rowUpdates[$row->_id];
if ($decorate) {
$row->_changed = array_keys($newVals);
$row->_orig = array_only((array)$row, $row->_changed);
}
$row = (object)array_merge((array)$row, $newVals);
}
// Drop deleted columns
if (!$decorate) {
foreach ($this->removedColumns as $colId) {
unset($row->$colId);
}
}
unset($row->_row_pivot);
return $row;
}
/**
* Decorate / transform columns (loaded from the source revision)
*
* @return Column[]
*/
public function transformColumns()
{
if ($this->cachedColumns) return $this->cachedColumns;
$columns = Column::columnsFromJson($this->revision->columns);
// Modify columns
$byId = [];
foreach ($columns as $column) {
$byId[$column->id] = $column;
if (isset($this->columnUpdates[$column->id])) {
$column->modifyByChangeset($this->columnUpdates[$column->id]);
}
if (in_array($column->id, $this->removedColumns)) {
$column->markForRemoval();
}
}
// Append new columns
foreach ($this->newColumns as $newColumn) {
$columns[] = $c =new Column($newColumn);
$c->markAsNew();
}
// Reorder
$newOrder = [];
foreach ($this->columnOrder as $id) {
$newOrder[] = $byId[$id];
}
$leftover_keys = array_diff(array_keys($byId), $this->columnOrder);
foreach ($leftover_keys as $id) {
$newOrder[] = $byId[$id];
}
return $this->cachedColumns = $newOrder;
}
}

@ -1,9 +1,8 @@
<?php
namespace App\Tables;
use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;
use MightyPork\Exceptions\NotApplicableException;
use MightyPork\Utils\Utils;
@ -15,8 +14,20 @@ use MightyPork\Utils\Utils;
* @property-read string $type
* @property-read string $name
* @property-read string $title
* @property-read bool $isNew
* @property-read bool $toRemove
*
* @property-read bool $id_modified
* @property-read bool $type_modified
* @property-read bool $name_modified
* @property-read bool $title_modified
*
* @property-read string $id_orig
* @property-read string $type_orig
* @property-read string $name_orig
* @property-read string $title_orig
*/
class Column implements JsonSerializable
class Column implements JsonSerializable, Arrayable
{
const colTypes = [
'int', 'bool', 'float', 'string'
@ -27,6 +38,76 @@ class Column implements JsonSerializable
private $name;
private $title;
/** @var bool - column is marked to be deleted by a changeset */
private $toRemove = false;
/** @var bool - column is new in this changeset */
private $isNew = false;
/** @var array - original attrib values if edited in a changeset */
private $orig_attribs = [];
/** @var array - list of attrib names that are modified by a changeset */
private $modified_attribs = [];
/**
* Mark for removal (used in editing GUI)
*/
public function markForRemoval()
{
$this->toRemove = true;
}
/**
* Mark this column as new
*/
public function markAsNew()
{
$this->isNew = true;
}
/**
* Modify by a changeset
*
* @param array $columnObject
*/
public function modifyByChangeset(array $columnObject)
{
foreach ((array)$columnObject as $key => $value) {
if ($value != $this->$key) {
$this->modified_attribs[] = $key;
$this->orig_attribs[] = $this->$key;
$this->$key = $value;
}
}
}
public function __get($name)
{
if (property_exists($this, $name)) {
return $this->$name;
}
if (ends_with($name, '_modified')) {
$basename = str_replace('_modified', '', $name);
if (property_exists($this, $basename)) {
return in_array($basename, $this->modified_attribs);
}
}
if (ends_with($name, '_orig')) {
$basename = str_replace('_orig', '', $name);
if (property_exists($this, $basename)) {
return $this->orig_attribs[$basename];
}
}
throw new NotApplicableException("No such column property: $name");
}
/**
* @param $columns
* @return Column[]
*/
public static function columnsFromJson($columns)
{
if (is_string($columns)) {
@ -73,15 +154,6 @@ class Column implements JsonSerializable
$this->title = $b->title ?: $b->name;
}
public function __get($name)
{
if (property_exists($this, $name)) {
return $this->$name;
}
throw new NotApplicableException("No such column property: $name");
}
/**
* @return array with keys {name, title, type}
*/

@ -85,6 +85,10 @@ function old_json($name, $default) {
// Safe JSON funcs
function toJSON($object) {
if (!$object instanceof JsonSerializable && $object instanceof \Illuminate\Contracts\Support\Arrayable) {
$object = $object->toArray();
}
return \GuzzleHttp\json_encode($object, JSON_UNESCAPED_SLASHES + JSON_UNESCAPED_UNICODE);
}

@ -175,6 +175,7 @@
.fa-google:before,
.fa-history:before,
.fa-home:before,
.fa-hourglass:before,
.fa-inbox:before,
.fa-key-modern:before,
.fa-link:before,
@ -187,7 +188,9 @@
.fa-star-o:before,
.fa-table:before,
.fa-th-list:before,
.fa-times:before,
.fa-trash-o:before,
.fa-undo:before,
.fa-user:before,
.fa-user-circle-o:before,
.fa-user-plus:before,
@ -221,24 +224,27 @@
.fa-google:before { content: "\f10b"; }
.fa-history:before { content: "\f10c"; }
.fa-home:before { content: "\f10d"; }
.fa-inbox:before { content: "\f10e"; }
.fa-key-modern:before { content: "\f10f"; }
.fa-link:before { content: "\f110"; }
.fa-pencil:before { content: "\f111"; }
.fa-plus:before { content: "\f112"; }
.fa-question-circle:before { content: "\f113"; }
.fa-sign-in:before { content: "\f114"; }
.fa-sign-out:before { content: "\f115"; }
.fa-star:before { content: "\f116"; }
.fa-star-o:before { content: "\f117"; }
.fa-table:before { content: "\f118"; }
.fa-th-list:before { content: "\f119"; }
.fa-trash-o:before { content: "\f11a"; }
.fa-user:before { content: "\f11b"; }
.fa-user-circle-o:before { content: "\f11c"; }
.fa-user-plus:before { content: "\f11d"; }
.fa-users:before { content: "\f11e"; }
.fa-wrench:before { content: "\f11f"; }
.fa-hourglass:before { content: "\f10e"; }
.fa-inbox:before { content: "\f10f"; }
.fa-key-modern:before { content: "\f110"; }
.fa-link:before { content: "\f111"; }
.fa-pencil:before { content: "\f112"; }
.fa-plus:before { content: "\f113"; }
.fa-question-circle:before { content: "\f114"; }
.fa-sign-in:before { content: "\f115"; }
.fa-sign-out:before { content: "\f116"; }
.fa-star:before { content: "\f117"; }
.fa-star-o:before { content: "\f118"; }
.fa-table:before { content: "\f119"; }
.fa-th-list:before { content: "\f11a"; }
.fa-times:before { content: "\f11b"; }
.fa-trash-o:before { content: "\f11c"; }
.fa-undo:before { content: "\f11d"; }
.fa-user:before { content: "\f11e"; }
.fa-user-circle-o:before { content: "\f11f"; }
.fa-user-plus:before { content: "\f120"; }
.fa-users:before { content: "\f121"; }
.fa-wrench:before { content: "\f122"; }
</style>
<!--[if lte IE 8]><script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
@ -254,7 +260,7 @@
<body class="characters-off">
<div id="page" class="container">
<header>
<h1>fa-dtbl-1 contains 32 glyphs:</h1>
<h1>fa-dtbl-1 contains 35 glyphs:</h1>
<a onclick="toggleCharacters(); return false;" href="#">Toggle Preview Characters</a>
</header>
@ -444,6 +450,19 @@
</div>
</div>
<div class="glyph">
<div class="preview-glyphs">
<span class="step size-12"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span><span class="step size-14"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span><span class="step size-16"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span><span class="step size-18"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span><span class="step size-21"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span><span class="step size-24"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span><span class="step size-36"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span><span class="step size-48"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span><span class="step size-60"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span><span class="step size-72"><span class="letters">Pp</span><i id="fa-hourglass" class="fa-hourglass"></i></span>
</div>
<div class="preview-scale">
<span class="step">12</span><span class="step">14</span><span class="step">16</span><span class="step">18</span><span class="step">21</span><span class="step">24</span><span class="step">36</span><span class="step">48</span><span class="step">60</span><span class="step">72</span>
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-hourglass" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf10e;" />
</div>
</div>
<div class="glyph">
<div class="preview-glyphs">
<span class="step size-12"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span><span class="step size-14"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span><span class="step size-16"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span><span class="step size-18"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span><span class="step size-21"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span><span class="step size-24"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span><span class="step size-36"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span><span class="step size-48"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span><span class="step size-60"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span><span class="step size-72"><span class="letters">Pp</span><i id="fa-inbox" class="fa-inbox"></i></span>
@ -453,7 +472,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-inbox" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf10e;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf10f;" />
</div>
</div>
@ -466,7 +485,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-key-modern" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf10f;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf110;" />
</div>
</div>
@ -479,7 +498,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-link" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf110;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf111;" />
</div>
</div>
@ -492,7 +511,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-pencil" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf111;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf112;" />
</div>
</div>
@ -505,7 +524,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-plus" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf112;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf113;" />
</div>
</div>
@ -518,7 +537,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-question-circle" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf113;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf114;" />
</div>
</div>
@ -531,7 +550,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-sign-in" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf114;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf115;" />
</div>
</div>
@ -544,7 +563,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-sign-out" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf115;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf116;" />
</div>
</div>
@ -557,7 +576,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-star" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf116;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf117;" />
</div>
</div>
@ -570,7 +589,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-star-o" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf117;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf118;" />
</div>
</div>
@ -583,7 +602,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-table" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf118;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf119;" />
</div>
</div>
@ -596,7 +615,21 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-th-list" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf119;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11a;" />
</div>
</div>
<div class="glyph">
<div class="preview-glyphs">
<span class="step size-12"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span><span class="step size-14"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span><span class="step size-16"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span><span class="step size-18"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span><span class="step size-21"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span><span class="step size-24"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span><span class="step size-36"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span><span class="step size-48"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span><span class="step size-60"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span><span class="step size-72"><span class="letters">Pp</span><i id="fa-times" class="fa-times"></i></span>
</div>
<div class="preview-scale">
<span class="step">12</span><span class="step">14</span><span class="step">16</span><span class="step">18</span><span class="step">21</span><span class="step">24</span><span class="step">36</span><span class="step">48</span><span class="step">60</span><span class="step">72</span>
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-times" />
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-close" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11b;" />
</div>
</div>
@ -609,7 +642,20 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-trash-o" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11a;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11c;" />
</div>
</div>
<div class="glyph">
<div class="preview-glyphs">
<span class="step size-12"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span><span class="step size-14"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span><span class="step size-16"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span><span class="step size-18"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span><span class="step size-21"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span><span class="step size-24"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span><span class="step size-36"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span><span class="step size-48"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span><span class="step size-60"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span><span class="step size-72"><span class="letters">Pp</span><i id="fa-undo" class="fa-undo"></i></span>
</div>
<div class="preview-scale">
<span class="step">12</span><span class="step">14</span><span class="step">16</span><span class="step">18</span><span class="step">21</span><span class="step">24</span><span class="step">36</span><span class="step">48</span><span class="step">60</span><span class="step">72</span>
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-undo" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11d;" />
</div>
</div>
@ -622,7 +668,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-user" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11b;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11e;" />
</div>
</div>
@ -635,7 +681,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-user-circle-o" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11c;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11f;" />
</div>
</div>
@ -648,7 +694,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-user-plus" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11d;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf120;" />
</div>
</div>
@ -661,7 +707,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-users" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11e;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf121;" />
</div>
</div>
@ -674,7 +720,7 @@
</div>
<div class="usage">
<input class="class" type="text" readonly="readonly" onClick="this.select();" value=".fa-wrench" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf11f;" />
<input class="point" type="text" readonly="readonly" onClick="this.select();" value="&amp;#xf122;" />
</div>
</div>

@ -52,21 +52,24 @@
.fa-google::before { content: "\f10b"; }
.fa-history::before { content: "\f10c"; }
.fa-home::before { content: "\f10d"; }
.fa-inbox::before { content: "\f10e"; }
.fa-key-modern::before { content: "\f10f"; }
.fa-link::before { content: "\f110"; }
.fa-pencil::before { content: "\f111"; }
.fa-plus::before { content: "\f112"; }
.fa-question-circle::before { content: "\f113"; }
.fa-sign-in::before { content: "\f114"; }
.fa-sign-out::before { content: "\f115"; }
.fa-star::before { content: "\f116"; }
.fa-star-o::before { content: "\f117"; }
.fa-table::before { content: "\f118"; }
.fa-th-list::before { content: "\f119"; }
.fa-trash-o::before { content: "\f11a"; }
.fa-user::before { content: "\f11b"; }
.fa-user-circle-o::before { content: "\f11c"; }
.fa-user-plus::before { content: "\f11d"; }
.fa-users::before { content: "\f11e"; }
.fa-wrench::before { content: "\f11f"; }
.fa-hourglass::before { content: "\f10e"; }
.fa-inbox::before { content: "\f10f"; }
.fa-key-modern::before { content: "\f110"; }
.fa-link::before { content: "\f111"; }
.fa-pencil::before { content: "\f112"; }
.fa-plus::before { content: "\f113"; }
.fa-question-circle::before { content: "\f114"; }
.fa-sign-in::before { content: "\f115"; }
.fa-sign-out::before { content: "\f116"; }
.fa-star::before { content: "\f117"; }
.fa-star-o::before { content: "\f118"; }
.fa-table::before { content: "\f119"; }
.fa-th-list::before { content: "\f11a"; }
.fa-times::before, .fa-close::before { content: "\f11b"; }
.fa-trash-o::before { content: "\f11c"; }
.fa-undo::before { content: "\f11d"; }
.fa-user::before { content: "\f11e"; }
.fa-user-circle-o::before { content: "\f11f"; }
.fa-user-plus::before { content: "\f120"; }
.fa-users::before { content: "\f121"; }
.fa-wrench::before { content: "\f122"; }

Binary file not shown.

@ -1,11 +1,11 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<!--
2018-8-4: Created with FontForge (http://fontforge.org)
2018-8-5: Created with FontForge (http://fontforge.org)
-->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<metadata>
Created by FontForge 20170805 at Sat Aug 4 10:56:23 2018
Created by FontForge 20170805 at Sun Aug 5 14:30:22 2018
By ondra
The Fork Awesome font is licensed under the SIL OFL 1.1 (http://scripts.sil.org/OFL). Fork Awesome is a fork based of off Font Awesome 4.7.0 by Dave Gandy. More info on licenses at https://forkawesome.github.io
</metadata>
@ -22,7 +22,7 @@ The Fork Awesome font is licensed under the SIL OFL 1.1 (http://scripts.sil.org/
bbox="-0.14014 -256.168 2048 1536.01"
underline-thickness="89.6"
underline-position="-179.2"
unicode-range="U+0020-F11F"
unicode-range="U+0020-F122"
/>
<missing-glyph />
<glyph glyph-name="space" unicode=" " horiz-adv-x="200"
@ -81,66 +81,75 @@ c18 0 32 -14 32 -32z" />
<glyph glyph-name="home" unicode="&#xf10d;" horiz-adv-x="1612"
d="M1382 544v-480c0 -35 -29 -64 -64 -64h-384v384h-256v-384h-384c-35 0 -64 29 -64 64v480c0 2 1 4 1 6l575 474l575 -474c1 -2 1 -4 1 -6zM1605 613l-62 -74c-5 -6 -13 -10 -21 -11h-3c-8 0 -15 2 -21 7l-692 577l-692 -577c-7 -5 -15 -8 -24 -7c-8 1 -16 5 -21 11
l-62 74c-11 13 -9 34 4 45l719 599c42 35 110 35 152 0l244 -204v195c0 18 14 32 32 32h192c18 0 32 -14 32 -32v-408l219 -182c13 -11 15 -32 4 -45z" />
<glyph glyph-name="inbox" unicode="&#xf10e;"
<glyph glyph-name="hourglass" unicode="&#xf10e;"
d="M1504 -64c18 0 32 -14 32 -32v-128c0 -18 -14 -32 -32 -32h-1472c-18 0 -32 14 -32 32v128c0 18 14 32 32 32h1472zM130 0c19 337 294 518 478 640c-184 122 -459 303 -478 640h1276c-19 -337 -294 -518 -478 -640c184 -122 459 -303 478 -640h-1276zM1504 1536
c18 0 32 -14 32 -32v-128c0 -18 -14 -32 -32 -32h-1472c-18 0 -32 14 -32 32v128c0 18 14 32 32 32h1472z" />
<glyph glyph-name="inbox" unicode="&#xf10f;"
d="M1023 576h316c-2 5 -3 11 -5 16l-212 496h-708l-212 -496c-2 -5 -3 -11 -5 -16h316l95 -192h320zM1536 546v-482c0 -35 -29 -64 -64 -64h-1408c-35 0 -64 29 -64 64v482c0 36 11 89 25 123l238 552c14 33 54 59 89 59h832c35 0 75 -26 89 -59l238 -552
c14 -34 25 -87 25 -123z" />
<glyph glyph-name="key-modern" unicode="&#xf10f;" horiz-adv-x="1792"
<glyph glyph-name="key-modern" unicode="&#xf110;" horiz-adv-x="1792"
d="M546 1536v0c139 1 278 -52 383 -158c142 -141 187 -343 137 -525l726 -726v-319c0 -35 -29 -64 -64 -64h-300l-45 45l135 226l-46 45l-225 -135l-45 46l134 225l-45 45l-225 -134l-46 45l135 225l-45 46l-243 -139l-186 186c-182 -50 -382 -5 -524 136
c-211 212 -209 556 4 770c107 106 246 159 385 160zM405 1290v0c-41 0 -82 -16 -113 -47c-63 -63 -63 -163 0 -226s164 -63 227 0s63 163 0 226c-31 31 -73 47 -114 47z" />
<glyph glyph-name="link" unicode="&#xf110;" horiz-adv-x="1632"
<glyph glyph-name="link" unicode="&#xf111;" horiz-adv-x="1632"
d="M1440 320c0 26 -10 50 -28 68l-208 208c-18 18 -43 28 -68 28c-29 0 -52 -11 -72 -32c33 -33 72 -61 72 -112c0 -53 -43 -96 -96 -96c-51 0 -79 39 -112 72c-21 -20 -33 -43 -33 -73c0 -25 10 -50 28 -68l206 -207c18 -18 43 -27 68 -27s50 9 68 26l147 146
c18 18 28 42 28 67zM737 1025c0 25 -10 50 -28 68l-206 207c-18 18 -43 28 -68 28s-50 -10 -68 -27l-147 -146c-18 -18 -28 -42 -28 -67c0 -26 10 -50 28 -68l208 -208c18 -18 43 -27 68 -27c29 0 52 10 72 31c-33 33 -72 61 -72 112c0 53 43 96 96 96c51 0 79 -39 112 -72
c21 20 33 43 33 73zM1632 320c0 -76 -31 -150 -85 -203l-147 -146c-54 -54 -127 -83 -203 -83c-77 0 -150 30 -204 85l-206 207c-54 54 -83 127 -83 203c0 79 32 154 88 209l-88 88c-55 -56 -129 -88 -208 -88c-76 0 -150 30 -204 84l-208 208c-55 55 -84 127 -84 204
c0 76 31 150 85 203l147 146c54 54 127 83 203 83c77 0 150 -30 204 -85l206 -207c54 -54 83 -127 83 -203c0 -79 -32 -154 -88 -209l88 -88c55 56 129 88 208 88c76 0 150 -30 204 -84l208 -208c55 -55 84 -127 84 -204z" />
<glyph glyph-name="pencil" unicode="&#xf111;" horiz-adv-x="1515"
<glyph glyph-name="pencil" unicode="&#xf112;" horiz-adv-x="1515"
d="M363 0l91 91l-235 235l-91 -91v-107h128v-128h107zM886 928c0 13 -9 22 -22 22c-6 0 -12 -2 -17 -7l-542 -542c-5 -5 -7 -11 -7 -17c0 -13 9 -22 22 -22c6 0 12 2 17 7l542 542c5 5 7 11 7 17zM832 1120l416 -416l-832 -832h-416v416zM1515 1024c0 -34 -14 -67 -37 -90
l-166 -166l-416 416l166 165c23 24 56 38 90 38s67 -14 91 -38l235 -234c23 -24 37 -57 37 -91z" />
<glyph glyph-name="plus" unicode="&#xf112;" horiz-adv-x="1408"
<glyph glyph-name="plus" unicode="&#xf113;" horiz-adv-x="1408"
d="M1408 800v-192c0 -53 -43 -96 -96 -96h-416v-416c0 -53 -43 -96 -96 -96h-192c-53 0 -96 43 -96 96v416h-416c-53 0 -96 43 -96 96v192c0 53 43 96 96 96h416v416c0 53 43 96 96 96h192c53 0 96 -43 96 -96v-416h416c53 0 96 -43 96 -96z" />
<glyph glyph-name="question-circle" unicode="&#xf113;"
<glyph glyph-name="question-circle" unicode="&#xf114;"
d="M896 160v192c0 18 -14 32 -32 32h-192c-18 0 -32 -14 -32 -32v-192c0 -18 14 -32 32 -32h192c18 0 32 14 32 32zM1152 832c0 183 -192 320 -364 320c-163 0 -285 -70 -371 -213c-9 -14 -5 -32 8 -42l132 -100c5 -4 12 -6 19 -6c9 0 19 4 25 12c47 60 67 78 86 92
c17 12 50 24 86 24c64 0 123 -41 123 -85c0 -52 -27 -78 -88 -106c-71 -32 -168 -115 -168 -212v-36c0 -18 14 -32 32 -32h192c18 0 32 14 32 32c0 23 29 72 76 99c76 43 180 101 180 253zM1536 640c0 -424 -344 -768 -768 -768s-768 344 -768 768s344 768 768 768
s768 -344 768 -768z" />
<glyph glyph-name="sign-in" unicode="&#xf114;"
<glyph glyph-name="sign-in" unicode="&#xf115;"
d="M1184 640c0 -17 -7 -33 -19 -45l-544 -544c-12 -12 -28 -19 -45 -19c-35 0 -64 29 -64 64v288h-448c-35 0 -64 29 -64 64v384c0 35 29 64 64 64h448v288c0 35 29 64 64 64c17 0 33 -7 45 -19l544 -544c12 -12 19 -28 19 -45zM1536 992v-704c0 -159 -129 -288 -288 -288
h-320c-17 0 -32 15 -32 32c0 28 -13 96 32 96h320c88 0 160 72 160 160v704c0 88 -72 160 -160 160h-288c-25 0 -64 -5 -64 32c0 28 -13 96 32 96h320c159 0 288 -129 288 -288z" />
<glyph glyph-name="sign-out" unicode="&#xf115;" horiz-adv-x="1568"
<glyph glyph-name="sign-out" unicode="&#xf116;" horiz-adv-x="1568"
d="M640 96c0 -28 13 -96 -32 -96h-320c-159 0 -288 129 -288 288v704c0 159 129 288 288 288h320c17 0 32 -15 32 -32c0 -28 13 -96 -32 -96h-320c-88 0 -160 -72 -160 -160v-704c0 -88 72 -160 160 -160h288c25 0 64 5 64 -32zM1568 640c0 -17 -7 -33 -19 -45l-544 -544
c-12 -12 -28 -19 -45 -19c-35 0 -64 29 -64 64v288h-448c-35 0 -64 29 -64 64v384c0 35 29 64 64 64h448v288c0 35 29 64 64 64c17 0 33 -7 45 -19l544 -544c12 -12 19 -28 19 -45z" />
<glyph glyph-name="star" unicode="&#xf116;" horiz-adv-x="1664"
<glyph glyph-name="star" unicode="&#xf117;" horiz-adv-x="1664"
d="M1664 889c0 -18 -13 -35 -26 -48l-363 -354l86 -500c1 -7 1 -13 1 -20c0 -26 -12 -50 -41 -50c-14 0 -28 5 -40 12l-449 236l-449 -236c-13 -7 -26 -12 -40 -12c-29 0 -42 24 -42 50c0 7 1 13 2 20l86 500l-364 354c-12 13 -25 30 -25 48c0 30 31 42 56 46l502 73
l225 455c9 19 26 41 49 41s40 -22 49 -41l225 -455l502 -73c24 -4 56 -16 56 -46z" />
<glyph glyph-name="star-o" unicode="&#xf117;" horiz-adv-x="1664"
<glyph glyph-name="star-o" unicode="&#xf118;" horiz-adv-x="1664"
d="M1137 532l306 297l-422 62l-189 382l-189 -382l-422 -62l306 -297l-73 -421l378 199l377 -199zM1664 889c0 -18 -13 -35 -26 -48l-363 -354l86 -500c1 -7 1 -13 1 -20c0 -27 -12 -50 -41 -50c-14 0 -28 5 -40 12l-449 236l-449 -236c-13 -7 -26 -12 -40 -12
c-29 0 -42 24 -42 50c0 7 1 13 2 20l86 500l-364 354c-12 13 -25 30 -25 48c0 30 31 42 56 46l502 73l225 455c9 19 26 41 49 41s40 -22 49 -41l225 -455l502 -73c24 -4 56 -16 56 -46z" />
<glyph glyph-name="table" unicode="&#xf118;" horiz-adv-x="1664"
<glyph glyph-name="table" unicode="&#xf119;" horiz-adv-x="1664"
d="M512 160v192c0 18 -14 32 -32 32h-320c-18 0 -32 -14 -32 -32v-192c0 -18 14 -32 32 -32h320c18 0 32 14 32 32zM512 544v192c0 18 -14 32 -32 32h-320c-18 0 -32 -14 -32 -32v-192c0 -18 14 -32 32 -32h320c18 0 32 14 32 32zM1024 160v192c0 18 -14 32 -32 32h-320
c-18 0 -32 -14 -32 -32v-192c0 -18 14 -32 32 -32h320c18 0 32 14 32 32zM512 928v192c0 18 -14 32 -32 32h-320c-18 0 -32 -14 -32 -32v-192c0 -18 14 -32 32 -32h320c18 0 32 14 32 32zM1024 544v192c0 18 -14 32 -32 32h-320c-18 0 -32 -14 -32 -32v-192
c0 -18 14 -32 32 -32h320c18 0 32 14 32 32zM1536 160v192c0 18 -14 32 -32 32h-320c-18 0 -32 -14 -32 -32v-192c0 -18 14 -32 32 -32h320c18 0 32 14 32 32zM1024 928v192c0 18 -14 32 -32 32h-320c-18 0 -32 -14 -32 -32v-192c0 -18 14 -32 32 -32h320c18 0 32 14 32 32z
M1536 544v192c0 18 -14 32 -32 32h-320c-18 0 -32 -14 -32 -32v-192c0 -18 14 -32 32 -32h320c18 0 32 14 32 32zM1536 928v192c0 18 -14 32 -32 32h-320c-18 0 -32 -14 -32 -32v-192c0 -18 14 -32 32 -32h320c18 0 32 14 32 32zM1664 1248v-1088c0 -88 -72 -160 -160 -160
h-1344c-88 0 -160 72 -160 160v1088c0 88 72 160 160 160h1344c88 0 160 -72 160 -160z" />
<glyph glyph-name="th-list" unicode="&#xf119;" horiz-adv-x="1792"
<glyph glyph-name="th-list" unicode="&#xf11a;" horiz-adv-x="1792"
d="M512 288v-192c0 -53 -43 -96 -96 -96h-320c-53 0 -96 43 -96 96v192c0 53 43 96 96 96h320c53 0 96 -43 96 -96zM512 800v-192c0 -53 -43 -96 -96 -96h-320c-53 0 -96 43 -96 96v192c0 53 43 96 96 96h320c53 0 96 -43 96 -96zM1792 288v-192c0 -53 -43 -96 -96 -96h-960
c-53 0 -96 43 -96 96v192c0 53 43 96 96 96h960c53 0 96 -43 96 -96zM512 1312v-192c0 -53 -43 -96 -96 -96h-320c-53 0 -96 43 -96 96v192c0 53 43 96 96 96h320c53 0 96 -43 96 -96zM1792 800v-192c0 -53 -43 -96 -96 -96h-960c-53 0 -96 43 -96 96v192c0 53 43 96 96 96
h960c53 0 96 -43 96 -96zM1792 1312v-192c0 -53 -43 -96 -96 -96h-960c-53 0 -96 43 -96 96v192c0 53 43 96 96 96h960c53 0 96 -43 96 -96z" />
<glyph glyph-name="trash-o" unicode="&#xf11a;" horiz-adv-x="1408"
<glyph glyph-name="times" unicode="&#xf11b;" horiz-adv-x="1188"
d="M1188 214c0 -25 -10 -50 -28 -68l-136 -136c-18 -18 -43 -28 -68 -28s-50 10 -68 28l-294 294l-294 -294c-18 -18 -43 -28 -68 -28s-50 10 -68 28l-136 136c-18 18 -28 43 -28 68s10 50 28 68l294 294l-294 294c-18 18 -28 43 -28 68s10 50 28 68l136 136
c18 18 43 28 68 28s50 -10 68 -28l294 -294l294 294c18 18 43 28 68 28s50 -10 68 -28l136 -136c18 -18 28 -43 28 -68s-10 -50 -28 -68l-294 -294l294 -294c18 -18 28 -43 28 -68z" />
<glyph glyph-name="trash-o" unicode="&#xf11c;" horiz-adv-x="1408"
d="M512 800v-576c0 -18 -14 -32 -32 -32h-64c-18 0 -32 14 -32 32v576c0 18 14 32 32 32h64c18 0 32 -14 32 -32zM768 800v-576c0 -18 -14 -32 -32 -32h-64c-18 0 -32 14 -32 32v576c0 18 14 32 32 32h64c18 0 32 -14 32 -32zM1024 800v-576c0 -18 -14 -32 -32 -32h-64
c-18 0 -32 14 -32 32v576c0 18 14 32 32 32h64c18 0 32 -14 32 -32zM1152 76v948h-896v-948c0 -48 27 -76 32 -76h832c5 0 32 28 32 76zM480 1152h448l-48 117c-3 4 -12 10 -17 11h-317c-6 -1 -14 -7 -17 -11zM1408 1120v-64c0 -18 -14 -32 -32 -32h-96v-948
c0 -110 -72 -204 -160 -204h-832c-88 0 -160 90 -160 200v952h-96c-18 0 -32 14 -32 32v64c0 18 14 32 32 32h309l70 167c20 49 80 89 133 89h320c53 0 113 -40 133 -89l70 -167h309c18 0 32 -14 32 -32z" />
<glyph glyph-name="user" unicode="&#xf11b;" horiz-adv-x="1280"
<glyph glyph-name="undo" unicode="&#xf11d;"
d="M1536 640c0 -423 -345 -768 -768 -768c-229 0 -445 101 -591 277c-10 13 -9 32 2 43l137 138c7 6 16 9 25 9c9 -1 18 -5 23 -12c98 -127 245 -199 404 -199c282 0 512 230 512 512s-230 512 -512 512c-131 0 -255 -50 -348 -137l137 -138c19 -18 24 -46 14 -69
c-10 -24 -33 -40 -59 -40h-448c-35 0 -64 29 -64 64v448c0 26 16 49 40 59c23 10 51 5 69 -14l130 -129c141 133 332 212 529 212c423 0 768 -345 768 -768z" />
<glyph glyph-name="user" unicode="&#xf11e;" horiz-adv-x="1280"
d="M1280 137c0 -146 -96 -265 -213 -265h-854c-117 0 -213 119 -213 265c0 263 65 567 327 567c81 -79 191 -128 313 -128s232 49 313 128c262 0 327 -304 327 -567zM1024 1024c0 -212 -172 -384 -384 -384s-384 172 -384 384s172 384 384 384s384 -172 384 -384z" />
<glyph glyph-name="user-circle-o" unicode="&#xf11c;" horiz-adv-x="1792"
<glyph glyph-name="user-circle-o" unicode="&#xf11f;" horiz-adv-x="1792"
d="M896 1536c495 0 896 -401 896 -896c0 -492 -399 -896 -896 -896c-496 0 -896 403 -896 896c0 495 401 896 896 896zM1515 185c93 128 149 285 149 455c0 423 -345 768 -768 768s-768 -345 -768 -768c0 -170 56 -327 149 -455c36 179 123 327 306 327
c81 -79 191 -128 313 -128s232 49 313 128c183 0 270 -148 306 -327zM1280 832c0 -212 -172 -384 -384 -384s-384 172 -384 384s172 384 384 384s384 -172 384 -384z" />
<glyph glyph-name="user-plus" unicode="&#xf11d;" horiz-adv-x="2048"
<glyph glyph-name="user-plus" unicode="&#xf120;" horiz-adv-x="2048"
d="M704 640c-212 0 -384 172 -384 384s172 384 384 384s384 -172 384 -384s-172 -384 -384 -384zM1664 512h352c17 0 32 -15 32 -32v-192c0 -17 -15 -32 -32 -32h-352v-352c0 -17 -15 -32 -32 -32h-192c-17 0 -32 15 -32 32v352h-352c-17 0 -32 15 -32 32v192
c0 17 15 32 32 32h352v352c0 17 15 32 32 32h192c17 0 32 -15 32 -32v-352zM928 288c0 -70 58 -128 128 -128h256v-238c-49 -36 -111 -50 -171 -50h-874c-160 0 -267 96 -267 259c0 226 53 573 346 573c16 0 27 -7 39 -17c98 -75 193 -122 319 -122s221 47 319 122
c12 10 23 17 39 17c85 0 160 -32 217 -96h-223c-70 0 -128 -58 -128 -128v-192z" />
<glyph glyph-name="users" unicode="&#xf11e;" horiz-adv-x="1920"
<glyph glyph-name="users" unicode="&#xf121;" horiz-adv-x="1920"
d="M593 640c-104 -3 -198 -48 -265 -128h-134c-100 0 -194 48 -194 159c0 81 -3 353 124 353c21 0 125 -85 260 -85c46 0 90 8 133 23c-3 -22 -5 -44 -5 -66c0 -91 29 -181 81 -256zM1664 3c0 -162 -107 -259 -267 -259h-874c-160 0 -267 97 -267 259c0 226 53 573 346 573
c34 0 158 -139 358 -139s324 139 358 139c293 0 346 -347 346 -573zM640 1280c0 -141 -115 -256 -256 -256s-256 115 -256 256s115 256 256 256s256 -115 256 -256zM1344 896c0 -212 -172 -384 -384 -384s-384 172 -384 384s172 384 384 384s384 -172 384 -384zM1920 671
c0 -111 -94 -159 -194 -159h-134c-67 80 -161 125 -265 128c52 75 81 165 81 256c0 22 -2 44 -5 66c43 -15 87 -23 133 -23c135 0 239 85 260 85c127 0 124 -272 124 -353zM1792 1280c0 -141 -115 -256 -256 -256s-256 115 -256 256s115 256 256 256s256 -115 256 -256z" />
<glyph glyph-name="wrench" unicode="&#xf11f;" horiz-adv-x="1641"
<glyph glyph-name="wrench" unicode="&#xf122;" horiz-adv-x="1641"
d="M363 64c0 35 -29 64 -64 64s-64 -29 -64 -64s29 -64 64 -64s64 29 64 64zM1007 484l-682 -682c-23 -23 -56 -37 -90 -37s-67 14 -91 37l-106 108c-24 23 -38 56 -38 90s14 67 38 91l681 681c52 -131 157 -236 288 -288zM1641 919c0 -33 -12 -74 -23 -106
c-63 -178 -234 -301 -423 -301c-247 0 -448 201 -448 448s201 448 448 448c73 0 168 -22 229 -63c10 -7 16 -16 16 -28c0 -11 -7 -22 -16 -28l-293 -169v-224l193 -107c33 19 265 165 285 165s32 -15 32 -35z" />
</font>

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Binary file not shown.

@ -68,6 +68,7 @@ $(document).on('input keypress paste keyup', 'input[data-autoalias]', function (
window.Vue = require('vue');
Vue.component('column-editor', require('./components/ColumnEditor.vue'));
Vue.component('row-editor', require('./components/RowEditor.vue'));
Vue.component('v-icon', require('./components/Icon.vue'));
const app = new Vue({

@ -0,0 +1,86 @@
<template>
<table class="table table-hover table-sm table-fixed td-va-middle">
<thead>
<tr>
<th style="width:3rem" class="border-top-0"></th>
<th style="width:3rem" class="border-top-0"></th>
<th v-for="col in columns" :class="colClasses(col)">{{col.title}}</th>
</tr>
</thead>
<tbody>
<tr v-for="row in rows" :style="rowStyle(row)">
<td>
<a href="" :class="['btn','btn-outline-danger',{'active': row._remove}]"
@click.prevent="toggleRowDelete(row._id)">
<v-icon :class="row._remove ? 'fa-undo' : 'fa-trash-o'"
:alt="row._remove ? 'Undo Remove' : 'Remove'" />
</a>
</td>
<td>
<a href="" class="btn btn-outline-secondary"
@click.prevent="toggleRowEditing(row._id)">
<v-icon :class="row._editing ? 'fa-save' : 'fa-pencil'"
:alt="row._editing ? 'Save' : 'Edit'" />
</a>
</td>
<template v-if="row._editing">
<td v-for="col in columns" class="pr-0">
<input v-model="row[col.id]" class="form-control" type="text">
</td>
</template>
<template v-else>
<td v-for="col in columns">
{{ row[col.id] || '' }}
</td>
</template>
</tr>
</tbody>
</table>
</template>
<style lang="scss" scoped>
@import "base";
</style>
<script>
export default {
props: {
route: String,
xRows: Object, // key'd by _id
columns: Array,
},
data: function() {
return {
rows: this.xRows,
}
},
methods: {
toggleRowDelete(_id) {
this.$set(this.rows[_id], '_remove', !this.rows[_id]._remove);
},
toggleRowEditing(_id) {
this.$set(this.rows[_id], '_editing', !this.rows[_id]._editing);
},
colClasses(col) {
return [
'border-top-0',
{
'text-danger': col._remove,
'strike': col._remove,
'text-success': col._new
}
]
},
rowStyle(row) {
return {
opacity: row._remove? .8 : 1,
backgroundColor: row._remove? '#FFC4CC': 'transparent'
}
},
}
}
</script>

@ -16,3 +16,12 @@
.box-shadow {
box-shadow: 0 2px 3px rgba(black, .3);
}
.strike {
text-decoration: line-through;
}
// for busy loaders etc
.opacity-fade {
transition: opacity .3s ease-in-out;
}

@ -20,6 +20,8 @@ html {
@import "bootstrap-customizations/button";
@import "bootstrap-customizations/responsive";
@import "bootstrap-customizations/typography";
@import "bootstrap-customizations/nav";
@import "bootstrap-customizations/table";
.bio-table {
td {

@ -15,3 +15,23 @@
.border-2 {
border-width: 2px !important;
}
.rounded-top-0 {
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
}
.rounded-left-0 {
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.rounded-right-0 {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.rounded-bottom-0 {
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}

@ -0,0 +1,9 @@
.table-fixed {
table-layout: fixed;
}
.td-va-middle {
td, th {
vertical-align: middle !important;
}
}

@ -4,24 +4,23 @@
@php
/** @var object[] $columns */
/** @var \App\Tables\Changeset[] $changeset */
/** @var \App\Models\Row[] $rows */
@endphp
<table class="table table-hover table-sm">
<thead>
<tr>
<th>_id</th>
@foreach($columns as $col)
<th>{{$col->name}} ("{{ $col->title }}") [ {{$col->id}} ]</th>
<th>{{ $col->title }}</th>
@endforeach
</tr>
</thead>
<tbody>
@foreach($rows as $row)
<tr>
<td>{{ $row->_id }}</td>
@foreach($columns as $col)
<td data-id="{{ $row->_id }}">{{ $row->{$col->name} }}</td>
<td>{{ $row->{$col->name} }}</td>
@endforeach
</tr>
@endforeach

@ -9,21 +9,17 @@
<nav aria-label="Table action buttons">
@sr(Table actions)
{{-- Disabled until implemented --}}
@if(guest() || !user()->confirmed || user()->ownsTable($table))
{{-- Guest, unconfirmed, or a table owner --}}
{{-- Passive fork buttons with counter --}}
<a href="" class="btn btn-outline-primary py-1 btn-sm" title="Forks"
data-toggle="tooltip" data-placement="top">
<a href="" class="btn btn-outline-primary py-1 btn-sm" @tooltip(Forks)>
@icon(fa-code-fork, sr:Forks)&nbsp;
{{ $table->forks_count ?: '–' }}
</a>
{{-- Passive favourite buttons with counter --}}
<a href="" class="btn btn-outline-primary py-1 btn-sm"
title="Favourites" data-toggle="tooltip" data-placement="top">
<a href="" class="btn btn-outline-primary py-1 btn-sm" @tooltip(Favourites)>
@icon(fa-star, sr:Favourites)&nbsp;
{{ $table->favourites_count ?: '–' }}
</a>
@ -33,12 +29,10 @@
{{-- Active fork button | counter --}}
<div class="btn-group" role="group" aria-label="Fork">
<a href="" class="btn btn-outline-primary py-1 btn-sm btn-square" title="Fork"
data-toggle="tooltip" data-placement="top">
<a href="" class="btn btn-outline-primary py-1 btn-sm btn-square" @tooltip(Fork)>
@icon(fa-code-fork, sr:Fork)
</a>
<a href="" class="btn btn-outline-primary py-1 btn-sm" title="Fork Count"
data-toggle="tooltip" data-placement="top">
<a href="" class="btn btn-outline-primary py-1 btn-sm" @tooltip(Fork Count)>
{{ $table->forks_count ?: '–' }}
</a>
</div>
@ -46,18 +40,15 @@
{{-- Active favourite button | counter --}}
<div class="btn-group" role="group" aria-label="Favourite">
@if(user()->favouritesTable($table))
<a href="" class="btn btn-outline-primary py-1 btn-sm btn-square" title="Un-favourite"
data-toggle="tooltip" data-placement="top">
<a href="" class="btn btn-outline-primary py-1 btn-sm btn-square" @tooltip(Un-favourite)>
@icon(fa-star, sr:Un-favourite)
</a>
@else
<a href="" class="btn btn-outline-primary py-1 btn-sm btn-square" title="Favourite"
data-toggle="tooltip" data-placement="top">
<a href="" class="btn btn-outline-primary py-1 btn-sm btn-square" @tooltip(Favourite)>
@icon(fa-star-o, sr:Favourite)
</a>
@endif
<a href="" class="btn btn-outline-primary py-1 btn-sm" title="Favourite Count"
data-toggle="tooltip" data-placement="top">
<a href="" class="btn btn-outline-primary py-1 btn-sm" @tooltip(Favourite Count)>
{{ $table->favourites_count ?: '–' }}
</a>
</div>
@ -65,29 +56,25 @@
@endif
{{-- Comments button with counter --}}
<a href="" class="btn btn-outline-primary py-1 btn-sm" title="Comments"
data-toggle="tooltip" data-placement="top">
<a href="" class="btn btn-outline-primary py-1 btn-sm" @tooltip(Comments)>
@icon(fa-comment, sr:Comments)&nbsp;
{{ $table->comments_count ?: '–' }}
</a>
{{-- Active proposals button | counter --}}
<div class="btn-group" role="group" aria-label="Fork">
<a href="" class="btn btn-outline-primary py-1 btn-sm" title="Change Proposals"
data-toggle="tooltip" data-placement="top">
<a href="" class="btn btn-outline-primary py-1 btn-sm" @tooltip(Change Proposals)>
@icon(fa-inbox fa-pr, sr:Change Proposals){{ $table->proposals_count ?: '–' }}
</a>
@auth
@if(user()->ownsTable($table))
{{-- Table owner logged in --}}
<a href="" class="btn btn-outline-primary py-1 btn-sm btn-square" title="Draft Change"
data-toggle="tooltip" data-placement="top">
<a href="{{ $table->draftRoute }}" class="btn btn-outline-primary py-1 btn-sm btn-square" @tooltip(Draft Change)>
@icon(fa-pencil, sr:Draft Change)
</a>
@else
{{-- Not a table owner --}}
<a href="" class="btn btn-outline-primary py-1 btn-sm btn-square" title="Propose Change"
data-toggle="tooltip" data-placement="top">
<a href="{{ $table->draftRoute }}" class="btn btn-outline-primary py-1 btn-sm btn-square" @tooltip(Propose Change)>
@icon(fa-pencil, sr:Propose Change)
</a>
@endif
@ -96,9 +83,8 @@
@if(authed() && user()->ownsTable($table))
{{-- Table opts menu for table owner --}}
<a href="{{ $table->settingsRoute }}" class="btn btn-outline-primary py-1 btn-sm"
title="Table Options" data-toggle="tooltip" data-placement="top">
<a href="{{ $table->settingsRoute }}" class="btn btn-outline-primary py-1 btn-sm" @tooltip(Table Options)>
@icon(fa-wrench, sr:Table Options)
</a>
@endif
@endif
</nav>

@ -17,8 +17,7 @@
<h1 class="mx-3">{{ $table->title }}</h1>
<a href="{{ $table->viewRoute }}" class="btn btn-outline-primary py-1 btn-sm"
title="Back to Table" data-toggle="tooltip" data-placement="top">
<a href="{{ $table->viewRoute }}" class="btn btn-outline-primary py-1 btn-sm" @tooltip(Back to Table)>
@icon(fa-table, sr:Back to Table)
</a>
</div>

@ -0,0 +1,6 @@
@php($tab='add-rows')
@extends('table.propose.layout')
@section('tab-content')
...
@stop

@ -0,0 +1,43 @@
@php
$tab = 'edit-rows';
/** @var \App\Tables\Column[] $columns */
/** @var \App\Tables\Changeset $changeset */
/** @var \App\Models\Row[]|Illuminate\Pagination\Paginator $rows */
/** @var \App\Models\Table $table */
@endphp
@extends('table.propose.layout')
@section('tab-content')
@if($rows->hasPages())
<div class="col-md-12 d-flex">
<nav class="text-center" aria-label="Pages of the table">
{{ $rows->links(null, ['ulClass' => 'mb-0']) }}
</nav>
</div>
@endif
<div class="col-12">
@php
$transformed = $rows->keyBy('_id')->map(function($r) use ($changeset) {
/** @var \App\Tables\Changeset $changeset */
return $changeset->transformRow($r, true);
});
@endphp
<table is="row-editor"
route="{{$table->getDraftRoute('update')}}"
:columns="{{toJSON($columns)}}"
:x-rows="{{toJSON($transformed)}}">
</table>
</div>
@if($rows->hasPages())
<div class="col-md-12 d-flex">
<nav class="text-center" aria-label="Pages of the table">
{{ $rows->links(null, ['ulClass' => 'mb-0']) }}
</nav>
</div>
@endif
@stop

@ -0,0 +1,58 @@
{{-- Basic table view --}}
@extends('layouts.app')
@php
/** @var \App\Models\Table $table */
if (!isset($tab) || $tab == '') $tab = 'edit-rows';
@endphp
@section('content')
<div class="row justify-content-start px-3">
<div class="d-flex w-100 align-items-center">
<small class="flex-grow-1" style="font-size: 120%;">
<a href="{{ route('profile.view', $table->owner->name) }}" class="link-no-color">{{ $table->owner->handle }}</a>{{--
--}}<span class="px-1">/</span>{{--
--}}<b>{{ $table->name }}</b>
</small>
<h1 class="mx-3">{{ $table->title }}</h1>
<a href="" class="btn btn-outline-danger mr-2" @tooltip(Discard changes)>
@icon(fa-close, sr:Discard)
</a>
@if(user()->ownsTable($table))
<a href="" class="btn btn-outline-success" @tooltip(Save the changes and apply them as a new table revision)>
@icon(fa-save fa-pr)Commit
</a>
@else
<a href="" class="btn btn-outline-success" @tooltip(Submit your changes for review by the table owner)>
@icon(fa-save fa-pr)Submit
</a>
@endif
</div>
</div>
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link{{ $tab=='edit-rows'?' active':'' }}" href="{{ $table->getDraftRoute('edit-rows') }}">Edit Rows</a>
</li>
<li class="nav-item">
<a class="nav-link{{ $tab=='add-rows'?' active':'' }}" href="{{ $table->getDraftRoute('add-rows') }}">Add Rows</a>
</li>
<li class="nav-item">
<a class="nav-link{{ $tab=='manage-columns'?' active':'' }}" href="{{ $table->getDraftRoute('manage-columns') }}">Columns</a>
</li>
<li class="nav-item">
<a class="nav-link{{ $tab=='review'?' active':'' }}" href="{{ $table->getDraftRoute('review') }}">Note & Review</a>
</li>
<li class="nav-item ml-auto pt-2 pr-2 opacity-fade" style="opacity:0" id="draft-busy">
@icon(fa-hourglass, Working...)
</li>
</ul>
<div class="row justify-content-center mb-2">
@yield('tab-content')
</div>{{-- End of row --}}
@endsection

@ -0,0 +1,6 @@
@php($tab='manage-columns')
@extends('table.propose.layout')
@section('tab-content')
...
@stop

@ -0,0 +1,6 @@
@php($tab='review')
@extends('table.propose.layout')
@section('tab-content')
...
@stop

@ -17,7 +17,7 @@
<h1 class="mx-3">{{ $table->title }}</h1>
@include('table._action-buttons')
@include('table._view-action-buttons')
</div>
</div>

@ -49,6 +49,9 @@ Route::group(['middleware' => ['auth', 'activated']], function () {
Route::get('@{user}/{table}/settings', 'TableController@settings')->name('table.conf');
Route::post('@{user}/{table}/settings', 'TableController@storeSettings')->name('table.storeConf');
Route::post('@{user}/{table}/delete', 'TableController@delete')->name('table.delete');
Route::post('@{user}/{table}/draft/update', 'TableEditController@draftUpdate')->name('table.draft-update');
Route::get('@{user}/{table}/draft/{tab?}', 'TableEditController@draft')->name('table.draft');
});
// Routes for all authed users

Loading…
Cancel
Save