Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added support for hot-key function #224

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion base-component/webroot/screen/includes/WebrootVue.qvt.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ along with this software (see the LICENSE.md file). If not, see
<#-- to build a layout use the handy Quasar tool: https://quasar.dev/layout-builder -->
<q-layout view="hHh LpR fFf">
<q-header reveal bordered class="${headerClass}" id="top"><q-toolbar style="font-size:15px;">
<q-btn dense flat icon="menu" @click="toggleLeftOpen()"></q-btn>
<q-btn dense flat icon="menu" @click="toggleLeftOpen()" moqui-key="`"></q-btn>

<#assign headerLogoList = sri.getThemeValues("STRT_HEADER_LOGO")>
<#if headerLogoList?has_content>
Expand Down
4 changes: 2 additions & 2 deletions base-component/webroot/screen/includes/WebrootVue.vuet.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ along with this software (see the LICENSE.md file). If not, see
<div id="top"><nav class="navbar navbar-inverse navbar-fixed-top"><#-- navbar-fixed-top navbar-static-top --><div class="container-fluid">
<#-- Brand and toggle get grouped for better mobile display -->
<header class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse" >
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<#assign headerLogoList = sri.getThemeValues("STRT_HEADER_LOGO")>
<#if headerLogoList?has_content><m-link href="/apps" class="navbar-brand"><img src="${sri.buildUrl(headerLogoList?first).getUrl()}" alt="Home"></m-link></#if>
<#if headerLogoList?has_content><m-link href="/apps" class="navbar-brand" moqui-key="`"><img src="${sri.buildUrl(headerLogoList?first).getUrl()}" alt="Home"></m-link></#if>
<#assign headerTitleList = sri.getThemeValues("STRT_HEADER_TITLE")>
<#if headerTitleList?has_content><div class="navbar-brand">${ec.resource.expand(headerTitleList?first, "")}</div></#if>
</header>
Expand Down
2 changes: 1 addition & 1 deletion base-component/webroot/screen/includes/navbar.html.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<span class="icon-bar"></span>
</button>
<#assign headerLogoList = sri.getThemeValues("STRT_HEADER_LOGO")>
<#if headerLogoList?has_content><a href="${sri.buildUrl("/apps").getUrl()}" class="navbar-brand"><img src="${sri.buildUrl(headerLogoList?first).getUrl()}" alt="Home"></a></#if>
<#if headerLogoList?has_content><a href="${sri.buildUrl("/apps").getUrl()}" class="navbar-brand" moqui-key="`" ><img src="${sri.buildUrl(headerLogoList?first).getUrl()}" alt="Home" ></a></#if>
<#assign headerTitleList = sri.getThemeValues("STRT_HEADER_TITLE")>
<#if headerTitleList?has_content><div class="navbar-brand">${ec.resource.expand(headerTitleList?first, "")}</div></#if>
</header>
Expand Down
121 changes: 121 additions & 0 deletions base-component/webroot/screen/webroot/js/MoquiLib.js
Original file line number Diff line number Diff line change
Expand Up @@ -598,3 +598,124 @@ if (window.Chart) {
};
}
*/
window.addEventListener('keydown', function(event) {
let e = event || window.event;
if (e == null) return
let keyUpper
if (e.key !== undefined) {
keyUpper = e.key.toUpperCase();
} else if (e.which !== undefined) {
keyUpper = String.fromCharCode(e.which).toUpperCase();
}

let altDown = e.altKey;
let ctrlDown = e.ctrlKey;
let processed = false;

//alt key
if(altDown){
if((keyUpper>="A" && keyUpper <="Z") || (keyUpper>="0" && keyUpper<="9") ){
let focusList = $(`input[moqui-key="${keyUpper}"], textarea[moqui-key="${keyUpper}"], div[moqui-key="${keyUpper}"], select[moqui-key="${keyUpper}"]`)
if(focusList && focusList.length == 0) return

let totalCount = focusList.length
let focusIndex = 0
if(moqui.moquiTraceFocusInputElement){
if(totalCount > 1){
for(i=0;i<totalCount;i++){
if(focusList[i] == moqui.moquiTraceFocusInputElement){
break
}
}
if(i <= totalCount-2) {
focusIndex = i+1
}
}

}
moqui.moquiTraceFocusInputElement = focusList[focusIndex]
focusList[focusIndex].focus();

processed = true;

}
}
//ctrl key
if(ctrlDown){
//char "`" stand for open left menu
if((keyUpper>="A" && keyUpper <="Z") || (keyUpper>="0" && keyUpper<="9") || (keyUpper == "`") ){

let curEl = $(`button[moqui-key="${keyUpper}"], a[moqui-key="${keyUpper}"]`)
if(curEl && curEl.length == 0){
curEl = $(`span[moqui-key="${keyUpper}"]`) //m-container-dialog
}
if(curEl && curEl.length == 0 ) return;
//maybe many EL, but what is it for?
curEl[0].click();
processed = true;
}

}

//Enter key
if(keyUpper === "ENTER"){


let curInput = e.target;
if(curInput == null) return

if(e.target.tagName != "INPUT") {
console.log(`Current target tagName is ${e.target.tagName}, not INPUT `)
return
}

if(curInput.form == null) return
let inputList = curInput.form.querySelectorAll("input,textarea");

let availableInputList = [];
inputList.forEach(function(item){
let available = true;
if (item.readOnly) available = false;
if (item.disabled) available = false;
if (available){
//quasar's checkbox and radio are special
if ((item.className.indexOf("q-checkbox__native")!== -1) || (item.className.indexOf("q-radio__native") !== -1)){
availableInputList.push(item.parentElement.parentElement)
}else{
availableInputList.push(item);
}

}

})

//if only one INPUT, don't process
if (availableInputList.length<=1) return;


//Loop in the same form
let curIndex = 0;
for(let i =0; i< availableInputList.length; i++){
if(availableInputList[i] === curInput){
curIndex = i;
break;
}
}
if (curIndex == availableInputList.length - 1){
curIndex = -1;
}

availableInputList[curIndex+1].focus();
processed = true;

}

//prevent default
if(processed){
if (e.preventDefault){
e.preventDefault()
}else {
window.event.returnValue = false;
}
}
});
63 changes: 50 additions & 13 deletions base-component/webroot/screen/webroot/js/WebrootVue.js
Original file line number Diff line number Diff line change
Expand Up @@ -336,16 +336,30 @@ Vue.component('box-body', {
data: function() { return this.height ? { dialogStyle:{'max-height':this.height+'px', 'overflow-y':'auto'}} : {dialogStyle:{}}},
template: '<div class="panel-body" :style="dialogStyle"><slot></slot></div>'
});

//todo maybe no need moquiKey in container-dialog because moquiKey is processed in DefaultScreenMacros.vuet.FTL
Vue.component('container-dialog', {
props: { id:{type:String,required:true}, title:String, width:{type:String,'default':'760'}, openDialog:{type:Boolean,'default':false} },
props: { id:{type:String,required:true}, title:String, width:{type:String,'default':'760'}, openDialog:{type:Boolean,'default':false}, moquiKey:String },
data: function() {
var viewportWidth = $(window).width();
return { isHidden:true, dialogStyle:{width:(this.width < viewportWidth ? this.width : viewportWidth) + 'px'}}},
computed: {
upperCaseMoquiKey: function(){
if(this.moquiKey){
return this.moquiKey.toUpperCase();
}else{
return null;
}
}
},
template:
'<div :id="id" class="modal dynamic-dialog" aria-hidden="true" style="display:none;" tabindex="-1">' +
'<div class="modal-dialog" :style="dialogStyle"><div class="modal-content">' +
'<div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>' +
'<h4 class="modal-title">{{title}}</h4></div>' +
'<div class="modal-header"><button type="button" class="close" data-dismiss="modal" aria-hidden="true" :moqui-key="upperCaseMoquiKey">&times;</button>' +
'<h4 class="modal-title">' +
'{{title}}' +
'<span v-if ="moquiKey"> ( <span style="text-decoration:underline">{{upperCaseMoquiKey}}</span> )</span> '+
'</h4></div>' +
'<div class="modal-body"><slot></slot></div>' +
'</div></div>' +
'</div>',
Expand Down Expand Up @@ -845,14 +859,14 @@ Vue.component('form-list', {
/* ========== form field widget components ========== */
Vue.component('date-time', {
props: { id:String, name:{type:String,required:true}, value:String, type:{type:String,'default':'date-time'},
size:String, format:String, tooltip:String, form:String, required:String, autoYear:String, minuteStep:{type:Number,'default':5} },
size:String, format:String, tooltip:String, form:String, required:String, autoYear:String, minuteStep:{type:Number,'default':5}, moquiKey:String },
template:
'<div v-if="type==\'time\'" class="input-group time" :id="id">' +
'<input type="text" class="form-control" :pattern="timePattern" :id="id?(id+\'_itime\'):\'\'" :name="name" :value="value" :size="sizeVal" :form="form">' +
'<input type="text" class="form-control" :pattern="timePattern" :id="id?(id+\'_itime\'):\'\'" :name="name" :value="value" :size="sizeVal" :form="form" :moqui-key="upperCaseMoquiKey">' +
'<span class="input-group-addon"><span class="fa fa-clock-o"></span></span>' +
'</div>' +
'<div v-else class="input-group date" :id="id">' +
'<input ref="dateInput" @focus="focusDate" @blur="blurDate" type="text" class="form-control" :id="id?(id+\'_idate\'):\'\'" :name="name" :value="value" :size="sizeVal" :form="form" :required="required == \'required\' ? true : false">' +
'<input ref="dateInput" @focus="focusDate" @blur="blurDate" type="text" class="form-control" :id="id?(id+\'_idate\'):\'\'" :name="name" :value="value" :size="sizeVal" :form="form" :required="required == \'required\' ? true : false" :moqui-key="upperCaseMoquiKey">' +
'<span class="input-group-addon"><span class="fa fa-calendar"></span></span>' +
'</div>',
methods: {
Expand Down Expand Up @@ -883,7 +897,14 @@ Vue.component('date-time', {
(this.type === 'date' ? ['l', 'L', 'YYYY-MM-DD'] : ['YYYY-MM-DD HH:mm', 'YYYY-MM-DD HH:mm:ss', 'MM/DD/YYYY HH:mm']); },
sizeVal: function() { var size = this.size; if (size && size.length > 0) { return size; }
return this.type === 'time' ? '9' : (this.type === 'date' ? '10' : '16'); },
timePattern: function() { return '^(?:(?:([01]?\\d|2[0-3]):)?([0-5]?\\d):)?([0-5]?\\d)$'; }
timePattern: function() { return '^(?:(?:([01]?\\d|2[0-3]):)?([0-5]?\\d):)?([0-5]?\\d)$'; },
upperCaseMoquiKey: function(){
if(this.moquiKey){
return this.moquiKey.toUpperCase();
}else{
return null;
}
}
},
mounted: function() {
var vm = this;
Expand Down Expand Up @@ -950,9 +971,9 @@ Vue.component('drop-down', {
props: { options:Array, value:[Array,String], combo:Boolean, allowEmpty:Boolean, multiple:String, submitOnSelect:Boolean, optionsUrl:String,
serverSearch:{type:Boolean,'default':false}, serverDelay:{type:Number,'default':300}, serverMinLength:{type:Number,'default':1},
optionsParameters:Object, labelField:String, valueField:String, dependsOn:Object, dependsOptional:Boolean,
optionsLoadInit:Boolean, form:String, tooltip:String },
optionsLoadInit:Boolean, form:String, tooltip:String, moquiKey:String },
data: function() { return { curData:null, s2Opts:null, lastVal:null } },
template: '<select :form="form" :data-title="tooltip"><slot></slot></select>',
template: '<select :form="form" :data-title="tooltip" :moqui-key="upperCaseMoquiKey" ><slot></slot></select>',
methods: {
processOptionList: function(list, page, term) {
var newData = [];
Expand Down Expand Up @@ -1056,7 +1077,16 @@ Vue.component('drop-down', {
}
},
computed: { curVal: { get: function() { return $(this.$el).select2().val(); },
set: function(value) { $(this.$el).val(value).trigger('change'); /* console.log('set ' + $(this.$el).attr('name') + ' to ' + this.curVal); */ } } },
set: function(value) { $(this.$el).val(value).trigger('change'); /* console.log('set ' + $(this.$el).attr('name') + ' to ' + this.curVal); */ } },
upperCaseMoquiKey: function(){
if(this.moquiKey){
return this.moquiKey.toUpperCase();
}else{
return null;
}
}

},
watch: {
value: function(value) { this.curVal = value; },
options: function(options) { this.curData = options; },
Expand Down Expand Up @@ -1090,18 +1120,25 @@ Vue.component('text-autocomplete', {
type:String, size:String, maxlength:String, disabled:Boolean, validationClasses:String, dataVvValidation:String,
required:Boolean, pattern:String, tooltip:String, form:String, delay:{type:Number,'default':300},
url:{type:String,required:true}, dependsOn:Object, acParameters:Object, minLength:{type:Number,'default':1},
showValue:Boolean, useActual:Boolean, skipInitial:Boolean },
showValue:Boolean, useActual:Boolean, skipInitial:Boolean, moquiKey:String },
data: function () { return { delayTimeout:null } },
template:
'<span><input ref="acinput" :id="acId" :name="acName" :type="type" :value="valueText" :size="size" :maxlength="maxlength" :disabled="disabled"' +
' :class="allClasses" :data-vv-validation="validationClasses" :required="required" :pattern="pattern"' +
' :data-toggle="tooltipToggle" :title="tooltip" autocomplete="off" :form="form">' +
' :data-toggle="tooltipToggle" :title="tooltip" autocomplete="off" :form="form" :moqui-key="upperCaseMoquiKey" >' +
'<input ref="hidden" :id="id" type="hidden" :name="name" :value="value" :form="form">' +
'<span ref="show" v-if="showValue" :id="showId" class="form-autocomplete-value">{{valueText}}</span>' +
'</span>',
computed: { acId: function() { return this.id + '_ac'; }, acName: function() { return this.name + '_ac'; },
allClasses: function() { return 'form-control typeahead' + (this.validationClasses ? ' ' + this.validationClasses : ''); },
showId: function() { return this.id + '_show'; }, tooltipToggle: function() { return this.tooltip && this.tooltip.length > 0 ? 'tooltip' : null; }
showId: function() { return this.id + '_show'; }, tooltipToggle: function() { return this.tooltip && this.tooltip.length > 0 ? 'tooltip' : null; },
upperCaseMoquiKey: function(){
if(this.moquiKey){
return this.moquiKey.toUpperCase();
}else{
return null;
}
}
},
methods: { fetchResults: function(query, syncResults, asyncResults) {
if (this.delayTimeout) { clearTimeout(this.delayTimeout); }
Expand Down
Loading