diff --git a/docs/customization/cbl/.pages b/docs/customization/cbl/.pages new file mode 100644 index 000000000..135f14916 --- /dev/null +++ b/docs/customization/cbl/.pages @@ -0,0 +1 @@ +title: CBL diff --git a/docs/customization/cbl/ratings.md b/docs/customization/cbl/ratings.md new file mode 100644 index 000000000..938b4f765 --- /dev/null +++ b/docs/customization/cbl/ratings.md @@ -0,0 +1,33 @@ +# Ratings + +## Options + +### `minRating` + +- Default: `5` +- The lowest rating presented on the primary scale + +### `maxRating` + +- Default: `12` +- The highest rating presented on the primary scale + +### `menuRatings` + +- Default: `[4, 3, 2, 1]` +- The ratings available on the secondary scale available under the menu button + +### `enableMissing` + +- Default: `true` +- Whether the `M` value is available on the primary scale + +### `enableDidNotMeet` + +- Default: `true` +- Whether the `DNM` value is available on the primary scale + +### `enableCheckmark` + +- Default: `true` +- Whether the checkmark value is available on the primary scale diff --git a/php-classes/Slate/CBL/Demonstrations/DidNotMeetDemonstrationSkill.php b/php-classes/Slate/CBL/Demonstrations/DidNotMeetDemonstrationSkill.php new file mode 100644 index 000000000..f9c985408 --- /dev/null +++ b/php-classes/Slate/CBL/Demonstrations/DidNotMeetDemonstrationSkill.php @@ -0,0 +1,9 @@ += maxValue) { + rating = maxValue; + } else if (rating < me.minValue && me.getMenuRatings().indexOf(rating) == -1) { + rating = null; + } + } + + // IMPORTANT: return a fresh object + return { + Class: value.Class || null, + DemonstratedLevel: rating, + TargetLevel: value.TargetLevel || null, + }; + }, + + isEqual: function(value1, value2) { + if (!value1 || !value2) { + return false; + } + + if (value1.Class != value2.Class) { + return false; + } + + if (value1.TargetLevel != value2.TargetLevel) { + return false; + } + + if (value1.DemonstratedLevel != value2.DemonstratedLevel) { + return false; + } + + return true; + }, + + onChange: function(value, oldValue) { + this.loadValue(); + this.callParent(arguments); + }, + + loadValue: function() { + var me = this, + value = me.value; + + console.info('loadValue', value); + me.setLevel(value.TargetLevel); + me.segmentedBtn.setValue(value.DemonstratedLevel); + }, + + + // component lifecycle + initItems: function() { + var me = this, + minRating = me.getMinRating(), + maxRating = me.getMaxRating(), + menuRatingItemsCfg = [], + menuRatings = me.getMenuRatings(), + menuRatingsLength = menuRatings && menuRatings.length, + menuRatingsIndex = 0, + rating, segmentedBtn; + + me.callParent(); + + // grab references to pre-configured items + segmentedBtn = me.segmentedBtn = me.down('segmentedbutton'); + + // insert config-managed label component + me.items.insert(0, me.getLabel()); + + // insert menu ratings button + if (menuRatingsLength) { + for (; menuRatingsIndex < menuRatingsLength; menuRatingsIndex++) { + rating = menuRatings[menuRatingsIndex]; + + menuRatingItemsCfg.push({ + value: rating, + text: Ext.util.Format.htmlEncode(Slate.cbl.util.Config.getAbbreviationForRating(rating)), + tooltip: Slate.cbl.util.Config.getTitleForRating(rating), + tooltipType: 'title' + }); + } + + me.menuBtn = segmentedBtn.add({ + value: 'MENU', + cls: 'slate-cbl-ratings-rater-menu-btn', + glyph: this.self.menuGlyph, + menu: { + items: menuRatingItemsCfg, + plain: true, + defaultType: 'menucheckitem', + defaults: { + group: 'rating', + listeners: { + checkchange: function(menuItem, checked) { + if (!checked) { + return; // ignore uncheck events + } + + var menuBtn = me.menuBtn; + + Ext.suspendLayouts(); + menuBtn.value = menuItem.value; + menuBtn.setGlyph(null); + menuBtn.setText(String(menuItem.value)); + menuBtn.setPressed(true); + me.segmentedBtn.setValue(menuItem.value); + Ext.resumeLayouts(true); + } + } + }, + listeners: { + beforeshow: function(menu) { + menu.setWidth(me.menuBtn.getWidth()) + } + } + }, + listeners: { + beforetoggle: function(btn, pressed) { + var checkedItem; + + // return menu button back to MENU value on un-press + if (!pressed && btn === me.menuBtn && btn.value != 'MENU') { + Ext.suspendLayouts(); + btn.value = 'MENU'; + btn.setText(null); + btn.setGlyph(me.self.menuGlyph); + checkedItem = btn.menu.down('[checked]'); + if (checkedItem) { + checkedItem.setChecked(false, true); + } + Ext.resumeLayouts(true); + } + + // prevent selecting menu button with no value set yet + if (pressed && btn.value == 'MENU') { + return false; + } + } + } + }); + } + + // insert M and DNM buttons + if (me.getEnableMissing()) { + segmentedBtn.add({ + value: 'M', + text: Ext.util.Format.htmlEncode(Slate.cbl.util.Config.getAbbreviationForRating('M')), + tooltip: Slate.cbl.util.Config.getTitleForRating('M'), + tooltipType: 'title' + }); + } + + if (me.getEnableDidNotMeet()) { + segmentedBtn.add({ + value: 'DNM', + text: Ext.util.Format.htmlEncode(Slate.cbl.util.Config.getAbbreviationForRating('DNM')), + tooltip: Slate.cbl.util.Config.getTitleForRating('DNM'), + tooltipType: 'title' + }); + } + + // insert min->max rating buttons + for (rating = minRating; rating <= maxRating; rating++) { + segmentedBtn.add({ + value: rating, + text: Ext.util.Format.htmlEncode(Slate.cbl.util.Config.getAbbreviationForRating(rating)), + tooltip: Slate.cbl.util.Config.getTitleForRating(rating), + tooltipType: 'title' + }); + } + + // insert checkmark button + if (me.getEnableCheckmark()) { + segmentedBtn.add({ + value: 'CHECK', + glyph: 'xf00c', // fa-check + tooltip: Slate.cbl.util.Config.getTitleForRating('CHECK'), + tooltipType: 'title' + }); + } + }, +}); diff --git a/sencha-workspace/packages/slate-cbl/src/field/ratings/Rater.scss b/sencha-workspace/packages/slate-cbl/src/field/ratings/Rater.scss new file mode 100644 index 000000000..1b1c10b94 --- /dev/null +++ b/sencha-workspace/packages/slate-cbl/src/field/ratings/Rater.scss @@ -0,0 +1,84 @@ +.slate-cbl-ratings-rater { + .x-btn-icon-el-default-large.x-btn-glyph { + // TODO replace selector? + font-size: 14px; + opacity: 0.9; + } + + .x-segmented-button-item-horizontal { + border-left: 1px solid rgba(black, 0.15); + + .x-btn-inner { + padding: 0; + } + + &:first-child { + border-left: 0; + } + } + + @for $n from 1 through length($level-colors) { + $color: nth($level-colors, $n); + $color-dark: nth($level-colors-dark, $n); + $color-medium: nth($level-colors-medium, $n); + $color-light: nth($level-colors-light, $n); + $color-shadow: darken($color-dark, 5%); + + &.cbl-level-#{$n} { + .x-btn { + background-color: $color-light; + border-bottom-color: $color-shadow; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.8); + + &:hover, + &:focus, + &.x-btn-over, + &.x-btn-focus { + background-color: $color-medium; + background-image: none; + } + + &.x-btn-pressed { + background-color: $color-dark; + background-image: linear-gradient(to bottom, $color, $color-dark); + border-color: $color-shadow; + + + .x-segmented-button-item-horizontal { + border-left-color: $color-shadow; + } + } + + .x-btn-inner { + font-weight: bold; + } + } + } + } +} + +.slate-cbl-ratings-rater-clear-btn { + .x-btn-inner { + font-size: 13px; + text-transform: uppercase; + } +} + +.slate-cbl-ratings-rater-menu-btn { + // match the framework selectors + + .x-btn-arrow-right > .x-btn-icon.x-btn-no-text.x-btn-button-default-large { + padding-right: 0; + } + + .x-btn-wrap-default-large.x-btn-arrow-right:after { + background: none; + color: white; + content: '\f0d7'; // fa-caret-down + font-family: FontAwesome; + font-size: 14px; + margin: 0 4px; + opacity: 0.6; + padding: 0; + width: 14px; + } +} diff --git a/sencha-workspace/packages/slate-cbl/src/field/ratings/SkillsCompetency.js b/sencha-workspace/packages/slate-cbl/src/field/ratings/SkillsCompetency.js index be9b5335d..4353a872a 100644 --- a/sencha-workspace/packages/slate-cbl/src/field/ratings/SkillsCompetency.js +++ b/sencha-workspace/packages/slate-cbl/src/field/ratings/SkillsCompetency.js @@ -5,9 +5,10 @@ Ext.define('Slate.cbl.field.ratings.SkillsCompetency', { 'Ext.util.Format', /* global Slate */ + 'Slate.cbl.field.ratings.Rater', 'Slate.cbl.model.Competency', 'Slate.sorter.Code', - 'Slate.ui.override.AddSorted' + 'Slate.ui.override.AddSorted', ], @@ -20,7 +21,7 @@ Ext.define('Slate.cbl.field.ratings.SkillsCompetency', { // container properties layout: 'anchor', - defaultType: 'slate-cbl-ratings-slider', + defaultType: 'slate-cbl-ratings-rater', defaults: { anchor: '100%', excludeForm: true // exclude from any parent forms @@ -170,4 +171,4 @@ Ext.define('Slate.cbl.field.ratings.SkillsCompetency', { } }); } -}); \ No newline at end of file +}); diff --git a/sencha-workspace/packages/slate-cbl/src/field/ratings/SkillsField.js b/sencha-workspace/packages/slate-cbl/src/field/ratings/SkillsField.js index 5ce49e821..9a1c87ea8 100644 --- a/sencha-workspace/packages/slate-cbl/src/field/ratings/SkillsField.js +++ b/sencha-workspace/packages/slate-cbl/src/field/ratings/SkillsField.js @@ -14,7 +14,7 @@ Ext.define('Slate.cbl.field.ratings.SkillsField', { 'Slate.cbl.store.Competencies', 'Slate.cbl.store.StudentCompetencies', 'Slate.cbl.field.ratings.SkillsCompetency', - 'Slate.cbl.field.ratings.Slider', + 'Slate.cbl.field.ratings.Rater', 'Slate.cbl.field.SkillsSelector', 'Jarvus.override.data.RequireLoadedStores', @@ -275,7 +275,7 @@ Ext.define('Slate.cbl.field.ratings.SkillsField', { onRatingChange: function(ratingSlider, rating) { var me = this; - me.setSkillValue(ratingSlider.getSkill().getId(), rating, ratingSlider.getLevel()); + me.setSkillValue(rating`Slider`.getSkill().getId(), rating, ratingSlider.getLevel()); me.validate(); me.checkDirty(); @@ -578,4 +578,4 @@ Ext.define('Slate.cbl.field.ratings.SkillsField', { // clear queue immediately after request is executed studentCompetenciesLoadQueue.length = 0; } -}); \ No newline at end of file +}); diff --git a/sencha-workspace/packages/slate-cbl/src/field/ratings/Slider.js b/sencha-workspace/packages/slate-cbl/src/field/ratings/Slider.js index 9191afcd4..e53de03b5 100644 --- a/sencha-workspace/packages/slate-cbl/src/field/ratings/Slider.js +++ b/sencha-workspace/packages/slate-cbl/src/field/ratings/Slider.js @@ -459,4 +459,4 @@ Ext.define('Slate.cbl.field.ratings.Slider', { }, function(Slider) { // make this class statically observable so instances can monitor other instances for tip showing Ext.util.Observable.observe(Slider); -}); \ No newline at end of file +}); diff --git a/sencha-workspace/packages/slate-cbl/src/field/ratings/StudentCompetenciesField.js b/sencha-workspace/packages/slate-cbl/src/field/ratings/StudentCompetenciesField.js index dbd0f4454..3b44513a3 100644 --- a/sencha-workspace/packages/slate-cbl/src/field/ratings/StudentCompetenciesField.js +++ b/sencha-workspace/packages/slate-cbl/src/field/ratings/StudentCompetenciesField.js @@ -210,7 +210,7 @@ Ext.define('Slate.cbl.field.ratings.StudentCompetenciesField', { i = 0, skill; for (; i < length; i++) { - skill = card.query('field')[i].getSkill(); + skill = fields[i].getSkill(); if (skill) { me.removeSkillValue(skill.getId()); @@ -291,7 +291,7 @@ Ext.define('Slate.cbl.field.ratings.StudentCompetenciesField', { permanentCards.push(competencyId); } - card.setSkillValue(skillId, skillData.DemonstratedLevel, skillData.TargetLevel); + card.setSkillValue(skillId, skillData); skillIds.push(skillId); } diff --git a/sencha-workspace/packages/slate-cbl/src/field/ratings/StudentCompetency.js b/sencha-workspace/packages/slate-cbl/src/field/ratings/StudentCompetency.js index 3606b2b8a..179661972 100644 --- a/sencha-workspace/packages/slate-cbl/src/field/ratings/StudentCompetency.js +++ b/sencha-workspace/packages/slate-cbl/src/field/ratings/StudentCompetency.js @@ -7,7 +7,7 @@ Ext.define('Slate.cbl.field.ratings.StudentCompetency', { requires: [ /* global Slate */ 'Slate.cbl.model.StudentCompetency', - 'Slate.cbl.field.ratings.Slider' + 'Slate.cbl.field.ratings.Rater', ], @@ -35,10 +35,10 @@ Ext.define('Slate.cbl.field.ratings.StudentCompetency', { // component configuration componentCls: 'slate-cbl-ratings-studentcompetency', - padding: '16 75', + padding: '16', // container configuration - defaultType: 'slate-cbl-ratings-slider', + defaultType: 'slate-cbl-ratings-rater', layout: 'anchor', defaults: { anchor: '100%', @@ -48,13 +48,27 @@ Ext.define('Slate.cbl.field.ratings.StudentCompetency', { { dock: 'top', - xtype: 'component', - itemId: 'competencyInfo', + xtype: 'container', + layout: 'hbox', hidden: true, - tpl: [ - '
{Statement}' - ] + itemId: 'competencyInfo', + items: [{ + flex: 1, + xtype: 'component', + itemId: 'competencyInfoHeader', + tpl: [ + '
{Statement}', + '