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>
<div>
<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>
<tr>
<th v-if="sortable"></th>
<th>Name</th>
<th>Type</th>
<th>Title</th>
<th :style="tdWidthStyle('name')">Name</th>
<th :style="tdWidthStyle('type')">Type</th>
<th :style="tdWidthStyle('title')">Title</th>
<th v-if="!newTable"></th>
<th></th>
<th></th>
@ -19,65 +19,81 @@ Complex animated column editor for the table edit page
</thead>
<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}">
<td class="btn-group" v-if="sortable">
<button type="button" class="btn btn-outline-secondary drag-btn"
@keyup.up="move(i, -1)"
@keyup.down="move(i, 1)"
:ref="`col${i}-sort`"
@mousedown="beginDrag(i, $event)">
<v-icon class="fa-bars" alt="Drag" />
</button><!--
--><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i==0}]" v-if="manualSort"
@click.prevent="move(i, -1)">
<v-icon class="fa-chevron-up" alt="Move Up" />
</button><!--
--><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i == (columns.length-1)}]" v-if="manualSort"
@click.prevent="move(i, 1)">
<v-icon class="fa-chevron-down" alt="Move Down" />
</button>
</td>
<td>
<input v-model="col.name"
@input="markColNeedSave(i)"
class="form-control"
type="text"
style="width: 140px">
<td v-if="sortable">
<span class="btn-group">
<button type="button" class="btn btn-outline-secondary drag-btn"
@keyup.up="move(i, -1)"
@keyup.down="move(i, 1)"
:ref="`col${i}-sort`"
:style="{visibility: (columns.length > 1) ? 'visible' : 'hidden'}"
@mousedown="beginDrag(i, $event)">
<v-icon class="fa-bars" alt="Drag" />
</button><!--
--><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i==0}]" v-if="manualSort"
@click.prevent="move(i, -1)">
<v-icon class="fa-chevron-up" alt="Move Up" />
</button><!--
--><button type="button" :class="['btn', 'btn-outline-secondary', {disabled: i == (columns.length-1)}]" v-if="manualSort"
@click.prevent="move(i, 1)">
<v-icon class="fa-chevron-down" alt="Move Down" />
</button>
</span>
</td>
<td>
<select v-model="col.type"
@input="markColNeedSave(i)"
class="form-control custom-select"
style="width: 110px">
<option v-for="t in colTypes" :value="t">{{t}}</option>
</select>
</td>
<td>
<input v-model="col.title"
@input="markColNeedSave(i)"
class="form-control"
type="text"
style="width: 170px">
</td>
<template v-if="col._editing || newTable">
<!-- Editable cells -->
<td :style="tdWidthStyle('name')">
<input v-model="col.name"
class="form-control"
type="text">
</td>
<td :style="tdWidthStyle('type')">
<select v-model="col.type"
class="form-control custom-select">
<option v-for="t in colTypes" :value="t">{{t}}</option>
</select>
</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">
<button type="button" :class="['btn', col._dirty ? 'btn-info' : 'btn-outline-secondary']"
@click.prevent="saveCol(i)">
<v-icon class="fa-save" alt="Save" />
</button>
<!-- Save button -->
<a href="" :class="['btn','btn-outline-secondary',{'disabled': col._remove}]"
@click.prevent="toggleColEditing(col)">
<v-icon :class="col._editing ? 'fa-save' : 'fa-pencil'"
:alt="col._editing ? 'Save' : 'Edit'" />
</a>
</td>
<td>
<!-- Delete button -->
<button type="button" :class="delBtnClass(col)"
@click.prevent="toggleColDelete(i)">
@click.prevent="toggleColDelete(col)">
<v-icon :class="col._remove ? 'fa-undo' : 'fa-trash-o'"
:alt="col._remove ? 'Undo Remove' : 'Remove'" />
</button>
</td>
<td>
<!-- Add button -->
<button type="button" :class="['btn', 'btn-outline-secondary']"
v-if="i === columns.length - 1"
@click.prevent="addCol()">
@ -93,8 +109,10 @@ Complex animated column editor for the table edit page
<style lang="scss" scoped>
@import "base";
table {
border-collapse: collapse;
@media screen and (min-width: 625px) {
table.new-table {
margin-left: -53px;
}
}
td, th {
@ -102,15 +120,23 @@ Complex animated column editor for the table edit page
@include py(1);
}
th {
border-top: 0 none;
}
tr.dragging {
position: relative;
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 {
// fake hover
background-color: $secondary;
color: color-yiq($secondary);
box-shadow: $btn-box-shadow, 0 0 0 $btn-focus-width rgba($secondary, .5);
}
.col-list-enter-active {
@ -169,6 +195,7 @@ export default {
},
data: function () {
return {
newColNum: 0,
columns: this.xColumns,
colTypes: ['string', 'int', 'float', 'bool'],
}
@ -179,11 +206,35 @@ export default {
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 */
addCol () {
if (this.newTable) {
this.columns.push({
id: Math.random().toString(),
id: '_' + (this.newColNum++),
name: '',
type: 'string',
title: '',
@ -218,11 +269,6 @@ export default {
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 */
beginDrag (i, evt) {
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. */
toggleColDelete(n) {
let col = this.columns[n]
toggleColDelete(col) {
if (_.isUndefined(col)) return
let n = this.colPos(col)
if (this.newTable) {
if (this.columns.length == 1) return; // can't delete the last col
// hard delete
this.columns.splice(n, 1)
} 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>

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

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

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

Loading…
Cancel
Save