Implemented HiViz dropslot big "drop here" feature

This commit is contained in:
PMKuipers 2024-05-27 22:11:32 +02:00
parent c88f132201
commit 4fd1e3a547
16 changed files with 24106 additions and 38 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

11912
amd/build/vue/vue.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -119,7 +119,7 @@ export default {
line_enrollable_1: 'line_enrollable:1', line_enrollable_1: 'line_enrollable:1',
line_enrollable_2: 'line_enrollable:2', line_enrollable_2: 'line_enrollable:2',
line_enrollable_3: 'line_enrollable:3', line_enrollable_3: 'line_enrollable:3',
drophere: 'drophere',
}, },
studyplan_advanced: { studyplan_advanced: {
advanced_tools: 'advanced_tools', advanced_tools: 'advanced_tools',
@ -1483,7 +1483,10 @@ export default {
computed: { computed: {
selectedpage() { selectedpage() {
return this.value.pages[this.selectedpageindex]; return this.value.pages[this.selectedpageindex];
} },
hivizdrop() {
return settings("hivizdropslots");
},
}, },
methods: { methods: {
premiumenabled, premiumenabled,
@ -1744,6 +1747,122 @@ export default {
selectedpageChanged(newTabIndex,prevTabIndex) { selectedpageChanged(newTabIndex,prevTabIndex) {
const page = this.value.pages[newTabIndex]; const page = this.value.pages[newTabIndex];
this.$emit('pagechanged',page); this.$emit('pagechanged',page);
},
sumLineLayers(idx,page) {
if ( idx < 0 || page.studylines.count == 0 ) {
return 0;
} else {
let sum = 0;
for (let i = 0; i < idx; i++) {
sum += this.countLineLayers(page.studylines[i],page) + 1;
}
return sum;
}
},
span (line, slot, layer) {
let span = 1;
for (const course of line.slots[slot].courses) {
if (course.slot == slot && course.layer == layer) {
span = course.span;
}
}
return span;
},
onDrop(event, line, slot) {
debug.info("dropping", event, line, slot);
const self = this;
if (event.type.component) { // Double check in case filter fails
debug.info("Adding new component");
if(event.type.type == "gradable"){
// Determine first available layer;
const lineslot = line.slots[slot].courses;
let nextlayer = 0;
for(const itm of lineslot){
if (itm.layer >= nextlayer ) {
nextlayer = itm.layer + 1;
}
}
call([{
methodname: 'local_treestudyplan_add_studyitem',
args: {
"line_id": line.id,
"slot" : slot,
"layer" : nextlayer,
"type": 'course',
"details": {
"competency_id": null,
'conditions':'',
'course_id':event.data.id,
'badge_id':null,
'continuation_id':null,
}
}
}])[0].then((response) => {
let item = response;
lineslot.push(item);
self.$emit("input",self.value);
// call the validate period function on next tick,
// since it paints the item in the slot first
this.$nextTick(() => {
if(this.$refs.timingChecker){
this.$refs.timingChecker.validate_course_period();
}
});
ItemEventBus.$emit('coursechange');
}).catch(notification.exception);
}
else if(event.type.type == "filter") {
debug.info("Adding new filter compenent");
// Determine first available layer;
const lineslot = line.slots[slot].filters;
let nextlayer = 0;
for(const itm of lineslot){
if (itm.layer >= nextlayer ) {
nextlayer = itm.layer + 1;
}
}
call([{
methodname: 'local_treestudyplan_add_studyitem',
args: {
"line_id": line.id,
"slot" : slot,
"type": event.data.type,
"layer" : nextlayer,
"details":{
"badge_id": event.data.badge?event.data.badge.id:undefined,
}
}
}])[0].then((response) => {
let item = response;
lineslot.push(item);
self.$emit("input",self.value);
}).catch(notification.exception);
}
}
},
checkTypeCourse(type) {
if(type.type == "gradable"){
if ( settings("hivizdropslots") && !type.item) {
return true;
} else {
return false;
}
} else {
return false;
}
},
checkTypeFilter(type) {
if(type.type == "filter"){
if ( settings("hivizdropslots") && !type.item) {
return true;
} else {
return false;
}
} else {
return false;
}
} }
} }
, ,
@ -1880,6 +1999,8 @@ export default {
<template v-for="(n,index) in (page.periods+1)"> <template v-for="(n,index) in (page.periods+1)">
<s-studyline-header-period <s-studyline-header-period
mode="edit" mode="edit"
:x-index="index"
:style="'grid-area: 1 / '+ ((2*index)) +';'"
:identifier='Number(page.id)' :identifier='Number(page.id)'
v-if="index > 0" v-if="index > 0"
v-model="page.perioddesc[index-1]" v-model="page.perioddesc[index-1]"
@ -1892,7 +2013,10 @@ export default {
:maxend="(index < page.periods) ? sub_day(page.perioddesc[index].enddate,2) : null" :maxend="(index < page.periods) ? sub_day(page.perioddesc[index].enddate,2) : null"
></t-period-edit ></t-period-edit
></s-studyline-header-period> ></s-studyline-header-period>
<div class="s-studyline-header-filter"></div> <div class="s-studyline-header-filter"
:x-index="index"
:style="'grid-area: 1 / '+ ((2*index)+1) +';'"
></div>
</template> </template>
<!-- Line by line add the items --> <!-- Line by line add the items -->
@ -1902,6 +2026,10 @@ export default {
><template v-for="(n,index) in (page.periods+1)" ><template v-for="(n,index) in (page.periods+1)"
><t-studyline-slot ><t-studyline-slot
v-if="index > 0 && showslot(page,line, index, layeridx, 'gradable')" v-if="index > 0 && showslot(page,line, index, layeridx, 'gradable')"
:style="'grid-area: '+ (1+sumLineLayers(lineindex,page)+layernr)
+ ' / ' + (2 * index)
+ ' / ' + (1 + sumLineLayers(lineindex,page)+layernr)
+ ' / ' + ( (2 * index) + (2*span(line,index,layeridx) - 1)) + ';'"
type='gradable' type='gradable'
v-model="line.slots[index].courses" v-model="line.slots[index].courses"
:key="'c-'+lineindex+'-'+index+'-'+layernr" :key="'c-'+lineindex+'-'+index+'-'+layernr"
@ -1919,6 +2047,7 @@ export default {
></t-studyline-slot ></t-studyline-slot
><t-studyline-slot ><t-studyline-slot
type='filter' type='filter'
:style="'grid-area: '+ (1+sumLineLayers(lineindex,page)+layernr) + ' / ' + (2*index+1) +';'"
v-if="showslot(page,line, index, layeridx, 'filter')" v-if="showslot(page,line, index, layeridx, 'filter')"
v-model="line.slots[index].filters" v-model="line.slots[index].filters"
:key="'f-'+lineindex+'-'+index+'-'+layernr" :key="'f-'+lineindex+'-'+index+'-'+layernr"
@ -1937,6 +2066,32 @@ export default {
></template ></template
></template ></template
></template ></template
><template v-if="hivizdrop"
><template v-for="(line,lineindex) in page.studylines"
><template v-for="(n,index) in (page.periods+1)"
><drop v-if="index > 0"
:style="'grid-area: '+ (2 + sumLineLayers(lineindex,page))
+ ' / ' + (2 * index)
+ ' / ' + (1 + sumLineLayers(lineindex + 1,page))
+ ' / ' + (2 * index) + '; overflow: hidden;'"
:class="'t-slot-drop t-slot-linedrop course hiviz'"
:accepts-type="checkTypeCourse"
@drop="onDrop($event,line,index)"
mode="cut"
><span>{{text.drophere}}</span></drop
><drop
:style="'grid-area: '+ (2 + sumLineLayers(lineindex,page))
+ ' / ' + ((2 * index) + 1)
+ ' / ' + (1 + sumLineLayers(lineindex + 1,page))
+ ' / ' + ( (2 * index) + 1 ) + '; overflow: hidden;'"
:class="'t-slot-drop t-slot-linedrop filter hiviz'"
:accepts-type="checkTypeFilter"
@drop="onDrop($event,line,index)"
mode="cut"
><span>{{text.drophere}}</span></drop
></template
></template
></template
></div> ></div>
</div> </div>
</div> </div>
@ -2284,20 +2439,13 @@ export default {
listtype() { listtype() {
return this.type; return this.type;
}, },
dragacceptlist(){
if(this.type == "gradable"){
return ["course", "gradable-item"];
} else {
return ["filter", "filter-item"];
}
},
courseHoverDummy(){ courseHoverDummy(){
return {course: this.hover.component}; return {course: this.hover.component};
}, },
spanCss(){ spanCss(){
if(this.item && this.item.span > 1){ if(this.item && this.item.span > 1){
const span = (2 * this.item.span) - 1; const span = (2 * this.item.span) - 1;
return `width: 100%; grid-column: span ${span};`; return `width: 100%; `;
} else { } else {
return ""; return "";
} }
@ -2306,6 +2454,7 @@ export default {
data() { data() {
return { return {
text: strings.course_timing, text: strings.course_timing,
plantext: strings.studyplan_text,
resizeListener: null, resizeListener: null,
hover: { hover: {
component:null, component:null,
@ -2448,12 +2597,16 @@ export default {
}, },
checkType(type) { checkType(type) {
if(type.type == this.type){ if(type.type == this.type){
if(type == 'filter'){ if ( settings("hivizdropslots") && !type.item) {
return true;
} else if(type.span <= this.maxSpan()){
return true;
} else {
return false; return false;
} else {
if(type == 'filter'){
return true;
} else if(type.span <= this.maxSpan()){
return true;
} else {
return false;
}
} }
} else { } else {
return false; return false;
@ -2509,7 +2662,7 @@ export default {
class="t-slot-item feedback" class="t-slot-item feedback"
:key="hover.type">--{{ hover.type }}--</div :key="hover.type">--{{ hover.type }}--</div
></template ></template
></drop> ><span v-else-if="hivizdrop()">{{plantext.drophere}}</span></drop>
<t-item-timing-checker hidden <t-item-timing-checker hidden
v-if="value && value[itemidx] && value[itemidx].course" v-if="value && value[itemidx] && value[itemidx].course"
ref="timingChecker" ref="timingChecker"
@ -3488,7 +3641,7 @@ export default {
<a v-b-modal="'t-item-course-config-'+value.id" <a v-b-modal="'t-item-course-config-'+value.id"
:id="'t-item-course-details-'+value.id" :id="'t-item-course-details-'+value.id"
:href="wwwroot+'/course/view.php?id='+value.course.id" :href="wwwroot+'/course/view.php?id='+value.course.id"
@click.prevent.stop="">>{{ value.course.displayname }}</a> @click.prevent.stop="">{{ value.course.displayname }}</a>
</fittext> </fittext>
</div> </div>
<div class="h-100 t-item-course-indicator "> <div class="h-100 t-item-course-indicator ">
@ -4227,7 +4380,9 @@ export default {
} }
}, },
methods: { methods: {
// TODO: Filtering like this doesn't work, since the courses are lazyloaded on opening hivizdrop() {
return settings("hivizdropslots");
},
filterCategories(catlist) { filterCategories(catlist) {
const self = this; const self = this;
const list = []; const list = [];

File diff suppressed because one or more lines are too long

View File

@ -411,7 +411,7 @@ class studyplan {
global $CFG, $DB; global $CFG, $DB;
$addable = ['name', 'shortname', 'description', 'descriptionformat', 'idnumber', 'context_id', 'aggregation', 'aggregation_config']; $addable = ['name', 'shortname', 'description', 'descriptionformat', 'idnumber', 'context_id', 'aggregation', 'aggregation_config'];
$info = ['enddate' => null ]; $info = ['enddate' => null, "template" => 0, "suspended" => 0];
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];

View File

@ -65,7 +65,7 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
print $OUTPUT->header(); print $OUTPUT->header();
?> ?>
<div id='root'> <div id='root' class="t-studyplan-limit-width">
<div class='vue-loader' v-show='false'> <div class='vue-loader' v-show='false'>
<div class="spinner-border text-primary" role="status"> <div class="spinner-border text-primary" role="status">
<span class="sr-only">Loading...</span> <span class="sr-only">Loading...</span>

View File

@ -12,6 +12,8 @@
--excellent: var(--blue); --excellent: var(--blue);
--pending: var(--gray); --pending: var(--gray);
--incomplete: var(--gray); --incomplete: var(--gray);
--highlight-dropslot: yellow;
--highlight-dropslot-hover: var(--success);
} }
.path-local-treestudyplan div.tab-pane:target, .path-local-treestudyplan div.tab-pane:target,
@ -512,15 +514,41 @@ body.path-local-treestudyplan .editmode-switch-form > * {
min-height: 32px; min-height: 32px;
height: 100%; height: 100%;
min-width: 50px; min-width: 50px;
display: flex; opacity: 0.6;
flex-direction: column; flex-direction: column;
align-content: center; align-content: center;
justify-content: center; justify-content: center;
display: flex;
}
.path-local-treestudyplan .t-slot-drop > span,
.features-treestudyplan .t-slot-drop > span {
text-align: center;
display: none;
} }
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed, .path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed,
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed { .features-treestudyplan .t-slot-drop.hiviz.drop-allowed {
min-height: 44px; min-height: 44px;
background-color: yellow; background-color: var(--highlight-dropslot);
}
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed > span,
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed > span {
display: inline;
}
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed.drop-in,
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed.drop-in {
background-color: var(--highlight-dropslot-hover);
}
.path-local-treestudyplan .t-slot-linedrop,
.features-treestudyplan .t-slot-linedrop {
display: none;
}
.path-local-treestudyplan .t-slot-linedrop.type-allowed.drop-allowed,
.features-treestudyplan .t-slot-linedrop.type-allowed.drop-allowed {
display: block;
z-index: 50;
opacity: 0.6;
font-size: 12px;
text-align: center;
} }
.path-local-treestudyplan .t-slot-drop.competency, .path-local-treestudyplan .t-slot-drop.competency,
.features-treestudyplan .t-slot-drop.competency { .features-treestudyplan .t-slot-drop.competency {
@ -541,7 +569,6 @@ body.path-local-treestudyplan .editmode-switch-form > * {
.path-local-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz, .path-local-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz,
.features-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz { .features-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz {
min-height: 44px; min-height: 44px;
background-color: yellow;
} }
.path-local-treestudyplan .t-item-deletebox, .path-local-treestudyplan .t-item-deletebox,
.features-treestudyplan .t-item-deletebox { .features-treestudyplan .t-item-deletebox {

View File

@ -633,6 +633,28 @@ function xmldb_local_treestudyplan_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2024041501, 'local', 'treestudyplan'); upgrade_plugin_savepoint(true, 2024041501, 'local', 'treestudyplan');
} }
if ($oldversion < 2024052400) {
// Changing the default of field template on table local_treestudyplan to 0.
$table = new xmldb_table('local_treestudyplan');
$field = new xmldb_field('template', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'suspended');
// Launch change of default for field template.
$dbman->change_field_default($table, $field);
// Changing the default of field suspended on table local_treestudyplan to 0.
$table = new xmldb_table('local_treestudyplan');
$field = new xmldb_field('suspended', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '0', 'csync_flag');
// Launch change of default for field suspended.
$dbman->change_field_default($table, $field);
// Treestudyplan savepoint reached.
upgrade_plugin_savepoint(true, 2024052400, 'local', 'treestudyplan');
}
return true; return true;
} }

View File

@ -131,6 +131,7 @@ $string["settingdesc_hivizdropslots"] = 'Give the drop fields when editing a stu
$string["setting_toolboxleft"] = 'Toolbar left side by default'; $string["setting_toolboxleft"] = 'Toolbar left side by default';
$string["settingdesc_toolboxleft"] = 'Show the course and component toolbar on the left by default, instead of the right side of the screen'; $string["settingdesc_toolboxleft"] = 'Show the course and component toolbar on the left by default, instead of the right side of the screen';
$string["drophere"] = "Drop here";
$string["infofield_position_above"] = 'Above course results'; $string["infofield_position_above"] = 'Above course results';
$string["infofield_position_below"] = 'Below course results'; $string["infofield_position_below"] = 'Below course results';
$string["infofield_position_header"] = 'Header '; $string["infofield_position_header"] = 'Header ';

View File

@ -132,6 +132,7 @@ $string["settingdesc_hivizdropslots"] = 'Maak de velden waar cursussen heen gesl
$string["setting_toolboxleft"] = 'Toolbox standaard links'; $string["setting_toolboxleft"] = 'Toolbox standaard links';
$string["settingdesc_toolboxleft"] = 'Toon de toolbox met cursussen en componenten standaard links in plaats van rechts.'; $string["settingdesc_toolboxleft"] = 'Toon de toolbox met cursussen en componenten standaard links in plaats van rechts.';
$string["drophere"] = "Sleep hierheen";
$string["infofield_position_above"] = 'Boven cursusresultaten'; $string["infofield_position_above"] = 'Boven cursusresultaten';
$string["infofield_position_below"] = 'Onder cursusresultaten'; $string["infofield_position_below"] = 'Onder cursusresultaten';
$string["infofield_position_header"] = 'Header '; $string["infofield_position_header"] = 'Header ';

View File

@ -12,5 +12,7 @@
--excellent: var(--blue); --excellent: var(--blue);
--pending: var(--gray); --pending: var(--gray);
--incomplete: var(--gray); --incomplete: var(--gray);
--highlight-dropslot: yellow;
--highlight-dropslot-hover: var(--success);
} }

View File

@ -396,13 +396,37 @@
min-height: 32px; min-height: 32px;
height: 100%; height: 100%;
min-width: 50px; min-width: 50px;
display: flex; opacity: 0.6;
flex-direction: column; flex-direction: column;
align-content: center; align-content: center;
justify-content: center; justify-content: center;
display: flex;
> span {
text-align: center;
display: none;
}
&.hiviz.drop-allowed { &.hiviz.drop-allowed {
> span {
display: inline;
}
min-height: 44px; min-height: 44px;
background-color: yellow; background-color: var(--highlight-dropslot);
&.drop-in {
background-color: var(--highlight-dropslot-hover);
}
}
}
.t-slot-linedrop {
display: none;
&.type-allowed.drop-allowed {
display: block;
z-index: 50;
opacity: 0.6;
font-size: 12px;
text-align: center;
} }
} }
@ -422,7 +446,6 @@
min-height: 5px; min-height: 5px;
&.hiviz { &.hiviz {
min-height: 44px; min-height: 44px;
background-color: yellow;
} }
} }

View File

@ -12,6 +12,8 @@
--excellent: var(--blue); --excellent: var(--blue);
--pending: var(--gray); --pending: var(--gray);
--incomplete: var(--gray); --incomplete: var(--gray);
--highlight-dropslot: yellow;
--highlight-dropslot-hover: var(--success);
} }
.path-local-treestudyplan div.tab-pane:target, .path-local-treestudyplan div.tab-pane:target,
@ -512,15 +514,41 @@ body.path-local-treestudyplan .editmode-switch-form > * {
min-height: 32px; min-height: 32px;
height: 100%; height: 100%;
min-width: 50px; min-width: 50px;
display: flex; opacity: 0.6;
flex-direction: column; flex-direction: column;
align-content: center; align-content: center;
justify-content: center; justify-content: center;
display: flex;
}
.path-local-treestudyplan .t-slot-drop > span,
.features-treestudyplan .t-slot-drop > span {
text-align: center;
display: none;
} }
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed, .path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed,
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed { .features-treestudyplan .t-slot-drop.hiviz.drop-allowed {
min-height: 44px; min-height: 44px;
background-color: yellow; background-color: var(--highlight-dropslot);
}
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed > span,
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed > span {
display: inline;
}
.path-local-treestudyplan .t-slot-drop.hiviz.drop-allowed.drop-in,
.features-treestudyplan .t-slot-drop.hiviz.drop-allowed.drop-in {
background-color: var(--highlight-dropslot-hover);
}
.path-local-treestudyplan .t-slot-linedrop,
.features-treestudyplan .t-slot-linedrop {
display: none;
}
.path-local-treestudyplan .t-slot-linedrop.type-allowed.drop-allowed,
.features-treestudyplan .t-slot-linedrop.type-allowed.drop-allowed {
display: block;
z-index: 50;
opacity: 0.6;
font-size: 12px;
text-align: center;
} }
.path-local-treestudyplan .t-slot-drop.competency, .path-local-treestudyplan .t-slot-drop.competency,
.features-treestudyplan .t-slot-drop.competency { .features-treestudyplan .t-slot-drop.competency {
@ -541,7 +569,6 @@ body.path-local-treestudyplan .editmode-switch-form > * {
.path-local-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz, .path-local-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz,
.features-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz { .features-treestudyplan .t-slot-drop.secondary.drop-allowed.hiviz {
min-height: 44px; min-height: 44px;
background-color: yellow;
} }
.path-local-treestudyplan .t-item-deletebox, .path-local-treestudyplan .t-item-deletebox,
.features-treestudyplan .t-item-deletebox { .features-treestudyplan .t-item-deletebox {

View File

@ -22,7 +22,7 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
$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 = 2024052200; // YYYYMMDDHH (year, month, day, iteration). $plugin->version = 2024052400; // 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->release = "1.3.0"; $plugin->release = "1.3.0";