Replaced studentpicker sidebar by dropdown with arrows

This commit is contained in:
PMKuipers 2024-01-28 14:41:35 +01:00
parent fd1ab079a1
commit 2967f4a5bb
15 changed files with 239 additions and 60 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -181,6 +181,7 @@ export function init(contextid,categoryid) {
showStudentView(student){
app.selectedstudent = student;
app.studentstudyplan = null;
if (student) {
app.loadingstudyplan = true;
call([{
methodname: 'local_treestudyplan_get_user_studyplan',
@ -194,7 +195,7 @@ export function init(contextid,categoryid) {
notification.exception(error);
app.loadingstudyplan = false;
});
}
},
showOverview(){

View File

@ -480,9 +480,7 @@ export default {
let firststart = null;
for(const ix in this.value.pages) {
const page = this.value.pages[ix];
debug.info(`Checking page ${ix} - timing ${studyplanPageTiming(page)}`,page);
if(studyplanPageTiming(page) == "present") {
debug.info(`Found page nr ${ix} to be present`);
const s = new Date(page.startdate);
if( (!firststart) || firststart > s) {
startpageindex = ix;
@ -512,7 +510,7 @@ export default {
let maxLayer = -1;
for(let i = 0; i <= page.periods; i++){
const slot = line.slots[i];
// Determine the amount of used layers in a studyline slit
// Determine the amount of used layers in a studyline slot
for(const ix in line.slots[i].courses){
const item = line.slots[i].courses[ix];
if(item.layer > maxLayer){
@ -526,6 +524,7 @@ export default {
}
}
}
debug.info(`Counted ${maxLayer+1} layers for ${page.shortname}/${line.name}`);
return (maxLayer >= 0)?(maxLayer+1):1;
},
showslot(page,line,index, layeridx, type){
@ -630,7 +629,7 @@ export default {
<r-studyline-heading v-for="(line,lineindex) in page.studylines"
:key="line.id"
v-model="page.studylines[lineindex]"
:layers='countLineLayers(page,line)+1'
:layers='countLineLayers(line,page)+1'
:class=" 't-studyline' + ((lineindex%2==0)?' odd ' :' even ' )
+ ((lineindex==0)?' first ':' ')
+ ((lineindex==page.studylines.length-1)?' last ':' ')"
@ -652,7 +651,7 @@ export default {
<!-- Line by line add the items -->
<!-- The grid layout handles putting it in rows and columns -->
<template v-for="(line,lineindex) in page.studylines"
><template v-for="(layernr,layeridx) in countLineLayers(page,line)"
><template v-for="(layernr,layeridx) in countLineLayers(line,page)"
><template v-for="(n,index) in (page.periods+1)"
><r-studyline-slot
v-if="index > 0 && showslot(page,line, index, layeridx, 'gradable')"
@ -669,7 +668,8 @@ export default {
:layer="layeridx"
:class="'t-studyline ' + ((lineindex%2==0)?' odd ':' even ')
+ ((lineindex==0 && layernr==1)?' first ':' ')
+ ((lineindex==page.studylines.length-1)?' last ':' ')"
+ ((lineindex==page.studylines.length-1)?' last ':' ')
+ ((layernr == countLineLayers(line,page))?' lastlyr ':' ')"
></r-studyline-slot
><r-studyline-slot
v-if="showslot(page,line, index, layeridx, 'filter')"
@ -685,7 +685,8 @@ export default {
:class="'t-studyline ' + ((lineindex%2==0)?' odd ':' even ')
+ ((lineindex==0 && layernr==1)?' first ':'')
+ ((lineindex==page.studylines.length-1)?' last ':' ')
+ ((index==page.periods)?' rightmost':'')"
+ ((index==page.periods)?' rightmost':'')
+ ((layernr == countLineLayers(line,page))?' lastlyr ':' ')"
>
</r-studyline-slot
></template

View File

@ -1332,7 +1332,7 @@ export default {
for(let i = 0; i <= page.periods; i++){
if(line.slots[i]){
const slot = line.slots[i];
// Determine the amount of used layers in a studyline slit
// Determine the amount of used layers in a studyline slot
for(const ix in line.slots[i].courses){
const item = line.slots[i].courses[ix];
if(item.layer > maxLayer){

View File

@ -24,6 +24,12 @@ export default {
},
extrafields: {
show: "show@core"
},
prevnext: {
prev: "prev@core",
previous: "previous@core",
next: "next@core",
select: "selectanoptions@core",
}
});
// Create new eventbus for interaction between item components
@ -400,5 +406,155 @@ export default {
`,
});
Vue.component('s-prevnext-selector', {
props: {
value: {
type: Object,
default() { return null;}
},
options: {
type: Array,
},
grouped: {
type: Boolean,
default: false,
},
titlefield: {
type: String,
default: "title",
},
labelfield: {
type: String,
default: "label",
},
optionsfield: {
type: String,
default: "options",
},
defaultselectable: {
type: Boolean,
default: false,
},
variant: {
type: String,
default: "",
},
arrows: {
type: Boolean,
default: false,
},
},
data() {
return {
text: strings.prevnext,
};
},
computed: {
fields() {
const f = [];
if(this.defaultselectable) {
f.push(null);
}
if (this.grouped) {
for ( const gix in this.options) {
const group = this.options[gix];
for ( const ix in group) {
const v = this.options[ix];
f.push(v);
}
}
} else {
for ( const ix in this.options) {
const v = this.options[ix];
f.push(v);
}
}
return f;
},
bubblevalue: {
get() { return (this.value)?this.value:null;},
set(v) { this.$emit('input',(v)?v:null);},
}
},
methods: {
atFirst() {
if (this.fields.length > 0) {
return this.fields[0] == this.value;
}
return true; // Since it disables the button, do so if pressing it would make no sense.
},
atLast() {
if (this.fields.length > 0) {
const l = this.fields.length -1;
return this.fields[l] == this.value;
}
return true; // Since it disables the button, do so if pressing it would make no sense.
},
prev() {
if (this.fields.length > 0) {
const index = this.fields.indexOf(this.value);
if (index > 0) {
this.$emit("input",this.fields[index -1]);
this.$emit("change",this.fields[index -1]);
}
}
},
next() {
if (this.fields.length > 0) {
const index = this.fields.indexOf(this.value);
const l = this.fields.length -1;
if (index >= 0 && index < l) {
this.$emit("input",this.fields[index + 1]);
this.$emit("change",this.fields[index + 1]);
}
}
},
selectedchanged(value) {
this.$emit("change",value);
}
},
template: `
<div :class="'s-prevnext-selector '">
<b-button :variant='variant' @click="prev" :disabled="atFirst()"
><i v-if='arrows' class='fa fa-caret-left s-prevnext-arrow'></i
><template v-else>{{text.prev}}</template></b-button>
<b-form-select v-model="bubblevalue"
@change='selectedchanged'
>
<b-form-select-option
:disabled="!defaultselectable"
:value="null"
:class="(defaultselectable)?'font-italic text-primary ':'font-italic'"
><slot name="defaultlabel">{{text.select}}</slot></b-form-select-option>
</b-form-select-option-group>
<template v-if="grouped">
<b-form-select-option-group
v-for="(g,gi) in this.options"
:key="gi"
:label="g[grouptitlefield]"
>
<b-form-select-option
v-for="(o,i) in g[grouplistfield]"
:key="i"
:value="o"
><slot :value="o">{{o[titlefield]}}</slot></b-form-select-option>
</b-form-select-option-group>
</template>
<template v-else>
<b-form-select-option
v-for="(o,i) in this.options"
:key="i"
:value="o"
><slot :value="o">{{o[titlefield]}}</slot></b-form-select-option>
</template>
</b-form-select>
<b-button :variant='variant' @click="next" :disabled="atLast()"
><i v-if='arrows' class='fa fa-caret-right s-prevnext-arrow'></i
><template v-else>{{text.next}}</template></b-button>
</div>
`,
});
}
};

View File

@ -787,12 +787,12 @@
.path-local-treestudyplan .t-studyline-heading.last,
.path-local-treestudyplan .t-studyline-slot.last.newlyr,
.path-local-treestudyplan .r-studyline-heading.last,
.path-local-treestudyplan .r-studyline-slot.last,
.path-local-treestudyplan .r-studyline-slot.last.lastlyr,
.features-treestudyplan .t-studyline-drag:last-child,
.features-treestudyplan .t-studyline-heading.last,
.features-treestudyplan .t-studyline-slot.last.newlyr,
.features-treestudyplan .r-studyline-heading.last,
.features-treestudyplan .r-studyline-slot.last {
.features-treestudyplan .r-studyline-slot.last.lastlyr {
border-bottom-style: solid;
}
.path-local-treestudyplan .s-studyline-header-period,
@ -1367,6 +1367,15 @@
.features-treestudyplan div.r-item-course-header-details:last-child {
margin-bottom: 0.3rem;
}
.path-local-treestudyplan .s-prevnext-selector,
.features-treestudyplan .s-prevnext-selector {
display: inline-block;
}
.path-local-treestudyplan .s-prevnext-selector i.s-prevnext-arrow,
.features-treestudyplan .s-prevnext-selector i.s-prevnext-arrow {
position: relative;
top: -3px;
}
.path-local-treestudyplan .card.s-studyplan-card,
.features-treestudyplan .card.s-studyplan-card {

View File

@ -671,7 +671,7 @@
.t-studyline-heading.last,
.t-studyline-slot.last.newlyr,
.r-studyline-heading.last,
.r-studyline-slot.last {
.r-studyline-slot.last.lastlyr {
border-bottom-style: solid;
}
@ -1180,4 +1180,15 @@
}
}
.s-prevnext-selector {
display: inline-block;
i.s-prevnext-arrow {
position: relative;
top: -3px;
}
}
}

View File

@ -787,12 +787,12 @@
.path-local-treestudyplan .t-studyline-heading.last,
.path-local-treestudyplan .t-studyline-slot.last.newlyr,
.path-local-treestudyplan .r-studyline-heading.last,
.path-local-treestudyplan .r-studyline-slot.last,
.path-local-treestudyplan .r-studyline-slot.last.lastlyr,
.features-treestudyplan .t-studyline-drag:last-child,
.features-treestudyplan .t-studyline-heading.last,
.features-treestudyplan .t-studyline-slot.last.newlyr,
.features-treestudyplan .r-studyline-heading.last,
.features-treestudyplan .r-studyline-slot.last {
.features-treestudyplan .r-studyline-slot.last.lastlyr {
border-bottom-style: solid;
}
.path-local-treestudyplan .s-studyline-header-period,
@ -1367,6 +1367,15 @@
.features-treestudyplan div.r-item-course-header-details:last-child {
margin-bottom: 0.3rem;
}
.path-local-treestudyplan .s-prevnext-selector,
.features-treestudyplan .s-prevnext-selector {
display: inline-block;
}
.path-local-treestudyplan .s-prevnext-selector i.s-prevnext-arrow,
.features-treestudyplan .s-prevnext-selector i.s-prevnext-arrow {
position: relative;
top: -3px;
}
.path-local-treestudyplan .card.s-studyplan-card,
.features-treestudyplan .card.s-studyplan-card {

View File

@ -134,8 +134,24 @@ print $OUTPUT->header();
v-model="displayedstudyplan"
v-if="displayedstudyplan && displayedstudyplan.description"
></s-studyplan-details>
<b-button class="ml-1" v-if="activestudyplan" variant='primary' v-b-toggle.toolbox-sidebar
><?php t('selectstudent_btn') ?></b-button>
<div class="flex-grow-1"><!-- Spacer to align student selector right --></div>
<div v-if="displayedstudyplan && displayedstudyplan.description">
<span><?php t('selectstudent_btn') ?></span>
<s-prevnext-selector
:options="associatedstudents"
title="firstname"
v-model="selectedstudent"
defaultselectable
arrows
@change="showStudentView"
class="ml-2"
variant="primary"
>
<template v-slot="{value}">{{value.firstname}} {{value.lastname}}</template>
<template #defaultlabel><span class='text-primary'><?php t("showoverview"); ?></span></template>
</s-prevnext-selector>
</div>
</div>
<div class='t-studyplan-container'>
@ -161,30 +177,6 @@ print $OUTPUT->header();
</b-card-group>
</div>
</div>
<b-sidebar
id="toolbox-sidebar"
right
shadow
title='<?php t("selectstudent")?>'
>
<div class='m-2'><?php t("selectstudent_details")?></div>
<b-list-group v-if="associatedstudents">
<b-list-group-item :active="! selectedstudent"
button
variant="primary"
@click='showOverview()'
>
<?php t("showoverview"); ?>
</b-list-group-item>
<b-list-group-item v-for="student in associatedstudents" :key="student.id"
:active="selectedstudent && student.id == selectedstudent.id"
button
@click='showStudentView(student)'
>
{{student.firstname}} {{student.lastname}}
</b-list-group-item>
</b-list-group>
</b-sidebar>
</div>
</div>
<?php