Reworked timeline grid (stage 1)
This commit is contained in:
parent
f8896ddfdb
commit
340b0eef6b
|
@ -1,5 +1,7 @@
|
||||||
/*eslint no-var: "error"*/
|
/*eslint no-var: "error"*/
|
||||||
/*eslint no-console: "off"*/
|
/*eslint no-console: "off"*/
|
||||||
|
/*eslint no-unused-vars: warn */
|
||||||
|
/*eslint max-len: ["error", { "code": 160 }] */
|
||||||
/*eslint-disable no-trailing-spaces */
|
/*eslint-disable no-trailing-spaces */
|
||||||
/*eslint-env es6*/
|
/*eslint-env es6*/
|
||||||
// Put this file in path/to/plugin/amd/src
|
// Put this file in path/to/plugin/amd/src
|
||||||
|
@ -1131,6 +1133,26 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
countLineLayers(line){
|
||||||
|
let maxLayer = -1;
|
||||||
|
for(let i = 0; i <= this.value.slots; i++){
|
||||||
|
const slot = line.slots[i];
|
||||||
|
// Determine the amount of used layers in a studyline slit
|
||||||
|
for(const ix in line.slots[i].competencies){
|
||||||
|
const item = line.slots[i].competencies[ix];
|
||||||
|
if(item.layer > maxLayer){
|
||||||
|
maxLayer = item.layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(const ix in line.slots[i].filters){
|
||||||
|
const item = line.slots[i].filters[ix];
|
||||||
|
if(item.layer > maxLayer){
|
||||||
|
maxLayer = item.layer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxLayer+1;
|
||||||
|
},
|
||||||
slotsempty(slots) {
|
slotsempty(slots) {
|
||||||
if(Array.isArray(slots)){
|
if(Array.isArray(slots)){
|
||||||
let count = 0;
|
let count = 0;
|
||||||
|
@ -1353,32 +1375,36 @@ export default {
|
||||||
<!-- Line by line add the items -->
|
<!-- Line by line add the items -->
|
||||||
<!-- The grid layout handles putting it in rows and columns -->
|
<!-- The grid layout handles putting it in rows and columns -->
|
||||||
<template v-for="(line,lineindex) in value.studylines"
|
<template v-for="(line,lineindex) in value.studylines"
|
||||||
><template v-for="(n,index) in (value.slots+1)"
|
><template v-for="layeridx in countLineLayers(line)+1"
|
||||||
><t-studyline-slot
|
><template v-for="(n,index) in (value.slots+1)"
|
||||||
v-if="index > 0"
|
|
||||||
type='gradable'
|
|
||||||
v-model="line.slots[index].competencies"
|
|
||||||
:key="'c-'+lineindex+'-'+index"
|
|
||||||
:slotindex="index"
|
|
||||||
:line="line"
|
|
||||||
:plan="value"
|
|
||||||
:class= "'t-studyline ' + (line.sequence==1?' first':'') +
|
|
||||||
(line.sequence==value.studylines.length?' last':'')"
|
|
||||||
>
|
>
|
||||||
</t-studyline-slot
|
<t-studyline-slot
|
||||||
><t-studyline-slot
|
v-if="index > 0"
|
||||||
type='filter'
|
type='gradable'
|
||||||
v-model="line.slots[index].filters"
|
v-model="line.slots[index].competencies"
|
||||||
:key="'f-'+lineindex+'-'+index"
|
:key="'c-'+lineindex+'-'+index+'-'+layeridx"
|
||||||
:slotindex="index"
|
:slotindex="index"
|
||||||
:line="line"
|
:line="line"
|
||||||
:plan="value"
|
:plan="value"
|
||||||
:class=" 't-studyline '
|
:layer="layeridx-1"
|
||||||
+ (line.sequence==1?' first':'')
|
:class= "'t-studyline ' + ((line.sequence==1 && layeridx==1)?' first':'') +
|
||||||
+ (line.sequence==value.studylines.length?' last':'')
|
(line.sequence==value.studylines.length?' last':'')"
|
||||||
+ (index==value.slots?' end':'')"
|
></t-studyline-slot
|
||||||
>
|
><t-studyline-slot
|
||||||
</t-studyline-slot
|
type='filter'
|
||||||
|
v-model="line.slots[index].filters"
|
||||||
|
:key="'f-'+lineindex+'-'+index+'-'+layeridx"
|
||||||
|
:slotindex="index"
|
||||||
|
:line="line"
|
||||||
|
:plan="value"
|
||||||
|
:layer="layeridx-1"
|
||||||
|
:class=" 't-studyline '
|
||||||
|
+ ((line.sequence==1 && layeridx==1)?' first':'')
|
||||||
|
+ (line.sequence==value.studylines.length?' last':'')
|
||||||
|
+ (index==value.slots?' end':'')"
|
||||||
|
>
|
||||||
|
</t-studyline-slot
|
||||||
|
></template
|
||||||
></template
|
></template
|
||||||
></template
|
></template
|
||||||
></div><div :id="'studyplan-linewrapper-'+value.id" class='l-leaderline-linewrapper'></div>
|
></div><div :id="'studyplan-linewrapper-'+value.id" class='l-leaderline-linewrapper'></div>
|
||||||
|
@ -1594,6 +1620,11 @@ export default {
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* During a redisign it was decided to have the studyline still get the entire array as a value,
|
||||||
|
* even though it only shows one drop slot for the layer it is in. This is to make repainting easier,
|
||||||
|
* since we modify the array for the layer we handle
|
||||||
|
*/
|
||||||
Vue.component('t-studyline-slot', {
|
Vue.component('t-studyline-slot', {
|
||||||
props: {
|
props: {
|
||||||
type : {
|
type : {
|
||||||
|
@ -1608,8 +1639,11 @@ export default {
|
||||||
type: Object,
|
type: Object,
|
||||||
default(){ return null;},
|
default(){ return null;},
|
||||||
},
|
},
|
||||||
|
layer : {
|
||||||
|
type: Number,
|
||||||
|
},
|
||||||
value: {
|
value: {
|
||||||
type: Array,
|
type: Array, // dict with layer as index
|
||||||
default(){ return [];},
|
default(){ return [];},
|
||||||
},
|
},
|
||||||
plan: {
|
plan: {
|
||||||
|
@ -1640,20 +1674,35 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
item(){
|
||||||
|
for(const itm of this.value){
|
||||||
|
if(itm.layer == this.layer){
|
||||||
|
return itm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
listtype() {
|
listtype() {
|
||||||
return this.type;
|
return this.type;
|
||||||
},
|
},
|
||||||
dragacceptlist(){
|
dragacceptlist(){
|
||||||
if(this.type == "gradable"){
|
if(this.type == "gradable"){
|
||||||
return ["course","competency","gradable-item"];
|
return ["course", "gradable-item"];
|
||||||
} else {
|
} else {
|
||||||
return ["filter", "filter-item"];
|
return ["filter", "filter-item"];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
courseHoverDummy(){
|
||||||
|
return {course: this.hover.component};
|
||||||
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
resizeListener: null,
|
resizeListener: null,
|
||||||
|
hover: {
|
||||||
|
component:null,
|
||||||
|
type: null,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -1666,51 +1715,34 @@ export default {
|
||||||
},
|
},
|
||||||
dragacceptcomponent(){
|
dragacceptcomponent(){
|
||||||
if(this.type == "gradable"){
|
if(this.type == "gradable"){
|
||||||
return ["course","competency",];
|
return ["course",];
|
||||||
} else {
|
} else {
|
||||||
return ["filter",];
|
return ["filter",];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onInsert(event) {
|
onDrop(event) {
|
||||||
|
this.hover.component = null;
|
||||||
|
this.hover.type = null;
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
if(self.dragacceptitem().includes(event.type)) {
|
if(self.dragacceptitem().includes(event.type)) {
|
||||||
let item = event.data;
|
let item = event.data;
|
||||||
self.value.splice( event.index,0, item);
|
// Perform layer update - set this slot and layer here
|
||||||
self.afterReorder(self.value).done(function(){
|
self.relocateStudyItem(item).done(()=>{
|
||||||
|
item.layer = this.layer;
|
||||||
|
item.slot = this.slotindex;
|
||||||
|
self.value.push(item);
|
||||||
self.$emit("input",self.value);
|
self.$emit("input",self.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if(self.dragacceptcomponent().includes(event.type) ){
|
else if(self.dragacceptcomponent().includes(event.type) ){
|
||||||
if(event.type == "competency"){
|
if(event.type == "course"){
|
||||||
call([{
|
|
||||||
methodname: 'local_treestudyplan_add_studyitem',
|
|
||||||
args: {
|
|
||||||
"line_id": self.line.id,
|
|
||||||
"slot" : self.slotindex,
|
|
||||||
"type": 'competency',
|
|
||||||
"details": {
|
|
||||||
"competency_id": event.data.id,
|
|
||||||
'conditions':'',
|
|
||||||
'course_id':null,
|
|
||||||
'badge_id':null,
|
|
||||||
'continuation_id':null,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}])[0].done((response) => {
|
|
||||||
console.info('Add item response:', response);
|
|
||||||
let item = response;
|
|
||||||
self.value.splice(event.index, 0, item);
|
|
||||||
self.afterReorder(self.value).done(function () {
|
|
||||||
self.$emit("input", self.value);
|
|
||||||
});
|
|
||||||
}).fail(notification.exception);
|
|
||||||
}
|
|
||||||
else if(event.type == "course"){
|
|
||||||
call([{
|
call([{
|
||||||
methodname: 'local_treestudyplan_add_studyitem',
|
methodname: 'local_treestudyplan_add_studyitem',
|
||||||
args: {
|
args: {
|
||||||
"line_id": self.line.id,
|
"line_id": self.line.id,
|
||||||
"slot" : self.slotindex,
|
"slot" : self.slotindex,
|
||||||
|
"layer" : self.layer,
|
||||||
"type": 'course',
|
"type": 'course',
|
||||||
"details": {
|
"details": {
|
||||||
"competency_id": null,
|
"competency_id": null,
|
||||||
|
@ -1723,9 +1755,9 @@ export default {
|
||||||
}])[0].done((response) => {
|
}])[0].done((response) => {
|
||||||
console.info('Add item response:', response);
|
console.info('Add item response:', response);
|
||||||
let item = response;
|
let item = response;
|
||||||
self.value.splice(event.index, 0, item);
|
self.relocateStudyItem(item).done(()=>{
|
||||||
self.afterReorder(self.value).done(function () {
|
self.value.push(item);
|
||||||
self.$emit("input", self.value);
|
self.$emit("input",self.value);
|
||||||
});
|
});
|
||||||
}).fail(notification.exception);
|
}).fail(notification.exception);
|
||||||
}
|
}
|
||||||
|
@ -1743,9 +1775,9 @@ export default {
|
||||||
}])[0].done((response) => {
|
}])[0].done((response) => {
|
||||||
console.info('Add item response:', response);
|
console.info('Add item response:', response);
|
||||||
let item = response;
|
let item = response;
|
||||||
self.value.splice(event.index, 0, item);
|
self.relocateStudyItem(item).done(()=>{
|
||||||
self.afterReorder(self.value).done(function () {
|
self.value.push(item);
|
||||||
self.$emit("input", self.value);
|
self.$emit("input",self.value);
|
||||||
});
|
});
|
||||||
}).fail(notification.exception);
|
}).fail(notification.exception);
|
||||||
}
|
}
|
||||||
|
@ -1754,88 +1786,76 @@ export default {
|
||||||
onCut(event) {
|
onCut(event) {
|
||||||
const self=this;
|
const self=this;
|
||||||
let id = event.data.id;
|
let id = event.data.id;
|
||||||
|
|
||||||
for(let i = 0; i < self.value.length; i++){
|
for(let i = 0; i < self.value.length; i++){
|
||||||
if(self.value[i].id == id){
|
if(self.value[i].id == id){
|
||||||
self.value.splice(i, 1); i--;
|
self.value.splice(i, 1); i--;
|
||||||
break; // just remove one
|
break; // just remove one
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.afterReorder(self.value);
|
// Do something to signal that this item has been removed
|
||||||
this.$emit("input",this.value);
|
this.$emit("input",this.value);
|
||||||
},
|
},
|
||||||
onReorder(event) {
|
relocateStudyItem(item){
|
||||||
const self=this;
|
const iteminfo = {'id': item.id, 'layer': this.layer, 'slot': this.slotindex, 'line_id': this.line.id};
|
||||||
// apply list first
|
|
||||||
event.apply(self.value);
|
|
||||||
this.afterReorder(self.value);
|
|
||||||
},
|
|
||||||
afterReorder() {
|
|
||||||
const self=this;
|
|
||||||
// send the new order to the server
|
|
||||||
let items = [];
|
|
||||||
for(let idx in self.value)
|
|
||||||
{
|
|
||||||
self.value[idx].layer = idx;
|
|
||||||
items.push({'id': self.value[idx].id,'layer': idx, 'slot': this.slotindex, 'line_id': this.line.id});
|
|
||||||
}
|
|
||||||
return call([{
|
return call([{
|
||||||
methodname: 'local_treestudyplan_reorder_studyitems',
|
methodname: 'local_treestudyplan_reorder_studyitems',
|
||||||
args: { 'items': items }
|
args: { 'items': [iteminfo] } // function was designed to relocate multiple items at once, hence the array
|
||||||
}])[0].fail(notification.exception);
|
}])[0].fail(notification.exception);
|
||||||
},
|
},
|
||||||
feedbackDummy(type,data){
|
onDragEnter(event){
|
||||||
let item = {};
|
this.hover.component = event.data;
|
||||||
item[type] = data;
|
this.hover.type = event.type;
|
||||||
return item;
|
},
|
||||||
}
|
onDragLeave(){
|
||||||
|
this.hover.component = null;
|
||||||
|
this.hover.type = null;
|
||||||
|
},
|
||||||
|
|
||||||
},
|
},
|
||||||
template: `
|
template: `
|
||||||
<div :class="'t-studyline-slot '+type + ' t-studyline-slot-'+slotindex + ((slotindex==0)?'t-studyline-firstcolumn':'')
|
<div :class="'t-studyline-slot '+type + ' t-studyline-slot-'+slotindex + ((slotindex==0)?'t-studyline-firstcolumn':'')
|
||||||
+ ((line.sequence%2)?' odd':' even') "
|
+ ((line.sequence%2)?' odd':' even') "
|
||||||
:data-studyline="line.id" ref="sizeElement"
|
:data-studyline="line.id" ref="sizeElement"
|
||||||
>
|
><drag v-if="item"
|
||||||
<drop-list
|
|
||||||
:items="value"
|
:key="item.id"
|
||||||
|
class="t-slot-item"
|
||||||
|
:data="item"
|
||||||
|
:type="type+'-item'"
|
||||||
|
@cut="onCut"
|
||||||
|
><t-item v-model="item" :plan="plan"></t-item
|
||||||
|
></drag
|
||||||
|
><drop v-else
|
||||||
:class="'t-slot-droplist '+type"
|
:class="'t-slot-droplist '+type"
|
||||||
:accepts-type="dragacceptlist"
|
:accepts-type="dragacceptlist"
|
||||||
@insert="onInsert"
|
@drop="onDrop"
|
||||||
@reorder="onReorder"
|
|
||||||
mode="cut"
|
mode="cut"
|
||||||
row
|
@dragenter="onDragEnter"
|
||||||
>
|
@dragleave="onDragLeave"
|
||||||
<template v-slot:item="{item}">
|
><template v-if="hover.component">
|
||||||
<drag
|
<div v-if="hover.type == listtype+'-item'"
|
||||||
:key="item.id"
|
|
||||||
class="t-slot-item"
|
|
||||||
:data="item"
|
|
||||||
:type="type+'-item'"
|
|
||||||
@cut="onCut"><t-item v-model="item" :plan="plan"></t-item></drag>
|
|
||||||
</template>
|
|
||||||
<template v-slot:feedback="{data,type}">
|
|
||||||
<div v-if="type == listtype+'-item'"
|
|
||||||
class="t-slot-item feedback"
|
class="t-slot-item feedback"
|
||||||
:key="data.id"><t-item v-model="data" dummy></t-item></div>
|
:key="hover.component.id"
|
||||||
<div v-else-if="type == 'competency'"
|
><t-item v-model="hover.component" dummy></t-item
|
||||||
|
></div
|
||||||
|
><div v-else-if="hover.type == 'course'"
|
||||||
class="t-slot-item feedback"
|
class="t-slot-item feedback"
|
||||||
:key="'competency-'+data.idnumber"><t-item-competency v-model="data"></t-item-competency></div>
|
:key="'course-'+hover.component.id"
|
||||||
<div v-else-if="type == 'course'"
|
><t-item-course v-model="courseHoverDummy"></t-item-course></div
|
||||||
|
><div v-else-if="hover.type == 'filter'"
|
||||||
class="t-slot-item feedback"
|
class="t-slot-item feedback"
|
||||||
:key="'course-;'+data.id"><t-item-course v-model="feedbackDummy('course',data)"></t-item-course></div>
|
key="tooldrop"
|
||||||
<div v-else-if="type == 'filter'"
|
><t-item-junction v-if="hover.component.type == 'junction'" ></t-item-junction
|
||||||
class="t-slot-item feedback"
|
><t-item-start v-else-if="hover.component.type == 'start'" ></t-item-start
|
||||||
key="tooldrop">
|
><t-item-finish v-else-if="hover.component.type == 'finish'" ></t-item-finish
|
||||||
<t-item-junction v-if="data.type == 'junction'" ></t-item-junction>
|
><t-item-badge v-else-if="hover.component.type == 'badge'" ></t-item-badge
|
||||||
<t-item-start v-else-if="data.type == 'start'" ></t-item-start>
|
></div
|
||||||
<t-item-finish v-else-if="data.type == 'finish'" ></t-item-finish>
|
><div v-else
|
||||||
<t-item-badge v-else-if="data.type == 'badge'" ></t-item-badge>
|
|
||||||
</div>
|
|
||||||
<div v-else
|
|
||||||
class="t-slot-item feedback"
|
class="t-slot-item feedback"
|
||||||
:key="type">--{{ type }}--</div>
|
:key="hover.type">--{{ hover.type }}--</div
|
||||||
</template>
|
></template
|
||||||
</drop-list>
|
></drop
|
||||||
</div>
|
></div>
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -189,9 +189,8 @@ class studyitem {
|
||||||
public static function add($fields,$import=false)
|
public static function add($fields,$import=false)
|
||||||
{
|
{
|
||||||
global $DB;
|
global $DB;
|
||||||
$addable = ['line_id','type','conditions','slot','competency_id','course_id','badge_id','continuation_id'];
|
$addable = ['line_id','type','layer','conditions','slot','competency_id','course_id','badge_id','continuation_id'];
|
||||||
if($import){ $addable[] = "layer";}
|
$info = [ 'layer' => 0, ];
|
||||||
$info = [ 'layer' => -1, ];
|
|
||||||
foreach($addable as $f){
|
foreach($addable as $f){
|
||||||
if(array_key_exists($f,$fields)){
|
if(array_key_exists($f,$fields)){
|
||||||
$info[$f] = $fields[$f];
|
$info[$f] = $fields[$f];
|
||||||
|
|
|
@ -417,6 +417,7 @@ class studyplanservice extends \external_api
|
||||||
"continuation_id" => new \external_value(PARAM_INT, 'id of continued item',VALUE_OPTIONAL),
|
"continuation_id" => new \external_value(PARAM_INT, 'id of continued item',VALUE_OPTIONAL),
|
||||||
]),
|
]),
|
||||||
"slot" => new \external_value(PARAM_INT, 'slot in the study plan',VALUE_DEFAULT),
|
"slot" => new \external_value(PARAM_INT, 'slot in the study plan',VALUE_DEFAULT),
|
||||||
|
"layer" => new \external_value(PARAM_INT, 'layer in the slot',VALUE_OPTIONAL),
|
||||||
] );
|
] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -425,7 +426,7 @@ class studyplanservice extends \external_api
|
||||||
return studyitem::editor_structure();
|
return studyitem::editor_structure();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function add_studyitem($line_id,$type,$details,$slot=-1)
|
public static function add_studyitem($line_id,$type,$details,$slot=-1,$layer=0)
|
||||||
{
|
{
|
||||||
webservicehelper::require_capabilities(self::CAP_EDIT,studyline::findById($line_id)->context());
|
webservicehelper::require_capabilities(self::CAP_EDIT,studyline::findById($line_id)->context());
|
||||||
|
|
||||||
|
@ -434,6 +435,7 @@ class studyplanservice extends \external_api
|
||||||
'type' => $type,
|
'type' => $type,
|
||||||
//'conditions' => $conditions,
|
//'conditions' => $conditions,
|
||||||
'slot' => $slot,
|
'slot' => $slot,
|
||||||
|
'layer' => $layer,
|
||||||
'competency_id' => isset($details['competency_id'])?$details['competency_id']:null,
|
'competency_id' => isset($details['competency_id'])?$details['competency_id']:null,
|
||||||
'course_id' => isset($details['course_id'])?$details['course_id']:null,
|
'course_id' => isset($details['course_id'])?$details['course_id']:null,
|
||||||
'badge_id' => isset($details['badge_id'])?$details['badge_id']:null,
|
'badge_id' => isset($details['badge_id'])?$details['badge_id']:null,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php
|
<?php
|
||||||
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494)
|
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494)
|
||||||
$plugin->version = 2023070400; // YYYYMMDDHH (year, month, day, iteration)
|
$plugin->version = 2023071400; // YYYYMMDDHH (year, month, day, iteration)
|
||||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11)
|
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11)
|
||||||
|
|
||||||
$plugin->dependencies = [
|
$plugin->dependencies = [
|
||||||
|
|
Loading…
Reference in New Issue
Block a user