improve styles and behavior of the col editor, preparing for backend impl

pull/35/head
Ondřej Hruška 6 years ago
parent ef6e89bab6
commit 81261aba8b
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 218
      resources/assets/js/components/ColumnEditor.vue
  2. 21
      resources/assets/js/components/RowsEditor.vue
  3. 4
      resources/assets/sass/bootstrap-customizations/_table.scss
  4. 1
      resources/views/table/create.blade.php

@ -5,13 +5,13 @@ Complex animated column editor for the table edit page
<template> <template>
<div> <div>
<input type="hidden" :name="name" :value="JSON.stringify(columns)" v-if="newTable"> <input type="hidden" :name="name" :value="JSON.stringify(columns)" v-if="newTable">
<table class="editor-table"> <table :class="[{'table': !newTable},{'new-table': newTable},'table-narrow','table-sm','table-fixed','td-va-middle']">
<thead> <thead>
<tr> <tr>
<th v-if="sortable"></th> <th v-if="sortable"></th>
<th>Name</th> <th :style="tdWidthStyle('name')">Name</th>
<th>Type</th> <th :style="tdWidthStyle('type')">Type</th>
<th>Title</th> <th :style="tdWidthStyle('title')">Title</th>
<th v-if="!newTable"></th> <th v-if="!newTable"></th>
<th></th> <th></th>
<th></th> <th></th>
@ -19,65 +19,81 @@ Complex animated column editor for the table edit page
</thead> </thead>
<transition-group name="col-list" tag="tbody" ref="col-list"> <transition-group name="col-list" tag="tbody" ref="col-list">
<tr v-for="(col, i) in columns" :key="col.id" :ref="`col${i}`" :class="{dragging: col._dragging}"> <tr v-for="(col, i) in columns" :key="col.id" :ref="`col${i}`" :class="{dragging: col._dragging}">
<td class="btn-group" v-if="sortable"> <td v-if="sortable">
<button type="button" class="btn btn-outline-secondary drag-btn" <span class="btn-group">
@keyup.up="move(i, -1)" <button type="button" class="btn btn-outline-secondary drag-btn"
@keyup.down="move(i, 1)" @keyup.up="move(i, -1)"
:ref="`col${i}-sort`" @keyup.down="move(i, 1)"
@mousedown="beginDrag(i, $event)"> :ref="`col${i}-sort`"
<v-icon class="fa-bars" alt="Drag" /> :style="{visibility: (columns.length > 1) ? 'visible' : 'hidden'}"
</button><!-- @mousedown="beginDrag(i, $event)">
--><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i==0}]" v-if="manualSort" <v-icon class="fa-bars" alt="Drag" />
@click.prevent="move(i, -1)"> </button><!--
<v-icon class="fa-chevron-up" alt="Move Up" /> --><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i==0}]" v-if="manualSort"
</button><!-- @click.prevent="move(i, -1)">
--><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i == (columns.length-1)}]" v-if="manualSort" <v-icon class="fa-chevron-up" alt="Move Up" />
@click.prevent="move(i, 1)"> </button><!--
<v-icon class="fa-chevron-down" alt="Move Down" /> --><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i == (columns.length-1)}]" v-if="manualSort"
</button> @click.prevent="move(i, 1)">
</td> <v-icon class="fa-chevron-down" alt="Move Down" />
</button>
<td> </span>
<input v-model="col.name"
@input="markColNeedSave(i)"
class="form-control"
type="text"
style="width: 140px">
</td> </td>
<td> <template v-if="col._editing || newTable">
<select v-model="col.type" <!-- Editable cells -->
@input="markColNeedSave(i)" <td :style="tdWidthStyle('name')">
class="form-control custom-select" <input v-model="col.name"
style="width: 110px"> class="form-control"
<option v-for="t in colTypes" :value="t">{{t}}</option> type="text">
</select> </td>
</td>
<td :style="tdWidthStyle('type')">
<td> <select v-model="col.type"
<input v-model="col.title" class="form-control custom-select">
@input="markColNeedSave(i)" <option v-for="t in colTypes" :value="t">{{t}}</option>
class="form-control" </select>
type="text" </td>
style="width: 170px">
</td> <td :style="tdWidthStyle('title')">
<input v-model="col.title"
class="form-control"
type="text">
</td>
</template>
<template v-else>
<!-- Value fields -->
<td v-for='cell in ["name", "type", "title"]'>
<span class="text-danger strike" title="Original value" v-if="isChanged(col, cell)">{{col._orig[cell]}}</span>
<span>{{ col[cell] }}</span>
<v-icon v-if="isChanged(col, cell)"
@click="revertCell(col, cell)"
class="fa-undo text-danger pointer"
alt="Revert Change" />
</td>
</template>
<td v-if="!newTable"> <td v-if="!newTable">
<button type="button" :class="['btn', col._dirty ? 'btn-info' : 'btn-outline-secondary']" <!-- Save button -->
@click.prevent="saveCol(i)"> <a href="" :class="['btn','btn-outline-secondary',{'disabled': col._remove}]"
<v-icon class="fa-save" alt="Save" /> @click.prevent="toggleColEditing(col)">
</button> <v-icon :class="col._editing ? 'fa-save' : 'fa-pencil'"
:alt="col._editing ? 'Save' : 'Edit'" />
</a>
</td> </td>
<td> <td>
<!-- Delete button -->
<button type="button" :class="delBtnClass(col)" <button type="button" :class="delBtnClass(col)"
@click.prevent="toggleColDelete(i)"> @click.prevent="toggleColDelete(col)">
<v-icon :class="col._remove ? 'fa-undo' : 'fa-trash-o'" <v-icon :class="col._remove ? 'fa-undo' : 'fa-trash-o'"
:alt="col._remove ? 'Undo Remove' : 'Remove'" /> :alt="col._remove ? 'Undo Remove' : 'Remove'" />
</button> </button>
</td> </td>
<td> <td>
<!-- Add button -->
<button type="button" :class="['btn', 'btn-outline-secondary']" <button type="button" :class="['btn', 'btn-outline-secondary']"
v-if="i === columns.length - 1" v-if="i === columns.length - 1"
@click.prevent="addCol()"> @click.prevent="addCol()">
@ -93,8 +109,10 @@ Complex animated column editor for the table edit page
<style lang="scss" scoped> <style lang="scss" scoped>
@import "base"; @import "base";
table { @media screen and (min-width: 625px) {
border-collapse: collapse; table.new-table {
margin-left: -53px;
}
} }
td, th { td, th {
@ -102,15 +120,23 @@ Complex animated column editor for the table edit page
@include py(1); @include py(1);
} }
th {
border-top: 0 none;
}
tr.dragging { tr.dragging {
position: relative; position: relative;
z-index: 1; z-index: 1;
background-color: $body-bg; //background-color: $body-bg;
}
tr.dragging .btn {
background-color: $body-bg; // no see-through buttons
} }
tr.dragging .drag-btn { tr.dragging .drag-btn {
// fake hover // fake hover
background-color: $secondary; background-color: $secondary;
color: color-yiq($secondary); color: color-yiq($secondary);
box-shadow: $btn-box-shadow, 0 0 0 $btn-focus-width rgba($secondary, .5);
} }
.col-list-enter-active { .col-list-enter-active {
@ -169,6 +195,7 @@ export default {
}, },
data: function () { data: function () {
return { return {
newColNum: 0,
columns: this.xColumns, columns: this.xColumns,
colTypes: ['string', 'int', 'float', 'bool'], colTypes: ['string', 'int', 'float', 'bool'],
} }
@ -179,11 +206,35 @@ export default {
query(this.route, data, sucfn, erfn) query(this.route, data, sucfn, erfn)
}, },
colPos(col) {
for (let n = 0; n < this.columns.length; n++) {
if (this.columns[n].id == col.id) return n;
}
throw `Col ${col.id} not found in list`;
},
/** Save a change */
submitColChange (col) {
if (_.isUndefined(col)) return
let n = this.colPos(col)
this.query({
action: 'col.update',
data: col
}, (resp) => {
this.$set(this.columns, n, resp.data)
}, (er) => {
if (!_.isUndefined(er.errors)) {
this.$set(this.columns[n], '_errors', er.errors)
}
})
},
/** Add a column at the end */ /** Add a column at the end */
addCol () { addCol () {
if (this.newTable) { if (this.newTable) {
this.columns.push({ this.columns.push({
id: Math.random().toString(), id: '_' + (this.newColNum++),
name: '', name: '',
type: 'string', type: 'string',
title: '', title: '',
@ -218,11 +269,6 @@ export default {
setTimeout(() => this.$refs[`col${pos2}-sort`][0].focus(), 0) setTimeout(() => this.$refs[`col${pos2}-sort`][0].focus(), 0)
}, },
/** Indicate a column is dirty and needs saving */
markColNeedSave(i) {
this.$set(this.columns[i], '_dirty', true)
},
/** User started dragging a column */ /** User started dragging a column */
beginDrag (i, evt) { beginDrag (i, evt) {
const column = this.columns[i] const column = this.columns[i]
@ -274,29 +320,14 @@ export default {
] ]
}, },
/** Save a column */
saveCol(n) {
let col = this.columns[n]
if (_.isUndefined(col)) return
this.query({
action: 'col.update',
data: col
}, (resp) => {
this.$set(this.columns, n, resp.data)
}, (er) => {
if (!_.isUndefined(er.errors)) {
this.$set(this.columns[n], '_errors', er.errors)
}
})
},
/** Delete / undelete a column; New columns vanish. */ /** Delete / undelete a column; New columns vanish. */
toggleColDelete(n) { toggleColDelete(col) {
let col = this.columns[n]
if (_.isUndefined(col)) return if (_.isUndefined(col)) return
let n = this.colPos(col)
if (this.newTable) { if (this.newTable) {
if (this.columns.length == 1) return; // can't delete the last col
// hard delete // hard delete
this.columns.splice(n, 1) this.columns.splice(n, 1)
} else { } else {
@ -317,6 +348,39 @@ export default {
}) })
} }
}, },
/** Toggle editing state - edit or save */
toggleColEditing (col) {
if (col._remove) return false // can't edit col marked for removal
let editing = !col._editing
let n = this.colPos(col)
if (!editing) {
this.submitColChange(col)
} else {
this.$set(this.columns[n], '_editing', true)
}
},
/** Test if a value cell is changed */
isChanged (col, cell) {
return col._changed && col._changed.indexOf(cell) > -1
},
/** Revert a value cell */
revertCell (col, cell) {
this.submitColChange(_.merge({}, col, {[cell]: col._orig[cell]}))
},
tdWidthStyle(cell) {
let w = 100;
if (cell == 'name') w = '14'
if (cell == 'type') w = '12'
if (cell == 'title') w = '14'
return {width: `${w}rem`};
}
} }
} }
</script> </script>

@ -13,8 +13,8 @@ Rows are identified by row._id, columns by col.id
<table class="table table-hover table-sm table-fixed td-va-middle"> <table class="table table-hover table-sm table-fixed td-va-middle">
<thead> <thead>
<tr> <tr>
<th style="width:3rem" class="border-top-0"></th> <th style="width:3rem"></th>
<th style="width:3rem" class="border-top-0"></th> <th style="width:3rem"></th>
<th v-for="col in columns" :class="colClasses(col)" :title="col.name">{{col.title}}</th> <th v-for="col in columns" :class="colClasses(col)" :title="col.name">{{col.title}}</th>
</tr> </tr>
</thead> </thead>
@ -62,6 +62,10 @@ Rows are identified by row._id, columns by col.id
<style lang="scss" scoped> <style lang="scss" scoped>
@import "base"; @import "base";
th {
border-top: 0 none;
}
</style> </style>
<script> <script>
@ -133,14 +137,11 @@ Rows are identified by row._id, columns by col.id
/** Compute classes for a value cell */ /** Compute classes for a value cell */
colClasses (col) { colClasses (col) {
return [ return {
'border-top-0', 'text-danger': col._remove,
{ 'strike': col._remove,
'text-danger': col._remove, 'text-success': col._new
'strike': col._remove, }
'text-success': col._new
}
]
}, },
/** Compute style for a row */ /** Compute style for a row */

@ -7,3 +7,7 @@
vertical-align: middle !important; vertical-align: middle !important;
} }
} }
.table.table-narrow {
width: auto;
}

@ -81,6 +81,7 @@
name: 'columns', name: 'columns',
xColumns: {!! old('columns', toJSON($columns)) !!}, xColumns: {!! old('columns', toJSON($columns)) !!},
newTable: true, newTable: true,
//sortable: false,
}) })
}); });
</script> </script>

Loading…
Cancel
Save