nuVistA/htdocs/ViewDocEdit.vue

178 lines
6.4 KiB
Vue

<template>
<div v-if="record !== null" class="card mb-3 shadow">
<div class="card-header d-flex justify-content-between align-items-center">
<span>{{record['~.01'] && record['~.01'].description || 'Document'}}</span>
<a class="widget" @click="() => update(true)">✔</a>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item"><DateTimePicker v-model="datetime" /></li>
<li class="list-group-item">
<div class="input-group">
<span class="input-group-text">Subject</span>
<input type="text" class="form-control" v-model="subject" />
</div>
</li>
<li class="list-group-item"><textarea ref="textarea" class="form-control" v-model="text" @keydown.tab.exact.prevent="tab" @keydown.shift.tab.exact.prevent="untab" /></li>
</ul>
<div v-if="saved" class="card-footer" style="text-align: right;">Saved at {{saved.toLocaleString()}}</div>
</div>
</template>
<style scoped>
a.widget {
cursor: default;
text-decoration: none;
}
textarea {
font-family: monospace;
tab-size: 8;
}
</style>
<script>
import { debounce } from './util.mjs';
import { strptime, strftime } from './fmdatetime.mjs';
import DateTimePicker from './DateTimePicker.vue';
function untab({ input, size=8, tab='\t', space=' ', join=true}={}) {
input = input.split('\n');
for(var i = input.length - 1; i >= 0; --i) input[i] = untab_line(input[i], size, tab, space);
return join ? input.join('\n') : input;
}
function untab_line(line, size=8, tab='\t', space=' ') {
var res = '', index = 0, offset = 0, next, count;
while((next = line.indexOf(tab, index)) >= 0) {
count = size - (next + offset)%size;
res += line.substring(index, next) + Array(count + 1).join(space);
offset += count - 1;
index = next + 1;
}
return res + line.substring(index);
}
function retab({ input, size=8, tab='\t', space=' ' }={}) {
var re_space = new RegExp(space.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '+', 'g');
return input.replace(re_space, function(m) { return Array(Math.ceil(m.length/size) + 1).join(tab); });
}
function wrap({ input, width=80, cut=false, tabsize=8, tab='\t', space=' ', untab=false, join=true }={}) {
var input = input.split('\n'), lines, res = [];
if(untab) {
for(var i = 0; i < input.length; ++i) {
lines = wrap_line_split(untab_line(input[i], tabsize, tab, space), width, cut);
for(var j = 0; j < lines.length; ++j) res.push(lines[j].replace(/(\s)\s+$/, '$1'));
}
return join ? res.join('\n') : res;
} else {
for(var i = 0; i < input.length; ++i) {
lines = wrap_line_split(untab_line(input[i], tabsize, tab, tab), width, cut); // replace tabs with placeholder tabs
for(var j = 0; j < lines.length; ++j) res.push(lines[j].replace(/(\s)\s+$/, '$1'));
}
res = retab({ input: res.join('\n'), size: tabsize, tab, space: tab }); // collapse placeholder tabs
return join ? res : res.split('\n');
}
}
function wrap_line(str, width=80, cut=false, brk='\n') {
if(!str) return str;
return str.match(new RegExp('.{1,' + width + '}(\\s+|$)' + (cut ? '|.{' + width + '}|.+$' : '|\\S+?(\\s+|$)'), 'g')).join(brk);
}
function wrap_line_split(str, width=80, cut=false) {
if(!str) return [str];
return str.match(new RegExp('.{1,' + width + '}(\\s+|$)' + (cut ? '|.{' + width + '}|.+$' : '|\\S+?(\\s+|$)'), 'g'));
}
export default {
components: {
DateTimePicker
},
props: {
client: Object,
dfn: String,
ien: String
},
emits: {
'accept': null,
'update': Object
},
data() {
return {
record: null,
saved: null
};
},
computed: {
datetime: {
get() { return this.record && this.record['~1301'] && this.record['~1301'].value; },
set(value) { this.record['~1301'].value = strftime(strptime(value)); this.autosave(); }
},
subject: {
get() { return this.record && this.record['~1701'] && this.record['~1701'].value; },
set(value) { this.record['~1701'].value = this.record['~.07'].value = value; this.autosave(); }
},
text: {
get() { return this.record && this.record.text; },
set(value) { this.record.text = value; this.autosave(); }
}
},
watch: {
text() {
this.resize();
}
},
methods: {
async tab(evt) {
var target = event.target, value = target.value, start = target.selectionStart, end = target.selectionEnd;
if(start == end) document.execCommand('insertText', false, '\t');
else {
start = target.selectionStart = value.lastIndexOf('\n', start - 1) + 1;
end = target.selectionEnd = value.indexOf('\n', end); if(end < 0) end = value.length;
var selection = value.substring(start, end);
document.execCommand('insertText', false, selection.replace(/^/gm, '\t'));
await this.$nextTick();
target.selectionStart = start;
target.selectionEnd = end + selection.split('\n').length;
}
},
async untab(evt) {
var target = event.target, value = target.value;
var start = target.selectionStart = value.lastIndexOf('\n', target.selectionStart - 1) + 1;
var end = target.selectionEnd = value.indexOf('\n', target.selectionEnd); if(end < 0) end = value.length;
var selection = value.substring(start, end);
document.execCommand('insertText', false, selection.replace(/^\t/gm, ''));
await this.$nextTick();
target.selectionStart = start;
target.selectionEnd = end - (selection.match(/^\t/gm) || []).length;
},
async update(accept) {
var res = this.record.reduce((acc, val) => (acc['"' + val.field + '"'] = val.value, acc), {});
var text = wrap({input: this.text || '\x01', width: 80, cut: true, untab: false, join: false});
for(var i = 0; i < text.length; ++i) res['"TEXT","' + (i + 1) + '","0"'] = text[i];
this.$emit('update', res);
if(await this.client.TIU_UPDATE_RECORD(this.ien, res, 0)) {
await this.client.TIU_GET_RECORD_TEXT_FLUSH(this.ien);
this.saved = new Date();
if(accept) this.$emit('accept');
}
}
},
created() {
this.resize = debounce(async function() {
var textarea = this.$refs.textarea;
textarea.style.height = 'auto';
await this.$nextTick();
textarea.style.height = textarea.scrollHeight + 4 + 'px';
}, 50);
this.autosave = debounce(this.update, 2000);
this.$watch(
() => (this.client, this.ien, {}),
async function() {
this.record = (this.client) && (this.ien) ? await this.client.TIU_LOAD_RECORD_FOR_EDIT(this.ien, '.01;.06;.07;1301;1204;1208;1701;1205;1405;2101;70201;70202') : [];
this.resize();
},
{ immediate: true }
);
}
};
</script>