|
|
@ -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> |
|
|
|