From a3f1b31785c4f64503b9e6c0bc60231f1329304d Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Wed, 30 Aug 2023 13:58:00 -0600 Subject: [PATCH 01/61] Bump required FW version --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 55f1c2f..8de9b72 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ The pageNav buttons will respect any [locking](https://github.com/adaptlearning/ ---------------------------- -**Framework versions:** 5.2+
+**Framework versions:** 5.19.1+
**Vanilla versions:** 5.1.1+
**Author / maintainer:** Kineo
**Accessibility support:** WAI AA
diff --git a/package.json b/package.json index 7bdb8b5..42854c7 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "url": "git://github.com/cgkineo/adapt-pageNav.git" }, "version": "2.3.2", - "framework": ">=5.2", + "framework": ">=5.19.1", "homepage": "https://github.com/cgkineo/adapt-pageNav", "issues": "https://github.com/cgkineo/adapt-pageNav/issues/", "component": "pageNav", From 5c04cfca8834e7f90423c925795326decd49ef9f Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Wed, 30 Aug 2023 15:49:21 -0600 Subject: [PATCH 02/61] WIP - added new templates, view, model --- js/PageNavModel.js | 222 ++++++++++++++++++++++++ js/PageNavView.js | 179 ++++++++++++++++++++ js/adapt-pageNav.js | 16 +- js/model.js | 253 ---------------------------- js/view.js | 194 --------------------- templates/pageNav-tooltip.hbs | 7 - templates/pageNav.hbs | 17 -- templates/pageNav.jsx | 38 +++++ templates/pageNavItem.jsx | 77 +++++++++ templates/partials/pageNav-item.hbs | 42 ----- 10 files changed, 522 insertions(+), 523 deletions(-) create mode 100644 js/PageNavModel.js create mode 100644 js/PageNavView.js delete mode 100644 js/model.js delete mode 100644 js/view.js delete mode 100644 templates/pageNav-tooltip.hbs delete mode 100644 templates/pageNav.hbs create mode 100644 templates/pageNav.jsx create mode 100644 templates/pageNavItem.jsx delete mode 100644 templates/partials/pageNav-item.hbs diff --git a/js/PageNavModel.js b/js/PageNavModel.js new file mode 100644 index 0000000..98da5e2 --- /dev/null +++ b/js/PageNavModel.js @@ -0,0 +1,222 @@ +import Adapt from 'core/js/adapt'; +import location from 'core/js/location'; +import data from 'core/js/data'; +import ComponentModel from 'core/js/models/componentModel'; + +class PageNavModel extends ComponentModel { + getNavigationData() { + /* + * Combine the config, model, order, index and type for each _buttons + * Add each combined item to an array + */ + const buttonTypeModels = { + _returnToPreviousLocation: this.getReturnToPreviousLocation(), + _page: this.getCurrentPage(), + _up: this.getCurrentMenu(), + _root: Adapt.course, + _next: this.getNextPage(), + _previous: this.getPrevPage(), + _sibling: this.getSiblingPages(), + _close: new Backbone.Model({ + _id: '', + _onClick: ` + try { + var scormWrapper = require('extensions/adapt-contrib-spoor/js/scorm/wrapper'); + if (scormWrapper) { + var scormWrapperInstance = scormWrapper.getInstance(); + if (scormWrapperInstance.lmsConnected && !scormWrapperInstance.finishCalled) { + scormWrapperInstance.finish(); + } + } + } catch (err) {} + top.window.close(); + ` + }) + }; + + const data = []; + const buttons = this.get('_buttons'); + + if (!buttons) { return data; } + + let order = 0; + let item; + const currentPageComplete = buttonTypeModels._page.get('_isComplete'); + + for (const attrName in buttons) { + + const buttonConfig = buttons[attrName]; + let buttonModel = buttonTypeModels[attrName]; + + if (attrName === '_sibling') { + + // Skip if only one sibling + if (buttonModel.length <= 1) continue; + + // Generate sibling entries + _.each(buttonModel, function(model, index) { + + item = model.toJSON(); + _.extend(item, buttonConfig, { + type: attrName, + index, + _isCurrent: item._id === Location._currentId, + order: order++, + locked: item._isLocked || (buttonConfig._lockUntilPageComplete && !currentPageComplete) + }); + data.push(item); + + }); + + continue; + } + + // Find buttonModel from config._customRouteId if not found in defined type + if (buttonConfig._customRouteId) buttonModel = data.findById(buttonConfig._customRouteId); + + // Convert found buttonModel to json if exists or create an 'undefined' json + item = buttonModel ? buttonModel.toJSON() : { _isHidden: true }; + + _.extend(item, buttonConfig, { + type: attrName, + index: 0, + order: order++, + locked: item._isLocked || (buttonConfig._lockUntilPageComplete && !currentPageComplete) + }); + data.push(item); + } + + // requires a stable sorting algorithm - native sorting in Chrome is unstable (should be stable from Chrome 70) + const orderedData = _.sortBy(data, '_order'); + + orderedData.forEach(function(item, index) { + item._index = index; + }); + + return orderedData; + + }; + + getReturnToPreviousLocation() { + return location._previousId ? data.findById(location._previousId) : null; + }; + + getCurrentPage() { + const parents = this.getAncestorModels ? this.getAncestorModels() : this.getParents().models; + for (let i = 0, l = parents.length; i < l; i++) { + + const model = parents[i]; + switch (model.get('_type')) { + case 'page': + return model; + } + } + }; + + getCurrentMenu() { + const parents = this.getAncestorModels ? this.getAncestorModels() : this.getParents().models; + + for (let i = 0, l = parents.length; i < l; i++) { + + const model = parents[i]; + switch (model.get('_type')) { + case 'menu': + case 'course': + return model; + } + } + }; + + getSiblingPages() { + const currentMenu = this.getCurrentMenu(); + let siblingModels = currentMenu.getAllDescendantModels(true); + + siblingModels = _.filter(siblingModels, function(model) { + return model.get('_type') === 'page' && model.get('_isAvailable') && + (!this.get('_shouldSkipOptionalPages') || !model.get('_isOptional')); + }, this); + + return siblingModels; + }; + + getPrevPage() { + const currentPage = this.getCurrentPage(); + const currentPageId = currentPage.get('_id'); + + const pages = this.getPages(); + + let hasFoundCurrentPage = false; + for (let i = pages.length - 1; i > -1; i--) { + const page = pages[i]; + const isNotAvailable = !page.get('_isAvailable'); + if (isNotAvailable) continue; + + if (!hasFoundCurrentPage) { + hasFoundCurrentPage = page.get('_id') === currentPageId; + continue; + } + + if (!this.get('_shouldSkipOptionalPages') || !page.get('_isOptional')) { + return page; + } + } + }; + + getNextPage() { + const currentPage = this.getCurrentPage(); + const currentPageId = currentPage.get('_id'); + + const pages = this.getPages(); + + let hasFoundCurrentPage = false; + for (let i = 0, l = pages.length; i < l; i++) { + const page = pages[i]; + const isNotAvailable = !page.get('_isAvailable'); + if (isNotAvailable) continue; + + if (!hasFoundCurrentPage) { + hasFoundCurrentPage = page.get('_id') === currentPageId; + continue; + } + + if (!this.get('_shouldSkipOptionalPages') || !page.get('_isOptional')) { + return page; + } + } + }; + + getPages() { + const loopStyle = this.get('_loopStyle'); + + if (!loopStyle) return []; + + let loop = false; + let descendants; + let currentMenu; + + switch (loopStyle) { + case 'allPages': + loop = true; + descendants = Adapt.course.getAllDescendantModels(true); + break; + case 'siblings': + loop = true; + // falls through + default: + currentMenu = this.getCurrentMenu(); + descendants = currentMenu.getAllDescendantModels(true); + } + + if (loop) { + // Create a double copy to allow loop searching + descendants = descendants.concat(descendants); + } + + return _.filter(descendants, function(model) { + return model.get('_type') === 'page'; + }); + }; + +} + +export default PageNavModel; diff --git a/js/PageNavView.js b/js/PageNavView.js new file mode 100644 index 0000000..86bfeff --- /dev/null +++ b/js/PageNavView.js @@ -0,0 +1,179 @@ +import Adapt from 'core/js/adapt'; +import ComponentView from 'core/js/views/componentView'; + +class PageNavView extends ComponentView { + getAttributes($node) { + const attrs = {}; + _.each($node[0].attributes, function (attribute) { + attrs[attribute.name] = attribute.value; + }); + return attrs; + }; + + // events: { + // 'click .js-pagenav-btn': 'onButtonClick', + // 'mouseover .js-pagenav-btn': 'onButtonTooltip' + // }, + + preRender() { + this.model.set('_items', this.model.getNavigationData()); + + this.$el.addClass('pagenav ' + this.model.get('_id')); + + _.bindAll(this, 'postRender', 'checkButtonStates'); + + this.listenTo(Adapt, 'remove', this.remove); + this.listenTo(Adapt.contentObjects, { + 'change:_isComplete change:_isLocked': this.onContentObjectComplete + }); + }; + + postRender() { + this.checkButtonStates(); + this.setReadyStatus(); + this.setupInview(); + }; + + setupInview() { + const selector = this.getInviewElementSelector(); + + if (!selector) return this.setCompletionStatus(); + + this.setupInviewCompletion(selector); + }; + + /** + * determines which element should be used for inview logic - body, instruction, title or widget - and returns the selector for that element + */ + getInviewElementSelector() { + if (this.model.get('body')) return '.component__body'; + if (this.model.get('instruction')) return '.component__instruction'; + if (this.model.get('displayTitle')) return '.component__title'; + if (this.model.get('_buttons')) return '.component__widget'; + + return null; + }; + + onContentObjectComplete() { + _.defer(this.checkButtonStates); + }; + + checkButtonStates() { + this.$('button').each(function(index, item) { + this.checkButtonState(item); + }.bind(this)); + }; + + checkButtonState(button) { + const $button = $(button); + const id = $button.attr('data-id'); + const index = $button.attr('data-item-index'); + + if (!id) return; + + // get the button data + // const items = this.model.getNavigationData(); + // const data = items[index]; + + // rerender the button + // const $buttonRendered = $(Handlebars.partials['pageNav-item'](data)); + // if ($buttonRendered.length === 0) { + // $button.remove(); + // return; + // } + + // get button attribute names from current and rerendered + // const renderedAttrs = this.getAttributes($buttonRendered); + // const attrs = this.getAttributes($button); + // const renderedAttrNames = _.keys(renderedAttrs); + // const attrNames = _.keys(attrs); + + // // remove redundant attributes + // const removeAttrNames = _.difference(attrNames, renderedAttrNames); + // removeAttrNames.forEach(function(name) { + // $button.removeAttr(name); + // }); + + // // update remaining attributes + // $button.attr(renderedAttrs); + + // // update button text + // $button.html($buttonRendered.html()); + }; + + onButtonClick(event) { + const $target = $(event.currentTarget); + const isLocked = $target.hasClass('is-locked'); + const isSelected = $target.hasClass('is-selected'); + + if (isLocked || isSelected) return; + + const id = $target.attr('data-id'); + const index = $target.attr('data-item-index'); + let items; + + switch (id) { + case '': + items = this.model.getNavigationData(); + // try { + // const execute = new Function(items[index]._onClick||''); + // execute(); + // } catch (err) { + // Adapt.log.error(err); + // } + break; + default: + this.navigateTo(id); + } + }; + + // onButtonTooltip(event) { + // const $target = $(event.currentTarget); + + // const id = $target.attr('data-id') || this.model.getCurrentPage().get('_id'); + + // if (!id) { + // return; + // } + + // // If tooltip isn't defined allow the event to propogate down to the document + // if (!$target.attr('data-tooltip')) { + // return; + // } + + // // Don't allow event to propogate, to stop the document over events + // event.stopPropagation(); + + // // If this tooltip is already rendered then skip + // if (Adapt.tooltip) { + + // const type = $target.attr('data-type'); + // const index = $target.attr('data-index'); + // const isCurrentTooltip = (Adapt.tooltip.type === type) && (Adapt.tooltip.index === index); + + // if (isCurrentTooltip) { + // return; + // } + + // } + + // const tooltip = new Tooltip({ + // $target, + // model: Adapt.findById(id) + // }); + + // this.$('.pagenav__tooltip-container').append(tooltip.$el); + + // }; + + navigateTo(id) { + const isCourse = (id === Adapt.course.get('_id')); + const hash = '#' + (isCourse ? '/' : '/id/' + id); + + Backbone.history.navigate(hash, { trigger: true, replace: false }); + }; +} + +PageNavView.template = 'pageNav.jsx'; + +export default PageNavView; diff --git a/js/adapt-pageNav.js b/js/adapt-pageNav.js index c29dbf7..7ec018e 100644 --- a/js/adapt-pageNav.js +++ b/js/adapt-pageNav.js @@ -1,12 +1,8 @@ -define([ - 'core/js/adapt', - './model', - './view' -], function(Adapt, Model, View) { - - return Adapt.register('pageNav', { - model: Model, - view: View - }); +import components from 'core/js/components'; +import PageNavView from './PageNavView'; +import PageNavModel from './PageNavModel'; +export default components.register('pageNav', { + view: PageNavView, + model: PageNavModel }); diff --git a/js/model.js b/js/model.js deleted file mode 100644 index 86b2b05..0000000 --- a/js/model.js +++ /dev/null @@ -1,253 +0,0 @@ -define([ - 'core/js/adapt', - 'core/js/location', - 'core/js/models/componentModel' -], function(Adapt, Location, ComponentModel) { - - var Model = ComponentModel.extend({ - - getNavigationData: function() { - - /* - * Combine the config, model, order, index and type for each _buttons - * Add each combined item to an array - */ - - var buttonTypeModels = { - _returnToPreviousLocation: this.getReturnToPreviousLocation(), - _page: this.getCurrentPage(), - _up: this.getCurrentMenu(), - _root: Adapt.course, - _next: this.getNextPage(), - _previous: this.getPrevPage(), - _sibling: this.getSiblingPages(), - _close: new Backbone.Model({ - _id: '', - _onClick: ` -try { - var scormWrapper = require('extensions/adapt-contrib-spoor/js/scorm/wrapper'); - if (scormWrapper) { - var scormWrapperInstance = scormWrapper.getInstance(); - if (scormWrapperInstance.lmsConnected && !scormWrapperInstance.finishCalled) { - scormWrapperInstance.finish(); - } - } -} catch (err) {} -top.window.close(); -` - }) - }; - - var data = []; - var buttons = this.get('_buttons'); - - if (!buttons) { - return data; - } - - var order = 0; - var item; - var currentPageComplete = buttonTypeModels._page.get('_isComplete'); - - for (var attrName in buttons) { - - var buttonConfig = buttons[attrName]; - var buttonModel = buttonTypeModels[attrName]; - - if (attrName === '_sibling') { - - // Skip if only one sibling - if (buttonModel.length <= 1) continue; - - // Generate sibling entries - _.each(buttonModel, function(model, index) { - - item = model.toJSON(); - _.extend(item, buttonConfig, { - type: attrName, - index: index, - _isCurrent: item._id === Location._currentId, - order: order++, - locked: item._isLocked || (buttonConfig._lockUntilPageComplete && !currentPageComplete) - }); - data.push(item); - - }); - - continue; - - } - - // Find buttonModel from config._customRouteId if not found in defined type - if (buttonConfig._customRouteId) buttonModel = Adapt.findById(buttonConfig._customRouteId); - - // Convert found buttonModel to json if exists or create an 'undefined' json - item = buttonModel ? buttonModel.toJSON() : { '_isHidden': true }; - - _.extend(item, buttonConfig, { - type: attrName, - index: 0, - order: order++, - locked: item._isLocked || (buttonConfig._lockUntilPageComplete && !currentPageComplete) - }); - data.push(item); - - } - - // requires a stable sorting algorithm - native sorting in Chrome is unstable (should be stable from Chrome 70) - var orderedData = _.sortBy(data, '_order'); - - orderedData.forEach(function(item, index) { - item._index = index; - }); - - return orderedData; - - }, - - getReturnToPreviousLocation: function() { - - return Adapt.location._previousId ? Adapt.findById(Adapt.location._previousId) : null; - - }, - - getCurrentPage: function() { - - var parents = this.getAncestorModels ? this.getAncestorModels() : this.getParents().models; - for (var i = 0, l = parents.length; i < l; i++) { - - var model = parents[i]; - switch (model.get('_type')) { - case 'page': - return model; - } - - } - - }, - - getCurrentMenu: function() { - - var parents = this.getAncestorModels ? this.getAncestorModels() : this.getParents().models; - for (var i = 0, l = parents.length; i < l; i++) { - - var model = parents[i]; - switch (model.get('_type')) { - case 'menu': - case 'course': - return model; - } - - } - - }, - - getSiblingPages: function() { - - var currentMenu = this.getCurrentMenu(); - var siblingModels = currentMenu.getAllDescendantModels(true); - - siblingModels = _.filter(siblingModels, function(model) { - return model.get('_type') === 'page' && model.get('_isAvailable') && - (!this.get('_shouldSkipOptionalPages') || !model.get('_isOptional')); - }, this); - - return siblingModels; - - }, - - getPrevPage: function() { - - var currentPage = this.getCurrentPage(); - var currentPageId = currentPage.get('_id'); - - var pages = this.getPages(); - - var hasFoundCurrentPage = false; - for (var i = pages.length-1; i > -1; i--) { - - var page = pages[i]; - var isNotAvailable = !page.get('_isAvailable'); - if (isNotAvailable) continue; - - if (!hasFoundCurrentPage) { - hasFoundCurrentPage = page.get('_id') === currentPageId; - continue; - } - - if (!this.get('_shouldSkipOptionalPages') || !page.get('_isOptional')) { - return page; - } - - } - - return; - - }, - - getNextPage: function() { - - var currentPage = this.getCurrentPage(); - var currentPageId = currentPage.get('_id'); - - var pages = this.getPages(); - - var hasFoundCurrentPage = false; - for (var i = 0, l = pages.length; i < l; i++) { - - var page = pages[i]; - var isNotAvailable = !page.get('_isAvailable'); - if (isNotAvailable) continue; - - if (!hasFoundCurrentPage) { - hasFoundCurrentPage = page.get('_id') === currentPageId; - continue; - } - - if (!this.get('_shouldSkipOptionalPages') || !page.get('_isOptional')) { - return page; - } - - } - - return; - - }, - - getPages: function() { - - var loopStyle = this.get('_loopStyle'); - - if (!loopStyle) return []; - - var loop = false; - var descendants; - switch (loopStyle) { - case 'allPages': - loop = true; - descendants = Adapt.course.getAllDescendantModels(true); - break; - case 'siblings': - loop = true; - /* falls through */ - default: - var currentMenu = this.getCurrentMenu(); - descendants = currentMenu.getAllDescendantModels(true); - } - - if (loop) { - // Create a double copy to allow loop searching - descendants = descendants.concat(descendants); - } - - return _.filter(descendants, function(model) { - return model.get('_type') === 'page'; - }); - - } - - }); - - return Model; - -}); diff --git a/js/view.js b/js/view.js deleted file mode 100644 index e641927..0000000 --- a/js/view.js +++ /dev/null @@ -1,194 +0,0 @@ - -define([ - 'core/js/adapt', - 'core/js/views/componentView', - './tooltip' -], function(Adapt, ComponentView, Tooltip) { - - function getAttributes($node){ - var attrs = {}; - _.each($node[0].attributes, function (attribute) { - attrs[attribute.name] = attribute.value; - }); - return attrs; - } - - var View = ComponentView.extend({ - - events: { - 'click .js-pagenav-btn': 'onButtonClick', - 'mouseover .js-pagenav-btn': 'onButtonTooltip' - }, - - preRender: function() { - this.model.set('_items', this.model.getNavigationData()); - - this.$el.addClass('pagenav ' + this.model.get('_id')); - - _.bindAll(this, 'postRender', 'checkButtonStates'); - - this.listenTo(Adapt, 'remove', this.remove); - this.listenTo(Adapt.contentObjects, { - 'change:_isComplete change:_isLocked': this.onContentObjectComplete - }); - - }, - - postRender: function() { - this.checkButtonStates(); - this.setReadyStatus(); - this.setupInview(); - }, - - setupInview: function() { - const selector = this.getInviewElementSelector(); - if (!selector) return this.setCompletionStatus(); - this.setupInviewCompletion(selector); - }, - - /** - * determines which element should be used for inview logic - body, instruction, title or widget - and returns the selector for that element - */ - getInviewElementSelector: function() { - if (this.model.get('body')) return '.component__body'; - if (this.model.get('instruction')) return '.component__instruction'; - if (this.model.get('displayTitle')) return '.component__title'; - if (this.model.get('_buttons')) return '.component__widget'; - return null; - }, - - onContentObjectComplete: function() { - - _.defer(this.checkButtonStates); - - }, - - checkButtonStates: function() { - - this.$('button').each(function(index, item) { - this.checkButtonState(item); - }.bind(this)); - - }, - - checkButtonState: function(button) { - - var $button = $(button); - var id = $button.attr('data-id'); - var index = $button.attr('data-item-index'); - - if (!id) return; - - // get the button data - var items = this.model.getNavigationData(); - var data = items[index]; - - // rerender the button - var $buttonRendered = $(Handlebars.partials['pageNav-item'](data)); - if ($buttonRendered.length === 0) { - $button.remove(); - return; - } - - // get button attribute names from current and rerendered - var renderedAttrs = getAttributes($buttonRendered); - var attrs = getAttributes($button); - var renderedAttrNames = _.keys(renderedAttrs); - var attrNames = _.keys(attrs); - - // remove redundant attributes - var removeAttrNames = _.difference(attrNames, renderedAttrNames); - removeAttrNames.forEach(function(name) { - $button.removeAttr(name); - }); - - // update remaining attributes - $button.attr(renderedAttrs); - - // update button text - $button.html($buttonRendered.html()); - - }, - - onButtonClick: function(event) { - - var $target = $(event.currentTarget); - var isLocked = $target.hasClass('is-locked'); - var isSelected = $target.hasClass('is-selected'); - - if (isLocked || isSelected) return; - - var id = $target.attr('data-id'); - var index = $target.attr('data-item-index'); - - switch (id) { - case '': - var items = this.model.getNavigationData(); - try { - var execute = new Function(items[index]._onClick||''); - execute(); - } catch (err) { - Adapt.log.error(err); - } - break; - default: - this.navigateTo(id); - break; - } - }, - - onButtonTooltip: function(event) { - - var $target = $(event.currentTarget); - - var id = $target.attr('data-id') || this.model.getCurrentPage().get('_id'); - - if (!id) { - return; - } - - // If tooltip isn't defined allow the event to propogate down to the document - if (!$target.attr('data-tooltip')) { - return; - } - - // Don't allow event to propogate, to stop the document over events - event.stopPropagation(); - - - // If this tooltip is already rendered then skip - if (Adapt.tooltip) { - - var type = $target.attr('data-type'); - var index = $target.attr('data-index'); - var isCurrentTooltip = (Adapt.tooltip.type === type) && (Adapt.tooltip.index === index); - - if (isCurrentTooltip) { - return; - } - - } - - var tooltip = new Tooltip({ - $target: $target, - model: Adapt.findById(id) - }); - - this.$('.pagenav__tooltip-container').append(tooltip.$el); - - }, - - navigateTo: function(id) { - - var isCourse = (id === Adapt.course.get('_id')); - var hash = '#' + (isCourse ? '/' : '/id/' + id); - - Backbone.history.navigate(hash, { trigger: true, replace: false }); - - } - - }); - - return View; - -}); diff --git a/templates/pageNav-tooltip.hbs b/templates/pageNav-tooltip.hbs deleted file mode 100644 index 8f9bedc..0000000 --- a/templates/pageNav-tooltip.hbs +++ /dev/null @@ -1,7 +0,0 @@ - - - diff --git a/templates/pageNav.hbs b/templates/pageNav.hbs deleted file mode 100644 index 05f5429..0000000 --- a/templates/pageNav.hbs +++ /dev/null @@ -1,17 +0,0 @@ - diff --git a/templates/pageNav.jsx b/templates/pageNav.jsx new file mode 100644 index 0000000..22e6d80 --- /dev/null +++ b/templates/pageNav.jsx @@ -0,0 +1,38 @@ +import Adapt from 'core/js/adapt'; +import React from 'react'; +import { templates } from 'core/js/reactHelpers'; + +export default function PageNav(props) { + const globals = Adapt.course.get('_globals'); + + const { + _items + } = props; + + return ( +
+ + + +
+ +
+ + + +
+ +
+ ); +} diff --git a/templates/pageNavItem.jsx b/templates/pageNavItem.jsx new file mode 100644 index 0000000..604e8ed --- /dev/null +++ b/templates/pageNavItem.jsx @@ -0,0 +1,77 @@ +import Adapt from 'core/js/adapt'; +import React from 'react'; +import { compile, classes } from 'core/js/reactHelpers'; + +export default function PageNavItem(props) { + const globals = Adapt.course.get('_globals'); + + const { + _isEnabled, + _isHidden, + _isCurrent, + _classes, + _iconClass, + locked, + type, + _id, + _index, + text + } = props; + + return ( + <> + {_isEnabled && + + + } + + + ); +} diff --git a/templates/partials/pageNav-item.hbs b/templates/partials/pageNav-item.hbs deleted file mode 100644 index 04a7751..0000000 --- a/templates/partials/pageNav-item.hbs +++ /dev/null @@ -1,42 +0,0 @@ -{{import_globals}} -{{#if _isEnabled}} - -{{/if}} From 54cdae996f4eac5782ef0b0bd91f866aacfe8b2c Mon Sep 17 00:00:00 2001 From: Brad Simpson Date: Thu, 31 Aug 2023 10:31:47 -0600 Subject: [PATCH 03/61] Get buttons to actually display --- README.md | 16 +++++++------- js/PageNavModel.js | 51 ++++++++++++++++++++++++------------------- js/PageNavView.js | 10 ++++----- templates/pageNav.jsx | 2 +- 4 files changed, 42 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 8de9b72..083a092 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # adapt-pageNav -**PageNav** is a *presentation component* that adds basic navigation controls to a page +**PageNav** is a *presentation component* that adds basic navigation controls to a page. the page nav extension in action @@ -13,13 +13,13 @@ The attributes listed below are used in *components.json* to configure **PageNav Navigation bar component which can contain some or all of the following buttons: -- _returnToPreviousLocation (takes you back to the previous location - not back in history, just the last routed location) -- _root (takes you back to top level menu) -- _up (takes you to the menu the next level up in the hierarchy) -- _previous (navigates directly to the previous page, if exists, without having to navigate via the menu) -- _next (navigates directly to the next page, if exists, without having to navigate via the menu) -- _sibling (1,2,3,4 etc buttons representing each sibling page) -- _close (closes the course window - only possible if the course was launched in a popup window) +- `_returnToPreviousLocation` (takes you back to the previous location - not back in history, just the last routed location) +- `_root` (takes you back to top level menu) +- `_up` (takes you to the menu the next level up in the hierarchy) +- `_previous` (navigates directly to the previous page, if exists, without having to navigate via the menu) +- `_next` (navigates directly to the next page, if exists, without having to navigate via the menu) +- `_sibling` (1,2,3,4 etc buttons representing each sibling page) +- `_close` (closes the course window - only possible if the course was launched in a popup window) The pageNav buttons will respect any [locking](https://github.com/adaptlearning/adapt_framework/wiki/Locking-objects-with-'_isLocked'-and-'_lockType'#using-locking-with-menus) that has been configured in Adapt. In cases not covered by Adapt's locking system - such as a [start page](https://github.com/adaptlearning/adapt_framework/wiki/Content-starts-with-course.json#example-1) that appears immediately before the main menu - the setting `_lockUntilPageComplete` can be used to disable the button until the current page has been completed. diff --git a/js/PageNavModel.js b/js/PageNavModel.js index 98da5e2..d84cc04 100644 --- a/js/PageNavModel.js +++ b/js/PageNavModel.js @@ -4,10 +4,16 @@ import data from 'core/js/data'; import ComponentModel from 'core/js/models/componentModel'; class PageNavModel extends ComponentModel { + init() { + this.set('_items', this.getNavigationData()); + + super.init(); + }; + getNavigationData() { /* - * Combine the config, model, order, index and type for each _buttons - * Add each combined item to an array + Combine the config, model, order, index and type for each _buttons + Add each combined item to an array */ const buttonTypeModels = { _returnToPreviousLocation: this.getReturnToPreviousLocation(), @@ -17,21 +23,7 @@ class PageNavModel extends ComponentModel { _next: this.getNextPage(), _previous: this.getPrevPage(), _sibling: this.getSiblingPages(), - _close: new Backbone.Model({ - _id: '', - _onClick: ` - try { - var scormWrapper = require('extensions/adapt-contrib-spoor/js/scorm/wrapper'); - if (scormWrapper) { - var scormWrapperInstance = scormWrapper.getInstance(); - if (scormWrapperInstance.lmsConnected && !scormWrapperInstance.finishCalled) { - scormWrapperInstance.finish(); - } - } - } catch (err) {} - top.window.close(); - ` - }) + _close: this.getClose() }; const data = []; @@ -94,7 +86,6 @@ class PageNavModel extends ComponentModel { }); return orderedData; - }; getReturnToPreviousLocation() { @@ -117,7 +108,6 @@ class PageNavModel extends ComponentModel { const parents = this.getAncestorModels ? this.getAncestorModels() : this.getParents().models; for (let i = 0, l = parents.length; i < l; i++) { - const model = parents[i]; switch (model.get('_type')) { case 'menu': @@ -142,10 +132,9 @@ class PageNavModel extends ComponentModel { getPrevPage() { const currentPage = this.getCurrentPage(); const currentPageId = currentPage.get('_id'); - const pages = this.getPages(); - let hasFoundCurrentPage = false; + for (let i = pages.length - 1; i > -1; i--) { const page = pages[i]; const isNotAvailable = !page.get('_isAvailable'); @@ -165,7 +154,6 @@ class PageNavModel extends ComponentModel { getNextPage() { const currentPage = this.getCurrentPage(); const currentPageId = currentPage.get('_id'); - const pages = this.getPages(); let hasFoundCurrentPage = false; @@ -187,7 +175,6 @@ class PageNavModel extends ComponentModel { getPages() { const loopStyle = this.get('_loopStyle'); - if (!loopStyle) return []; let loop = false; @@ -217,6 +204,24 @@ class PageNavModel extends ComponentModel { }); }; + getClose() { + return new Backbone.Model({ + _id: '', + _onClick: ` + try { + var scormWrapper = require('extensions/adapt-contrib-spoor/js/scorm/wrapper'); + if (scormWrapper) { + var scormWrapperInstance = scormWrapper.getInstance(); + if (scormWrapperInstance.lmsConnected && !scormWrapperInstance.finishCalled) { + scormWrapperInstance.finish(); + } + } + } catch (err) {} + top.window.close(); + ` + }); + }; + } export default PageNavModel; diff --git a/js/PageNavView.js b/js/PageNavView.js index 86bfeff..6938b8e 100644 --- a/js/PageNavView.js +++ b/js/PageNavView.js @@ -1,4 +1,5 @@ import Adapt from 'core/js/adapt'; +import router from 'core/js/router'; import ComponentView from 'core/js/views/componentView'; class PageNavView extends ComponentView { @@ -16,8 +17,6 @@ class PageNavView extends ComponentView { // }, preRender() { - this.model.set('_items', this.model.getNavigationData()); - this.$el.addClass('pagenav ' + this.model.get('_id')); _.bindAll(this, 'postRender', 'checkButtonStates'); @@ -167,10 +166,11 @@ class PageNavView extends ComponentView { // }; navigateTo(id) { - const isCourse = (id === Adapt.course.get('_id')); - const hash = '#' + (isCourse ? '/' : '/id/' + id); + // const isCourse = (id === Adapt.course.get('_id')); + // const hash = '#' + (isCourse ? '/' : '/id/' + id); - Backbone.history.navigate(hash, { trigger: true, replace: false }); + // Backbone.history.navigate(hash, { trigger: true, replace: false }); + router.navigateToElement(id); }; } diff --git a/templates/pageNav.jsx b/templates/pageNav.jsx index 22e6d80..05bf19c 100644 --- a/templates/pageNav.jsx +++ b/templates/pageNav.jsx @@ -19,7 +19,7 @@ export default function PageNav(props) {