improve styles and behavior of the col editor, preparing for backend impl
This commit is contained in:
@@ -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,11 +19,13 @@ 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">
|
||||
<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><!--
|
||||
@@ -35,49 +37,63 @@ Complex animated column editor for the table edit page
|
||||
@click.prevent="move(i, 1)">
|
||||
<v-icon class="fa-chevron-down" alt="Move Down" />
|
||||
</button>
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<template v-if="col._editing || newTable">
|
||||
<!-- Editable cells -->
|
||||
<td :style="tdWidthStyle('name')">
|
||||
<input v-model="col.name"
|
||||
@input="markColNeedSave(i)"
|
||||
class="form-control"
|
||||
type="text"
|
||||
style="width: 140px">
|
||||
type="text">
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<td :style="tdWidthStyle('type')">
|
||||
<select v-model="col.type"
|
||||
@input="markColNeedSave(i)"
|
||||
class="form-control custom-select"
|
||||
style="width: 110px">
|
||||
class="form-control custom-select">
|
||||
<option v-for="t in colTypes" :value="t">{{t}}</option>
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<td :style="tdWidthStyle('title')">
|
||||
<input v-model="col.title"
|
||||
@input="markColNeedSave(i)"
|
||||
class="form-control"
|
||||
type="text"
|
||||
style="width: 170px">
|
||||
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',
|
||||
{
|
||||
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>
|
||||
|
||||
Reference in New Issue
Block a user