From 8b5cad01f8f0b3de1cdbee5519d2ea3f91d72be3 Mon Sep 17 00:00:00 2001 From: PMKuipers Date: Sat, 1 Jun 2024 14:00:32 +0200 Subject: [PATCH] Fixed layout of item labels --- amd/build/util/fittext-vue.min.js | 2 +- amd/build/util/fittext-vue.min.js.map | 2 +- amd/src/util/fittext-vue.js | 4 +-- classes/period.php | 16 +++++++++-- classes/studyline.php | 21 ++++++++------ classes/studyplanpage.php | 40 ++++++++++++++++++++++----- css/devstyles.css | 16 ++++++++--- lang/en/local_treestudyplan.php | 4 +++ lang/nl/local_treestudyplan.php | 5 ++++ scss/studyplan-report.scss | 18 +++++++++--- settings.php | 16 ++++++++++- styles.css | 16 ++++++++--- 12 files changed, 125 insertions(+), 35 deletions(-) diff --git a/amd/build/util/fittext-vue.min.js b/amd/build/util/fittext-vue.min.js index e0e9ce5..ac1c137 100644 --- a/amd/build/util/fittext-vue.min.js +++ b/amd/build/util/fittext-vue.min.js @@ -1,3 +1,3 @@ -define("local_treestudyplan/util/fittext-vue",["exports","./css-calc","./fitty","./textfit"],(function(_exports,_cssCalc,_fitty,_textfit){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_fitty=(obj=_fitty)&&obj.__esModule?obj:{default:obj};var _default={install(Vue){Vue.component("fittext",{props:{maxsize:{type:String,default:"512px"},minsize:{type:String,default:"10px"},vertical:Boolean,singleline:Boolean,dynamic:Boolean},data:()=>({resizeObserver:null,mutationObserver:null}),computed:{rootStyle(){return this.vertical?"height: 100%;":"width: 100%;"}},methods:{},mounted(){(0,_fitty.default)(this.$refs.text,{minSize:(0,_cssCalc.calc)(this.minsize),maxSize:(0,_cssCalc.calc)(this.maxsize),vertical:this.vertical,multiline:!this.singleline})},unmounted(){this.mutationObserver&&this.mutationObserver.disconnect(),this.resizeObserver&&this.resizeObserver.disconnect()},template:"\n
\n \n
\n "})}};return _exports.default=_default,_exports.default})); +define("local_treestudyplan/util/fittext-vue",["exports","./css-calc","./fitty","./textfit"],(function(_exports,_cssCalc,_fitty,_textfit){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_fitty=(obj=_fitty)&&obj.__esModule?obj:{default:obj};var _default={install(Vue){Vue.component("fittext",{props:{maxsize:{type:String,default:"512px"},minsize:{type:String,default:"10px"},vertical:Boolean,singleline:Boolean,dynamic:Boolean},data:()=>({resizeObserver:null,mutationObserver:null}),computed:{rootStyle(){return this.vertical?"height: 100%;":"width: 100%;"}},methods:{},mounted(){(0,_fitty.default)(this.$refs.text,{minSize:(0,_cssCalc.calc)(this.minsize),maxSize:(0,_cssCalc.calc)(this.maxsize),vertical:this.vertical,multiline:!this.singleline})},unmounted(){this.mutationObserver&&this.mutationObserver.disconnect(),this.resizeObserver&&this.resizeObserver.disconnect()},template:"\n
\n \n
\n "})}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=fittext-vue.min.js.map \ No newline at end of file diff --git a/amd/build/util/fittext-vue.min.js.map b/amd/build/util/fittext-vue.min.js.map index 239142d..b92bdf7 100644 --- a/amd/build/util/fittext-vue.min.js.map +++ b/amd/build/util/fittext-vue.min.js.map @@ -1 +1 @@ -{"version":3,"file":"fittext-vue.min.js","sources":["../../src/util/fittext-vue.js"],"sourcesContent":["/*eslint no-unused-vars: warn */\n/*eslint max-len: [\"error\", { \"code\": 160 }] */\n/*eslint-disable no-trailing-spaces */\n/*eslint-env es6*/\n\nimport {calc} from \"./css-calc\";\nimport fitty from \"./fitty\";\nimport {textFit} from \"./textfit\";\n\nexport default {\n install(Vue/*,options*/){\n Vue.component('fittext',{\n props: {\n maxsize: {\n type: String,\n default: \"512px\",\n },\n minsize: {\n type: String,\n default: \"10px\",\n },\n vertical: Boolean,\n singleline: Boolean,\n dynamic: Boolean,\n },\n data() {\n return {\n resizeObserver: null,\n mutationObserver: null,\n };\n },\n computed: {\n rootStyle() {\n if (this.vertical) {\n return `height: 100%;`;\n } else {\n return `width: 100%;`;\n }\n }\n },\n methods: {\n },\n mounted() {\n const self = this;\n // If the content could change after initial presentation,\n // Use the fitty method. It is slightly worse on multiline horizontal text,\n // but better supports content that can change later on.\n fitty(self.$refs.text,\n {\n minSize: calc(self.minsize),\n maxSize: calc(self.maxsize),\n vertical: self.vertical,\n multiline: !self.singleline,\n });\n },\n unmounted() {\n if(this.mutationObserver) {\n this.mutationObserver.disconnect();\n }\n if(this.resizeObserver) {\n this.resizeObserver.disconnect();\n }\n },\n template: `\n
\n \n
\n `,\n });\n },\n};"],"names":["install","Vue","component","props","maxsize","type","String","default","minsize","vertical","Boolean","singleline","dynamic","data","resizeObserver","mutationObserver","computed","rootStyle","this","methods","mounted","$refs","text","minSize","maxSize","multiline","unmounted","disconnect","template"],"mappings":"qSASe,CACXA,QAAQC,KACJA,IAAIC,UAAU,UAAU,CACxBC,MAAO,CACHC,QAAS,CACLC,KAAMC,OACNC,QAAS,SAEbC,QAAS,CACLH,KAAMC,OACNC,QAAS,QAEbE,SAAUC,QACVC,WAAYD,QACZE,QAASF,SAEbG,KAAI,KACO,CACHC,eAAgB,KAChBC,iBAAkB,OAG1BC,SAAU,CACNC,mBACQC,KAAKT,SACG,gBAEA,iBAIpBU,QAAS,GAETC,6BACiBF,KAIFG,MAAMC,KACb,CACAC,SAAS,iBANAL,KAMUV,SACnBgB,SAAS,iBAPAN,KAOUd,SACnBK,SARSS,KAQMT,SACfgB,WATSP,KASQP,cAGzBe,YACOR,KAAKH,uBACCA,iBAAiBY,aAEvBT,KAAKJ,qBACCA,eAAea,cAG5BC,SAAW"} \ No newline at end of file +{"version":3,"file":"fittext-vue.min.js","sources":["../../src/util/fittext-vue.js"],"sourcesContent":["/*eslint no-unused-vars: warn */\n/*eslint max-len: [\"error\", { \"code\": 160 }] */\n/*eslint-disable no-trailing-spaces */\n/*eslint-env es6*/\n\nimport {calc} from \"./css-calc\";\nimport fitty from \"./fitty\";\nimport {textFit} from \"./textfit\";\n\nexport default {\n install(Vue/*,options*/){\n Vue.component('fittext',{\n props: {\n maxsize: {\n type: String,\n default: \"512px\",\n },\n minsize: {\n type: String,\n default: \"10px\",\n },\n vertical: Boolean,\n singleline: Boolean,\n dynamic: Boolean,\n },\n data() {\n return {\n resizeObserver: null,\n mutationObserver: null,\n };\n },\n computed: {\n rootStyle() {\n if (this.vertical) {\n return `height: 100%;`;\n } else {\n return `width: 100%;`;\n }\n }\n },\n methods: {\n },\n mounted() {\n const self = this;\n // If the content could change after initial presentation,\n // Use the fitty method. It is slightly worse on multiline horizontal text,\n // but better supports content that can change later on.\n fitty(self.$refs.text,\n {\n minSize: calc(self.minsize),\n maxSize: calc(self.maxsize),\n vertical: self.vertical,\n multiline: !self.singleline,\n });\n },\n unmounted() {\n if(this.mutationObserver) {\n this.mutationObserver.disconnect();\n }\n if(this.resizeObserver) {\n this.resizeObserver.disconnect();\n }\n },\n template: `\n
\n \n
\n `,\n });\n },\n};"],"names":["install","Vue","component","props","maxsize","type","String","default","minsize","vertical","Boolean","singleline","dynamic","data","resizeObserver","mutationObserver","computed","rootStyle","this","methods","mounted","$refs","text","minSize","maxSize","multiline","unmounted","disconnect","template"],"mappings":"qSASe,CACXA,QAAQC,KACJA,IAAIC,UAAU,UAAU,CACxBC,MAAO,CACHC,QAAS,CACLC,KAAMC,OACNC,QAAS,SAEbC,QAAS,CACLH,KAAMC,OACNC,QAAS,QAEbE,SAAUC,QACVC,WAAYD,QACZE,QAASF,SAEbG,KAAI,KACO,CACHC,eAAgB,KAChBC,iBAAkB,OAG1BC,SAAU,CACNC,mBACQC,KAAKT,SACG,gBAEA,iBAIpBU,QAAS,GAETC,6BACiBF,KAIFG,MAAMC,KACb,CACAC,SAAS,iBANAL,KAMUV,SACnBgB,SAAS,iBAPAN,KAOUd,SACnBK,SARSS,KAQMT,SACfgB,WATSP,KASQP,cAGzBe,YACOR,KAAKH,uBACCA,iBAAiBY,aAEvBT,KAAKJ,qBACCA,eAAea,cAG5BC,SAAW"} \ No newline at end of file diff --git a/amd/src/util/fittext-vue.js b/amd/src/util/fittext-vue.js index b751305..81b1cdf 100644 --- a/amd/src/util/fittext-vue.js +++ b/amd/src/util/fittext-vue.js @@ -62,8 +62,8 @@ export default { } }, template: ` -
- +
+
`, diff --git a/classes/period.php b/classes/period.php index b6173f3..855b900 100644 --- a/classes/period.php +++ b/classes/period.php @@ -117,12 +117,24 @@ class period { $pend = $pstart + $ptime; } + // Continue period numbers if so configured + if (get_config("local_treestudyplan","continueperiodnumberingnewpage")) { + $offset = 0; + foreach (studyplanpage::find_studyplan_children($page->studyplan()) as $p) { + if ($p->id() != $page->id()) { + $offset += $p->periods(); + } + } + $displaynr = $periodnr + $offset; + } else { + $displaynr = $periodnr; + } // And create the period. $period = self::add([ 'page_id' => $page->id(), 'period' => $periodnr, - 'fullname' => \get_string("period_default_fullname", "local_treestudyplan", $periodnr), - 'shortname' => \get_string("period_default_shortname", "local_treestudyplan", $periodnr), + 'fullname' => \get_string("period_default_fullname", "local_treestudyplan", $displaynr), + 'shortname' => \get_string("period_default_shortname", "local_treestudyplan", $displaynr), 'startdate' => date("Y-m-d", $pstart), 'enddate' => date("Y-m-d", $pend), ]); diff --git a/classes/studyline.php b/classes/studyline.php index 41b1ff6..38f4b76 100644 --- a/classes/studyline.php +++ b/classes/studyline.php @@ -801,8 +801,9 @@ class studyline { * Duplicate this studyplan page * @param studyplan $newstudyplan Studyplan to copy the line into * @param array $translation Mapping array of old item ids to new item ids for connection matching + * @param bool $bare Set to true to skip copying contents */ - public function duplicate($newpage, &$translation) : self { + public function duplicate($newpage, &$translation,$bare = false) : self { global $DB; // Clone the database fields. @@ -814,14 +815,16 @@ class studyline { $id = $DB->insert_record(self::TABLE, (array)$fields); $new = self::find_by_id($id); - // Next copy all the study items for this studyline. - // And record the original and copy id's in the $translation array. - // So the calling function can connect the new studyitems as required. - $children = studyitem::find_studyline_children($this); - $translation = []; - foreach ($children as $c) { - $newchild = $c->duplicate($new); - $translation[$c->id()] = $newchild->id(); + if (!$bare) { + // Next copy all the study items for this studyline. + // And record the original and copy id's in the $translation array. + // So the calling function can connect the new studyitems as required. + $children = studyitem::find_studyline_children($this); + $translation = []; + foreach ($children as $c) { + $newchild = $c->duplicate($new); + $translation[$c->id()] = $newchild->id(); + } } return $new; } diff --git a/classes/studyplanpage.php b/classes/studyplanpage.php index f76eb74..5d587d9 100644 --- a/classes/studyplanpage.php +++ b/classes/studyplanpage.php @@ -297,13 +297,39 @@ class studyplanpage { $page = self::find_by_id($id); // Make sure the new page is immediately cached. if (!$bare) { - // Add an empty study line - $lineinfo = ['page_id' => $id, - 'name' => get_string("default_line_name","local_treestudyplan"), - 'shortname' => get_string("default_line_shortname","local_treestudyplan"), - "color" => "#DDDDDD", - ]; - studyline::add($lineinfo); + + if(get_config("local_treestudyplan","copystudylinesnewpage")) { + $templatepage = null; + $templatelines = []; + // find the latest a page with lines in the plan + $pages = $plan->pages(true); + foreach ($pages as $p) { + if ($p->id() != $id) { + $lines = studyline::find_page_children($p); + if (count($lines) > 0) { + $templatepage = $p; + $templatelines = $lines; + } + } + } + + if (isset($templatepage)) { + foreach ($templatelines as $l) { + $map = []; // Bare copy still requires passing an empty array. + $l->duplicate($page,$map,true); // Do bare copy, which does not include line content. + } + } + } + + if(count(studyline::find_page_children($page)) == 0) { + // Add an empty study line if there are currently no study lines + $lineinfo = ['page_id' => $id, + 'name' => get_string("default_line_name","local_treestudyplan"), + 'shortname' => get_string("default_line_shortname","local_treestudyplan"), + "color" => "#DDDDDD", + ]; + studyline::add($lineinfo); + } } return $page; diff --git a/css/devstyles.css b/css/devstyles.css index e063890..b2f2d92 100644 --- a/css/devstyles.css +++ b/css/devstyles.css @@ -1890,25 +1890,33 @@ body.path-local-treestudyplan .editmode-switch-form > * { height: 7rem; } .path-local-treestudyplan .q-header .q-item-heading > .q-wrap { - display: grid; + display: flex; height: 10rem; white-space: nowrap; - grid: auto/1em 1fr; + flex-direction: column; overflow: hidden; + gap: 2px; +} +.path-local-treestudyplan .q-header .q-item-heading > .q-wrap > * { + min-height: 0; } .path-local-treestudyplan .q-header .q-item-heading > .q-wrap > .q-toggle { width: 100%; - line-height: 100%; + text-align: center; } .path-local-treestudyplan .q-header .q-item-heading > .q-wrap > .q-title { + flex: 1 0 0; margin-left: auto; margin-right: auto; overflow: hidden; writing-mode: vertical-rl; text-orientation: sideways; hyphens: auto; - padding-top: 2px; width: 100%; + display: flex; + justify-content: center; + align-items: center; + padding-bottom: 0.5rem; } .path-local-treestudyplan .q-header .q-condition-heading { text-align: left; diff --git a/lang/en/local_treestudyplan.php b/lang/en/local_treestudyplan.php index b114710..0cab8ad 100644 --- a/lang/en/local_treestudyplan.php +++ b/lang/en/local_treestudyplan.php @@ -361,6 +361,10 @@ $string["setting_competency_thresh_completed"] = 'Threshold for good (%)'; $string["settingdesc_competency_thresh_completed"] = 'Minimum percentage of proficient competencies for result "Completed'; $string["setting_competency_support_failed"] = 'Support "Failed" result'; $string["settingdesc_competency_support_failed"] = 'When the course end date has passed, mark course as "Failed" instead of "Progress"'; +$string["setting_copystudylinesnewpage"] = 'Copy study lines of last page to new page'; +$string["settingdesc_copystudylinesnewpage"] = 'When creating a new page, fill it with an empty copy of the study lines from the last page.'; +$string["setting_continueperiodnumberingnewpage"] = 'Continue periode numbering on new page'; +$string["settingdesc_continueperiodnumberingnewpage"] = 'When creating a new page, the default name of the periods will continue the existing sequence (e.g. P5, P6, P7, P8) instead of starting a new sequence (P1, P2, P3, P4)'; $string["grade_include"] = 'Include'; $string["grade_require"] = 'Require'; diff --git a/lang/nl/local_treestudyplan.php b/lang/nl/local_treestudyplan.php index 1d8f6bd..252478f 100644 --- a/lang/nl/local_treestudyplan.php +++ b/lang/nl/local_treestudyplan.php @@ -364,6 +364,11 @@ $string["setting_competency_thresh_completed"] = 'Drempelwaarde voor voltooid (% $string["settingdesc_competency_thresh_completed"] = 'Minimumpercentage behaalde competenties voor "Behaald"'; $string["setting_competency_support_failed"] = 'Onvoldoende ingeschakeld'; $string["settingdesc_competency_support_failed"] = 'Vink aan om "Onvoldoende" weer te kunnen geven als eind resultaat voor een cursus'; +$string["setting_copystudylinesnewpage"] = 'Bestaande leerlijnen overnemen op nieuw tabblad'; +$string["settingdesc_copystudylinesnewpage"] = 'Bij aanmaken van een nieuw tabblad wordt een lege kopie van de leerlijnen van het vorige bestaande tabblad overgenomen'; +$string["setting_continueperiodnumberingnewpage"] = 'Perioden doornummeren op nieuw tabblad'; +$string["settingdesc_continueperiodnumberingnewpage"] = 'Bij aanmaken van een nieuw tabblad, wordt de standaardnaam van perioden doorgenummerd (b.v. P5, P6, P7, P8) in plaats van herstart (P1, P2, P3, P4)'; + $string["grade_include"] = 'Doel'; $string["grade_require"] = 'Verplicht'; diff --git a/scss/studyplan-report.scss b/scss/studyplan-report.scss index a0b3158..ff5d66a 100644 --- a/scss/studyplan-report.scss +++ b/scss/studyplan-report.scss @@ -32,6 +32,7 @@ border-right: 1px solid var(--border-color); border-top: 1px solid var(--border-color); padding: 0.5rem; + } .q-period-heading, .q-line-heading, .q-item-heading, @@ -75,18 +76,24 @@ height: 7rem; } .q-item-heading > .q-wrap{ - display: grid; + display: flex; height: 10rem; white-space: nowrap; - grid: auto / 1em 1fr; + flex-direction: column; overflow: hidden; + gap: 2px; + + > * { + min-height: 0; + } > .q-toggle { width: 100%; - line-height: 100%; + text-align: center; } > .q-title { + flex: 1 0 0 ; margin-left: auto; margin-right: auto; overflow: hidden; @@ -94,8 +101,11 @@ text-orientation: sideways; hyphens: auto; - padding-top: 2px; width: 100%; + display: flex; + justify-content: center; + align-items: center; + padding-bottom: 0.5rem; } } diff --git a/settings.php b/settings.php index 281c684..71a12f1 100644 --- a/settings.php +++ b/settings.php @@ -138,9 +138,23 @@ if ($hassiteconfig) { $page->add(new admin_setting_configcheckbox('local_treestudyplan/toolboxleft', get_string('setting_toolboxleft', 'local_treestudyplan'), get_string('settingdesc_toolboxleft', 'local_treestudyplan'), - false, + true, )); + //get_config("local_treestudyplan","copystudylinesnewpage") + $page->add(new admin_setting_configcheckbox('local_treestudyplan/copystudylinesnewpage', + get_string('setting_copystudylinesnewpage', 'local_treestudyplan'), + get_string('settingdesc_copystudylinesnewpage', 'local_treestudyplan'), + false, + )); + + //get_config("local_treestudyplan","continueperiodnumberingnewpage") + $page->add(new admin_setting_configcheckbox('local_treestudyplan/continueperiodnumberingnewpage', + get_string('setting_continueperiodnumberingnewpage', 'local_treestudyplan'), + get_string('settingdesc_continueperiodnumberingnewpage', 'local_treestudyplan'), + false, + )); + if (premium::enabled()) { //get_config("local_treestudyplan","enablecoach") $page->add(new admin_setting_configcheckbox('local_treestudyplan/enablecoach', diff --git a/styles.css b/styles.css index e063890..b2f2d92 100644 --- a/styles.css +++ b/styles.css @@ -1890,25 +1890,33 @@ body.path-local-treestudyplan .editmode-switch-form > * { height: 7rem; } .path-local-treestudyplan .q-header .q-item-heading > .q-wrap { - display: grid; + display: flex; height: 10rem; white-space: nowrap; - grid: auto/1em 1fr; + flex-direction: column; overflow: hidden; + gap: 2px; +} +.path-local-treestudyplan .q-header .q-item-heading > .q-wrap > * { + min-height: 0; } .path-local-treestudyplan .q-header .q-item-heading > .q-wrap > .q-toggle { width: 100%; - line-height: 100%; + text-align: center; } .path-local-treestudyplan .q-header .q-item-heading > .q-wrap > .q-title { + flex: 1 0 0; margin-left: auto; margin-right: auto; overflow: hidden; writing-mode: vertical-rl; text-orientation: sideways; hyphens: auto; - padding-top: 2px; width: 100%; + display: flex; + justify-content: center; + align-items: center; + padding-bottom: 0.5rem; } .path-local-treestudyplan .q-header .q-condition-heading { text-align: left;