diff --git a/README.md b/README.md index 55f1c2f..548a057 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,21 +13,19 @@ 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) +- `_root`: Navigates to the top level menu +- `_up`: Navigates to the menu that is the next level up in the hierarchy. For instance, a sub menu. +- `_previous`: Navigates to the previous page if it exists and is unlocked +- `_next`: Navigates to the next page if it exists and is unlocked +- `_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. +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. ### Attributes -[**core model attributes**](https://github.com/adaptlearning/adapt_framework/wiki/Core-model-attributes): These are inherited by every Adapt component. [Read more](https://github.com/adaptlearning/adapt_framework/wiki/Core-model-attributes). +[**core model attributes**](https://github.com/adaptlearning/adapt_framework/wiki/Core-model-attributes): These are inherited by every Adapt component. [Read more](https://github.com/adaptlearning/adapt_framework/wiki/Core-model-attributes) -**\_component** (string): This value must be: `pageNav`. (One word.) +**\_component** (string): This value must be `pageNav` (one word) **\_classes** (string): CSS class name to be applied to **PageNav**’s containing `div`. The class must be predefined in one of the Less files. Separate multiple classes with a space. @@ -35,43 +33,48 @@ The pageNav buttons will respect any [locking](https://github.com/adaptlearning/ **instruction** (string): This optional text appears above the component. It is frequently used to guide the learner’s interaction with the component. -**\_loopStyle** (string): Acceptable values are `allPages`, `siblings`, and `none`. `allPages` = loop sequentially through all pages in course. `siblings` = loop sequentially through all pages in current parent object. `none` = disable previous and next buttons at start and end of the pages in the current parent object. +**\_loopStyle** (string): Acceptable values are `allPages`, `siblings`, and `none`. Defaults to `none`. +- `allPages`: Loop sequentially through all pages in course +- `siblings`: Loop sequentially through all pages in current parent object +- `none`: Disable previous and next buttons at start and end of the pages in the current parent object. **\_shouldSkipOptionalPages** (boolean): Skip pages that are set to `"_isOptional": true`. Default is `false`. -**\_buttons** (object): The following attributes configure the defaults for the **Quickanv** buttons. These attributes are available on all of the following buttons **\_returnToPreviousLocation**, **\_previous**, **\_root**, **\_up**, **\_next**, **\_sibling**, and **\_close**. +**\_buttons** (object): The following attributes configure the defaults for the **Quickanv** buttons. These attributes are available on all of the following buttons **\_previous**, **\_root**, **\_up**, **\_next**, and **\_close**. #### Global button configurations >**\_isEnabled** (boolean): Turns the button on and off. Acceptable values are `true` and `false`. ->**\_lockUntilPageComplete** (boolean): For use when the standard Adapt locking system doesn't apply, such as in a start page before the main menu. Acceptable values are `true` and `false`. +>**\_lockUntilPageComplete** (boolean): For use when the standard Adapt locking system doesn't apply, such as in a start page before the main menu. Locks a button until the current page is complete. Acceptable values are `true` and `false`. Defaults to `false` >**\_order** (number): Defines the display order of the button. Numerical order with 0 rendering first. >**\_classes** (string): CSS class name to be applied to the `button`. The class must be predefined in one of the Less files. Separate multiple classes with a space. ->**\_iconClass** (string): CSS class name to be applied to the `button` icon. The class must be predefined in one of the Less files with the corresponding icon be added as part of a font. Suggested icons for each button detailed in the [_example.json_](https://github.com/cgkineo/adapt-pageNav/blob/master/example.json). List of all available [_vanilla_ icons](https://github.com/adaptlearning/adapt-contrib-vanilla/wiki/Icons) to choose from. +>**\_iconClass** (string): CSS class name to be applied to the `button` icon. The class must be predefined in one of the Less files with the corresponding icon included as part of a font. To have _no_ icon, leave this field blank. Suggested icons for each button are detailed in the [_example.json_](https://github.com/cgkineo/adapt-pageNav/blob/master/example.json). See the list of all available [_vanilla_ icons](https://github.com/adaptlearning/adapt-contrib-vanilla/wiki/Icons) to choose from. ->**\_alignIconright** (boolean): Defines whether the icon is aligned to the left or right of the text. Default is `false` which aligns the icon to the left of the text. +>**\_iconAlignment** (string): Determines how the icon is aligned to the text. Options include `auto`, `left`, `right`, `top`, and `bottom`. When using `auto`, the position will automatically adjust based on whether the user is using an LTR or RTL lanaguage. The `_next` button will also be adjusted accordingly when using `auto` (i.e. the icon will be right-aligned for LTR and left-aligned for RTL). Defaults to `auto`. >**text** (string): Defines the text that renders in the `button`. >**ariaLabel** (string): This text is associated with the button. It renders as part of the aria label to give screen readers more information. ->**\_showTooltip** (boolean): Defines whether the tooltip renders on hover. Default is `false`. +> **\_tooltip** (object): The tooltip object. Used when tooltips are enabled globally ->**tooltip** (string): Defines the text that renders in the tooltip. +>> **\_isEnabled** (boolean): Enables tooltips on the button -#### **\_previous** / **\_root** / **\_up** / **\_next** / **\_sibling** +>> **text** (string): The text of the tooltip + +#### **\_previous**, **\_root**, **\_up**, and **\_next** properties >**\_customRouteId** (string): Overrides the route ID. For use when non standard route navigation is required. ---------------------------- -**Framework versions:** 5.2+
+**Framework versions:** 5.30.2+
**Vanilla versions:** 5.1.1+
**Author / maintainer:** Kineo
**Accessibility support:** WAI AA
**RTL support:** Yes
-**Cross-platform coverage:** Chrome, Chrome for Android, Firefox (ESR + latest version), Edge, IE11, Safari 12+13 for macOS/iOS/iPadOS, Opera
+**Cross-platform coverage:** Chrome, Chrome for Android, Firefox (ESR + latest version), Edge, Safari for macOS/iOS/iPadOS, Opera
diff --git a/bower.json b/bower.json index ebd165b..1e1b58f 100644 --- a/bower.json +++ b/bower.json @@ -5,7 +5,7 @@ "url": "git://github.com/cgkineo/adapt-pageNav.git" }, "version": "2.4.0", - "framework": ">=5.2", + "framework": ">=5.30.2", "homepage": "https://github.com/cgkineo/adapt-pageNav", "issues": "https://github.com/cgkineo/adapt-pageNav/issues/", "component": "pageNav", diff --git a/demo.fw.png b/demo.fw.png deleted file mode 100644 index 942d901..0000000 Binary files a/demo.fw.png and /dev/null differ diff --git a/example.json b/example.json index b1ffe72..ed83f50 100644 --- a/example.json +++ b/example.json @@ -1,118 +1,97 @@ - // To go in the component.json file for each page - // -------------------------------------------------- - { - "_id": "c-05", - "_parentId": "b-05", - "_type": "component", - "_component": "pageNav", - "_classes": "", - "_layout": "full", - "title": "", - "displayTitle": "", - "body": "", - "instruction": "", - "_loopStyleComment": { - "allPages": "loop sequentially through all pages in course", - "siblings": "loop sequentially through all pages in current parent object", - "none": "disable previous and next buttons at start and end of the pages in the current parent object" - }, - "_loopStyle": "none", - "_shouldSkipOptionalPages": false, - "_buttons": { - "_returnToPreviousLocation": { - "_isEnabled": false, - "_lockUntilPageComplete": false, - "_order": 1, - "_classes": "", - "_iconClassComment": "Suggested icon = 'icon-controls-left'", - "_iconClass": "", - "_alignIconRight": false, - "text": "Return", - "ariaLabel": "Return to previous location", - "_showTooltip": false, - "tooltip": "{{displayTitle}}" - }, - "_previous": { +// To go in the component.json file for each page +// -------------------------------------------------- +{ + "_id": "c-05", + "_parentId": "b-05", + "_type": "component", + "_component": "pageNav", + "_classes": "", + "_layout": "full", + "title": "", + "displayTitle": "", + "body": "", + "instruction": "", + "_loopStyleComment": { + "allPages": "loop sequentially through all pages in course", + "siblings": "loop sequentially through all pages in current parent object", + "none": "disable previous and next buttons at start and end of the pages in the current parent object" + }, + "_loopStyle": "none", + "_shouldSkipOptionalPages": false, + "_buttons": { + "_root": { + "_isEnabled": true, + "_lockUntilPageComplete": false, + "_order": 1, + "_classes": "", + "_iconClass": "icon-home", + "_iconAlignment": "auto", + "text": "Go to main menu", + "ariaLabel": "Go to main menu", + "_tooltip": { "_isEnabled": true, - "_lockUntilPageComplete": false, - "_order": 1, - "_classes": "", - "_iconClassComment": "Suggested icon = 'icon-controls-left'", - "_iconClass": "", - "_alignIconRight": false, - "text": "Previous", - "ariaLabel": "Previous Page", - "_showTooltip": false, - "tooltip": "{{displayTitle}}", - "_customRouteId": "" + "text": "{{displayTitle}}" }, - "_root": { + "_customRouteId": "" + }, + "_up": { + "_isEnabled": false, + "_lockUntilPageComplete": false, + "_order": 1, + "_classes": "", + "_iconClass": "icon-controls-up", + "_iconAlignment": "auto", + "text": "Back to menu", + "ariaLabel": "Back to menu", + "_tooltip": { "_isEnabled": true, - "_lockUntilPageComplete": false, - "_order": 1, - "_classes": "", - "_iconClassComment": "Suggested icon = 'icon-home'", - "_iconClass": "", - "_alignIconRight": false, - "text": "Go to main menu", - "ariaLabel": "Go to main menu", - "_showTooltip": false, - "tooltip": "{{displayTitle}}", - "_customRouteId": "" + "text": "{{displayTitle}}" }, - "_up": { - "_isEnabled": false, - "_lockUntilPageComplete": false, - "_order": 1, - "_classes": "", - "_iconClassComment": "Suggested icon = 'icon-controls-up'", - "_iconClass": "", - "_alignIconRight": false, - "text": "Back to menu", - "ariaLabel": "Back to menu", - "_showTooltip": false, - "tooltip": "{{displayTitle}}", - "_customRouteId": "" - }, - "_next": { + "_customRouteId": "" + }, + "_previous": { + "_isEnabled": true, + "_lockUntilPageComplete": false, + "_order": 1, + "_classes": "", + "_iconClass": "icon-controls-left", + "_iconAlignment": "auto", + "text": "Previous", + "ariaLabel": "Previous Page", + "_tooltip": { "_isEnabled": true, - "_lockUntilPageComplete": false, - "_order": 1, - "_classes": "", - "_iconClassComment": "Suggested icon = 'icon-controls-right'", - "_iconClass": "", - "_alignIconRight": true, - "text": "Next", - "ariaLabel": "Next Page", - "_showTooltip": false, - "tooltip": "{{displayTitle}}", - "_customRouteId": "" + "text": "{{displayTitle}}" }, - "_sibling": { - "_isEnabled": false, - "_lockUntilPageComplete": false, - "_order": 1, - "_classes": "", - "_iconClass": "", - "_alignIconRight": false, - "text": "{{inc index}}", - "ariaLabel": "Page {{inc index}}", - "_showTooltip": false, - "tooltip": "{{displayTitle}}", - "_customRouteId": "" + "_customRouteId": "" + }, + "_next": { + "_isEnabled": true, + "_lockUntilPageComplete": false, + "_order": 1, + "_classes": "", + "_iconClass": "icon-controls-right", + "_iconAlignment": "auto", + "text": "Next", + "ariaLabel": "Next Page", + "_tooltip": { + "_isEnabled": true, + "text": "{{displayTitle}}" }, - "_close": { - "_isEnabled": false, - "_lockUntilPageComplete": false, - "_order": 1, - "_classes": "", - "_iconClassComment": "Suggested icon = 'icon-cross'", - "_iconClass": "", - "_alignIconRight": false, - "text": "Close", - "ariaLabel": "Close window", - "_showTooltip": false, - "tooltip": "Close window" + "_customRouteId": "" + }, + "_close": { + "_isEnabled": false, + "_lockUntilPageComplete": false, + "_order": 1, + "_classes": "", + "_iconClass": "icon-cross", + "_iconAlignment": "auto", + "text": "Close", + "ariaLabel": "Close window", + "_tooltip": { + "_isEnabled": true, + "text": "Close window" } } } +} diff --git a/js/PageNavModel.js b/js/PageNavModel.js new file mode 100644 index 0000000..fac2b1e --- /dev/null +++ b/js/PageNavModel.js @@ -0,0 +1,164 @@ +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 { + init() { + this.listenTo(Adapt, 'router:location', this.setupItemsModel); + + super.init(); + }; + + setupItemsModel() { + this.set('_items', this.getNavigationData()); + } + + /** + * Combines the config, model, order, index and type for each _buttons item + * + * @returns {array} An array of combined button items + */ + getNavigationData() { + const buttons = this.get('_buttons'); + if (!buttons) return []; + + const buttonTypeModels = this.getButtonTypeModels(); + const currentPageComplete = buttonTypeModels._page.get('_isComplete'); + const unsortedItems = []; + let order = 0; + let item; + + for (const type in buttons) { + const buttonConfig = buttons[type]; + + // Skip the button if not enabled + if (!buttonConfig._isEnabled) continue; + + // Get models, skipping any undefined types (ex. deprecated button types) + let buttonModel = buttonTypeModels[type]; + if (!buttonModel) 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 }; + + Object.assign(item, buttonConfig, { + type, + index: 0, + order: order++, + _tooltipId: `pagenav_btn${type}`, + locked: item._isLocked || (buttonConfig._lockUntilPageComplete && !currentPageComplete) + }); + unsortedItems.push(item); + } + + // requires a stable sorting algorithm - native sorting in Chrome is unstable (should be stable from Chrome 70) + const sortedItems = _.sortBy(unsortedItems, '_order'); + + sortedItems.forEach((item, index) => { + item._index = index; + }); + + return sortedItems; + }; + + getButtonTypeModels() { + return { + _page: this.getCurrentPage(), + _up: this.getCurrentMenu(), + _root: Adapt.course, + _next: this.getNextPage(), + _previous: this.getPrevPage(), + _close: this.getClose() + }; + } + + getCurrentPage() { + return location._currentModel; + }; + + getCurrentMenu() { + return this.findAncestor('menu'); + }; + + getPrevPage() { + const currentPage = this.getCurrentPage(); + const currentPageId = currentPage.get('_id'); + const pages = this.getPages(); + let hasFoundCurrentPage = false; + + for (const page of pages.reverse()) { + 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 (const page of pages) { + 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; + default: + currentMenu = this.getCurrentMenu(); + descendants = currentMenu.getAllDescendantModels(true); + } + + if (loop) { + // Create a double copy to allow loop searching + descendants = descendants.concat(descendants); + } + + return descendants.filter(model => { + return model.get('_type') === 'page'; + }); + }; + + getClose() { + return new Backbone.Model({ _id: '' }); + }; +} + +export default PageNavModel; diff --git a/js/PageNavView.js b/js/PageNavView.js new file mode 100644 index 0000000..32d6574 --- /dev/null +++ b/js/PageNavView.js @@ -0,0 +1,88 @@ +import Adapt from 'core/js/adapt'; +import router from 'core/js/router'; +import ComponentView from 'core/js/views/componentView'; +import tooltips from 'core/js/tooltips'; +import logging from 'core/js/logging'; + +class PageNavView extends ComponentView { + initialize() { + _.bindAll(this, 'postRender', 'onButtonClick'); + + super.initialize(); + } + + preRender() { + this.listenTo(Adapt, 'remove', this.remove); + this.listenTo(Adapt.contentObjects, { + 'change:_isComplete change:_isLocked': this.onContentObjectComplete + }); + }; + + postRender() { + this.setReadyStatus(); + this.setupInviewCompletion(); + this.setupTooltips(); + }; + + onContentObjectComplete() { + // Update model so that _lockUntilPageComplete works properly + this.model.setupItemsModel(); + }; + + onButtonClick(event) { + const $target = $(event.currentTarget); + const index = $target.data('item-index'); + const item = this.model.get('_items')[index]; + const type = item.type; + + // Close button + if (type === '_close') { + this.closeWindow(); + return; + } + + // Check if locked + const isLocked = item._isHidden || item._isLocked; + if (isLocked) return; + + this.navigateTo(item._id); + }; + + closeWindow() { + try { + const scormWrapper = require('extensions/adapt-contrib-spoor/js/scorm/wrapper'); + if (scormWrapper) { + const scormWrapperInstance = scormWrapper.getInstance(); + if (scormWrapperInstance.lmsConnected && !scormWrapperInstance.finishCalled) { + scormWrapperInstance.finish(); + } + } + } catch (err) { + logging.warn(`Could not close window. Error: ${err}`); + } + top.window.close(); + }; + + setupTooltips() { + const items = this.model.get('_items'); + items.forEach(item => { + if (!item._tooltip || item._isHidden) return; + + tooltips.register({ + _id: item._tooltipId, + _isEnabled: item._tooltip._isEnabled, + text: Handlebars.compile(item._tooltip.text)(item) + }); + }); + }; + + navigateTo(id) { + router.navigateToElement(id); + }; + + static get template() { + return '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/tooltip.js b/js/tooltip.js deleted file mode 100644 index 5af7cf4..0000000 --- a/js/tooltip.js +++ /dev/null @@ -1,114 +0,0 @@ -/* eslint-disable no-var */ -define(["core/js/adapt"], function (Adapt) { - var Tooltip = Backbone.View.extend({ - className: "collab__tooltip", - - initialize: function (options) { - this.removeOtherTooltips(); - this.setupEventListeners(); - this.setupData(options); - - this.render(); - }, - - removeOtherTooltips: function () { - Adapt.trigger("tooltip:remove"); - Adapt.tooltip = this; - }, - - setupEventListeners: function () { - _.bindAll(this, "remove", "postRender", "show"); - this.show = _.debounce(this.show, 17); - - $(document).on("mouseover", this.remove); - - this.listenTo(Adapt, { - "device:resize": this.checkPosition, - "tooltip:remove": this.remove, - remove: this.remove - }); - }, - - setupData: function (options) { - this.$target = options.$target; - this.id = this.$target.attr("data-id"); - this.type = this.$target.attr("data-type"); - this.index = this.$target.attr("data-index"); - - this.model.set("tooltip", this.$target.attr("data-tooltip")); - }, - - render: function () { - var template = Handlebars.templates["pageNav-tooltip"]; - - this.$el.html(template(this.model.toJSON())); - _.defer(this.postRender); - }, - - postRender: function () { - if (this.isRemoved) return; - - this.checkPosition(); - this.show(); - }, - - checkPosition: function () { - if (this.isRemoved) return; - - var buttonPosition = this.$target.position(); - var buttonWidth = this.$target.outerWidth(true); - var buttonHalfWidth = buttonWidth / 2; - var buttonCenterLeft = buttonPosition.left + buttonHalfWidth; - - var tooltipWidth = this.$el.outerWidth(true); - var tooltipHalfWidth = tooltipWidth / 2; - - var tooltipCenter = { - top: buttonPosition.top, - left: buttonCenterLeft - }; - - var $offsetParent = this.$el.offsetParent(); - var parentWidth = $offsetParent.innerWidth(); - - var overflowRight = parentWidth - (tooltipCenter.left + tooltipHalfWidth); - var overflowLeft = tooltipCenter.left - tooltipHalfWidth; - - var isOverflowingRight = overflowRight < 0; - var isOverflowingLeft = overflowLeft < 0; - - var leftOffset = isOverflowingRight - ? overflowRight - : isOverflowingLeft - ? -overflowLeft - : 0; - - this.$el.css({ - top: tooltipCenter.top, - left: tooltipCenter.left + leftOffset - }); - - this.$el.find(".pagenav__triangle").css({ - left: tooltipHalfWidth - leftOffset - }); - }, - - show: function () { - if (this.isRemoved) return; - - this.$el.addClass("show"); - }, - - remove: function () { - this.isRemoved = true; - - delete this.$target; - delete Adapt.tooltip; - - $(document).off("mouseover", this.onBodyMouseOver); - Backbone.View.prototype.remove.call(this); - } - }); - - return Tooltip; -}); 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/less/pageNav.less b/less/pageNav.less index 67260e8..d4c719c 100644 --- a/less/pageNav.less +++ b/less/pageNav.less @@ -1,10 +1,3 @@ -html { - // Prevents pagenav tooltip from triggering a horizontal scroll bar - // Only affects IE11 but applied globally to weed out bugs - // -------------------------------------------------- - overflow-x: hidden; -} - .pagenav { &__widget { position: relative; @@ -25,49 +18,103 @@ html { // -------------------------------------------------- // THEME // -------------------------------------------------- -.pagenav { - &__btn { - margin: @item-margin; +.pagenav__btn { + margin: @item-margin; - &[aria-current="page"] { - background-color: darken(@btn-color, 20%); - } - } - - &__btn-icon .icon { + &-icon .icon { display: block; } +} - // buttons with text and icon (left aligned icon as default) +// Buttons with text and icon +// -------------------------------------------------- +.pagenav__btn.btn-icon.btn-text { + + // Left aligned icon (icon before the text) // -------------------------------------------------- - &__btn.btn-icon:not(.icon-is-right).btn-text .icon { - padding-inline-end: (@icon-padding / 2); + &.has-icon-left, + .dir-ltr &.has-icon-auto, + .dir-rtl &.has-icon-auto[data-type="_next"] { + .icon { + padding-left: 0; + padding-right: (@icon-padding / 2); + } + } + .dir-rtl &.has-icon-left { + .icon { + padding-right: 0; + padding-left: (@icon-padding / 2); + } + } + .dir-rtl &.has-icon-auto[data-type="_next"] { + .pagenav__btn-inner { + flex-direction: row-reverse; + } } - // buttons with text and icon (right aligned icon) + // Right aligned icon (icon after the text) // -------------------------------------------------- - &__btn.btn-icon.icon-is-right.btn-text .icon { - padding-inline-start: (@icon-padding / 2); + &.has-icon-right, + .dir-rtl &.has-icon-auto, + .dir-ltr &.has-icon-auto[data-type="_next"] { + .icon { + padding-right: 0; + padding-left: (@icon-padding / 2); + } + } + .dir-rtl &.has-icon-right { + .icon { + padding-left: 0; + padding-right: (@icon-padding / 2); + } + } + &.has-icon-right, + .dir-ltr &.has-icon-auto[data-type="_next"] { + .pagenav__btn-inner { + flex-direction: row-reverse; + } } - // consistent with nav icon button styling + // Top aligned icon // -------------------------------------------------- - &__btn.btn-icon:not(.btn-text) { - padding: @icon-padding; - background-color: @btn-icon-color; - color: @btn-icon-color-inverted; - border-radius: @btn-border-radius; - - .no-touch &:not(.is-disabled):not(.is-locked):hover { - background-color: @btn-icon-color-hover; - color: @btn-icon-color-inverted-hover; - .transition(background-color @duration ease-in, color @duration ease-in;); + &.has-icon-top { + .pagenav__btn-inner { + flex-direction: column; + } + .icon { + padding-bottom: (@icon-padding / 4); } + } - &.is-locked, - &.is-disabled { - background-color: @disabled; - color: @disabled-inverted; + // Bottom aligned icon + // -------------------------------------------------- + &.has-icon-bottom { + .pagenav__btn-inner { + flex-direction: column-reverse; } + .icon { + padding-top: (@icon-padding / 4); + } + } +} + +// Consistent with nav icon button styling +// -------------------------------------------------- +.pagenav__btn.btn-icon:not(.btn-text) { + padding: @icon-padding; + background-color: @btn-icon-color; + color: @btn-icon-color-inverted; + border-radius: @btn-border-radius; + + .no-touch &:not(.is-disabled):not(.is-locked):hover { + background-color: @btn-icon-color-hover; + color: @btn-icon-color-inverted-hover; + .transition(background-color @duration ease-in, color @duration ease-in;); + } + + &.is-locked, + &.is-disabled { + background-color: @disabled; + color: @disabled-inverted; } } diff --git a/less/tooltip.less b/less/tooltip.less deleted file mode 100644 index 83aad9d..0000000 --- a/less/tooltip.less +++ /dev/null @@ -1,43 +0,0 @@ -.pagenav { - &__tooltip { - position: absolute; - .transform(translate(-50%, -100%)); - visibility: hidden; - white-space: nowrap; - } - - &__tooltip.show { - visibility: visible; - } - - &__triangle { - position: relative; - .transform(translateX(-50%)); - width: @item-padding; - height: @item-padding; - } - - &__triangle:after { - content: ""; - position: absolute; - border-width: @item-padding (@item-padding / 2) 0 (@item-padding / 2); /* vary these values to change the angle of the vertex */ - border-style: solid; - border-color: red transparent transparent transparent; - } -} - -// -------------------------------------------------- -// THEME -// -------------------------------------------------- -.pagenav { - &__tooltip-text { - padding: @item-padding / 2; - background-color: @item-color; - color: @item-color-inverted; - border-radius: @item-border-radius; - } - - &__triangle:after { - border-color: @item-color transparent transparent transparent; - } -} diff --git a/package.json b/package.json index ebd165b..1e1b58f 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "url": "git://github.com/cgkineo/adapt-pageNav.git" }, "version": "2.4.0", - "framework": ">=5.2", + "framework": ">=5.30.2", "homepage": "https://github.com/cgkineo/adapt-pageNav", "issues": "https://github.com/cgkineo/adapt-pageNav/issues/", "component": "pageNav", diff --git a/properties.schema b/properties.schema index 28f886d..70933c1 100644 --- a/properties.schema +++ b/properties.schema @@ -57,97 +57,6 @@ "required": true, "title": "Buttons settings", "properties": { - "_returnToPreviousLocation": { - "type": "object", - "required": true, - "legend": "Return to previous location button", - "properties": { - "_isEnabled": { - "type": "boolean", - "required": true, - "title": "Show", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "_lockUntilPageComplete": { - "type": "boolean", - "required": true, - "title": "Lock until page complete", - "default": false, - "inputType": "Checkbox", - "validators": [], - "help": "For use when the standard Adapt locking system doesn't apply, such as in a start page before the main menu" - }, - "_order": { - "type": "number", - "required": true, - "default": 0, - "title": "Order", - "inputType": "Number", - "validators": ["required", "number"], - "help": "Button display order" - }, - "_classes": { - "type": "string", - "required": false, - "title": "Classes", - "default": "", - "inputType": "Text", - "validators": [] - }, - "_iconClass": { - "type": "string", - "required": false, - "title": "Icon class", - "default": "", - "inputType": "Text", - "validators": [] - }, - "_alignIconRight": { - "type": "boolean", - "required": true, - "title": "Align icon right", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "text": { - "type": "string", - "required": true, - "default": "Return", - "inputType": "Text", - "validators": [], - "translatable": true - }, - "ariaLabel": { - "type": "string", - "required": true, - "title": "ARIA label", - "default": "Return to previous location", - "inputType": "Text", - "validators": [], - "translatable": true - }, - "_showTooltip": { - "type": "boolean", - "required": true, - "title": "Show tool tip", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "tooltip": { - "type": "string", - "required": false, - "title": "Tool tip", - "default": "{{displayTitle}}", - "inputType": "Text", - "validators": [], - "translatable": true - } - } - }, "_previous": { "type": "object", "required": true, @@ -191,17 +100,18 @@ "type": "string", "required": false, "title": "Icon class", - "default": "", + "default": "icon-controls-left", + "help": "CSS class name to be applied to the button icon. Suggested: icon-controls-left", "inputType": "Text", "validators": [] }, - "_alignIconRight": { - "type": "boolean", - "required": true, - "title": "Align icon right", - "default": false, - "inputType": "Checkbox", - "validators": [] + "_iconAlignment": { + "type": "string", + "required": false, + "default": "auto", + "inputType": {"type":"Select", "options":["auto","left","right","top","bottom"]}, + "title": "Icon alignment", + "help": "Determines how the icon is aligned to the text." }, "text": { "type": "string", @@ -220,22 +130,27 @@ "validators": [], "translatable": true }, - "_showTooltip": { - "type": "boolean", - "required": true, - "title": "Show tool tip", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "tooltip": { - "type": "string", - "required": false, - "title": "Tool tip", - "default": "{{displayTitle}}", - "inputType": "Text", - "validators": [], - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Navigation tooltip", + "properties": { + "_isEnabled": { + "type": "boolean", + "default": true, + "title": "Enable tooltip for navigation button", + "inputType": "Checkbox", + "validators": [] + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "help": "The tooltip text to display on hover over this item", + "inputType": "Text", + "validators": [], + "translatable": true + } + } }, "_customRouteId": { "type": "string", @@ -290,17 +205,18 @@ "type": "string", "required": false, "title": "Icon class", - "default": "", + "default": "icon-home", + "help": "CSS class name to be applied to the button icon. Suggested: icon-home", "inputType": "Text", "validators": [] }, - "_alignIconRight": { - "type": "boolean", - "required": true, - "title": "Align icon right", - "default": false, - "inputType": "Checkbox", - "validators": [] + "_iconAlignment": { + "type": "string", + "required": false, + "default": "auto", + "inputType": {"type":"Select", "options":["auto","left","right","top","bottom"]}, + "title": "Icon alignment", + "help": "Determines how the icon is aligned to the text." }, "text": { "type": "string", @@ -319,22 +235,27 @@ "validators": [], "translatable": true }, - "_showTooltip": { - "type": "boolean", - "required": true, - "title": "Show tool tip", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "tooltip": { - "type": "string", - "required": false, - "title": "Tool tip", - "default": "{{displayTitle}}", - "inputType": "Text", - "validators": [], - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Navigation tooltip", + "properties": { + "_isEnabled": { + "type": "boolean", + "default": true, + "title": "Enable tooltip for navigation button", + "inputType": "Checkbox", + "validators": [] + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "help": "The tooltip text to display on hover over this item", + "inputType": "Text", + "validators": [], + "translatable": true + } + } }, "_customRouteId": { "type": "string", @@ -389,17 +310,18 @@ "type": "string", "required": false, "title": "Icon class", - "default": "", + "default": "icon-controls-up", + "help": "CSS class name to be applied to the button icon. Suggested: icon-controls-up", "inputType": "Text", "validators": [] }, - "_alignIconRight": { - "type": "boolean", - "required": true, - "title": "Align icon right", - "default": false, - "inputType": "Checkbox", - "validators": [] + "_iconAlignment": { + "type": "string", + "required": false, + "default": "auto", + "inputType": {"type":"Select", "options":["auto","left","right","top","bottom"]}, + "title": "Icon alignment", + "help": "Determines how the icon is aligned to the text." }, "text": { "type": "string", @@ -418,22 +340,27 @@ "validators": [], "translatable": true }, - "_showTooltip": { - "type": "boolean", - "required": true, - "title": "Show tool tip", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "tooltip": { - "type": "string", - "required": false, - "title": "Tool tip", - "default": "{{displayTitle}}", - "inputType": "Text", - "validators": [], - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Navigation tooltip", + "properties": { + "_isEnabled": { + "type": "boolean", + "default": true, + "title": "Enable tooltip for navigation button", + "inputType": "Checkbox", + "validators": [] + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "help": "The tooltip text to display on hover over this item", + "inputType": "Text", + "validators": [], + "translatable": true + } + } }, "_customRouteId": { "type": "string", @@ -488,121 +415,23 @@ "type": "string", "required": false, "title": "Icon class", - "default": "", + "default": "icon-controls-right", + "help": "CSS class name to be applied to the button icon. Suggested: icon-controls-right", "inputType": "Text", "validators": [] }, - "_alignIconRight": { - "type": "boolean", - "required": true, - "title": "Align icon right", - "default": true, - "inputType": "Checkbox", - "validators": [] - }, - "text": { - "type": "string", - "required": true, - "default": "Next", - "inputType": "Text", - "validators": [], - "translatable": true - }, - "ariaLabel": { - "type": "string", - "required": true, - "title": "ARIA label", - "default": "Next Page", - "inputType": "Text", - "validators": [], - "translatable": true - }, - "_showTooltip": { - "type": "boolean", - "required": true, - "title": "Show tool tip", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "tooltip": { + "_iconAlignment": { "type": "string", "required": false, - "title": "Tool tip", - "default": "{{displayTitle}}", - "inputType": "Text", - "validators": [], - "translatable": true - }, - "_customRouteId": { - "type": "string", - "required": true, - "title": "Override the route id", - "default": "", - "inputType": "Text", - "validators": [] - } - } - }, - "_sibling": { - "type": "object", - "required": true, - "legend": "Sibling buttons", - "properties": { - "_isEnabled": { - "type": "boolean", - "required": true, - "title": "Show", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "_lockUntilPageComplete": { - "type": "boolean", - "required": true, - "title": "Lock until page complete", - "default": false, - "inputType": "Checkbox", - "validators": [], - "help": "For use when the standard Adapt locking system doesn't apply, such as in a start page before the main menu" - }, - "_order": { - "type": "number", - "required": true, - "default": 0, - "title": "Order", - "inputType": "Number", - "validators": ["required", "number"], - "help": "Button display order" - }, - "_classes": { - "type": "string", - "required": false, - "title": "Classes", - "default": "", - "inputType": "Text", - "validators": [] - }, - "_iconClass": { - "type": "string", - "required": false, - "title": "Icon class", - "default": "", - "inputType": "Text", - "validators": [] - }, - "_alignIconRight": { - "type": "boolean", - "required": true, - "title": "Align icon right", - "default": false, - "inputType": "Checkbox", - "validators": [] + "default": "auto", + "inputType": {"type":"Select", "options":["auto","left","right","top","bottom"]}, + "title": "Icon alignment", + "help": "Determines how the icon is aligned to the text." }, "text": { "type": "string", "required": true, - "default": "{{inc index}}", + "default": "Next", "inputType": "Text", "validators": [], "translatable": true @@ -611,27 +440,32 @@ "type": "string", "required": true, "title": "ARIA label", - "default": "Page {{inc index}}", + "default": "Next Page", "inputType": "Text", "validators": [], "translatable": true }, - "_showTooltip": { - "type": "boolean", - "required": true, - "title": "Show tool tip", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "tooltip": { - "type": "string", - "required": false, - "title": "Tool tip", - "default": "{{displayTitle}}", - "inputType": "Text", - "validators": [], - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Navigation tooltip", + "properties": { + "_isEnabled": { + "type": "boolean", + "default": true, + "title": "Enable tooltip for navigation button", + "inputType": "Checkbox", + "validators": [] + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "help": "The tooltip text to display on hover over this item", + "inputType": "Text", + "validators": [], + "translatable": true + } + } }, "_customRouteId": { "type": "string", @@ -686,17 +520,18 @@ "type": "string", "required": false, "title": "Icon class", - "default": "", + "default": "icon-cross", + "help": "CSS class name to be applied to the button icon. Suggested: icon-cross", "inputType": "Text", "validators": [] }, - "_alignIconRight": { - "type": "boolean", - "required": true, - "title": "Align icon right", - "default": false, - "inputType": "Checkbox", - "validators": [] + "_iconAlignment": { + "type": "string", + "required": false, + "default": "auto", + "inputType": {"type":"Select", "options":["auto","left","right","top","bottom"]}, + "title": "Icon alignment", + "help": "Determines how the icon is aligned to the text." }, "text": { "type": "string", @@ -715,22 +550,27 @@ "validators": [], "translatable": true }, - "_showTooltip": { - "type": "boolean", - "required": true, - "title": "Show tool tip", - "default": false, - "inputType": "Checkbox", - "validators": [] - }, - "tooltip": { - "type": "string", - "required": false, - "title": "Tool tip", - "default": "Close window", - "inputType": "Text", - "validators": [], - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Navigation tooltip", + "properties": { + "_isEnabled": { + "type": "boolean", + "default": true, + "title": "Enable tooltip for navigation button", + "inputType": "Checkbox", + "validators": [] + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "help": "The tooltip text to display on hover over this item", + "inputType": "Text", + "validators": [], + "translatable": true + } + } } } } diff --git a/schema/component.schema.json b/schema/component.schema.json index 28d7d04..e7df35d 100644 --- a/schema/component.schema.json +++ b/schema/component.schema.json @@ -49,86 +49,13 @@ "type": "object", "title": "Buttons settings", "required": [ - "_returnToPreviousLocation", "_previous", "_root", "_up", "_next", - "_sibling", "_close" ], "properties": { - "_returnToPreviousLocation": { - "type": "object", - "title": "Return to previous location button", - "properties": { - "_isEnabled": { - "type": "boolean", - "title": "Show", - "default": false - }, - "_lockUntilPageComplete": { - "type": "boolean", - "title": "Lock until page complete", - "description": "For use when the standard Adapt locking system doesn't apply, such as in a start page before the main menu", - "default": false - }, - "_order": { - "type": "number", - "title": "Order", - "description": "Button display order", - "default": 0 - }, - "_classes": { - "type": "string", - "title": "Classes", - "default": "" - }, - "_iconClass": { - "type": "string", - "title": "Icon class", - "description": "CSS class name to be applied to the button icon. Suggested: icon-controls-left", - "default": "" - }, - "_alignIconRight": { - "type": "boolean", - "title": "Align icon right", - "description": "Defines whether the icon is aligned to the left or right of the text.", - "default": false - }, - "text": { - "type": "string", - "title": "Button text", - "description": "Text that appears on the button. Optional since you can only use an icon.", - "default": "Return", - "_adapt": { - "translatable": true - } - }, - "ariaLabel": { - "type": "string", - "title": "ARIA label", - "default": "Return to previous location", - "_adapt": { - "translatable": true - } - }, - "_showTooltip": { - "type": "boolean", - "title": "Show tool tip", - "description": "Defines whether the tooltip renders on hover.", - "default": false - }, - "tooltip": { - "type": "string", - "title": "Tool tip", - "default": "{{displayTitle}}", - "_adapt": { - "translatable": true - } - } - } - }, "_previous": { "type": "object", "title": "Previous button", @@ -159,13 +86,21 @@ "type": "string", "title": "Icon class", "description": "CSS class name to be applied to the button icon. Suggested: icon-controls-left", - "default": "" - }, - "_alignIconRight": { - "type": "boolean", - "title": "Align icon right", - "description": "Defines whether the icon is aligned to the left or right of the text.", - "default": false + "default": "icon-controls-left" + }, + "_iconAlignment": { + "type": "string", + "title": "Icon alignment", + "description": "Determines how the icon is aligned to the text.", + "default": "auto", + "enum": [ + "auto", + "left", + "right", + "top", + "bottom" + ], + "_backboneForms": "Select" }, "text": { "type": "string", @@ -184,18 +119,24 @@ "translatable": true } }, - "_showTooltip": { - "type": "boolean", - "title": "Show tool tip", - "description": "Defines whether the tooltip renders on hover.", - "default": false - }, - "tooltip": { - "type": "string", - "title": "Tool tip", - "default": "{{displayTitle}}", - "_adapt": { - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Button tooltip", + "default": {}, + "properties": { + "_isEnabled": { + "type": "boolean", + "title": "Enable tooltip for this button", + "default": true + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "_adapt": { + "translatable": true + } + } } }, "_customRouteId": { @@ -237,13 +178,21 @@ "type": "string", "title": "Icon class", "description": "CSS class name to be applied to the button icon. Suggested: icon-home", - "default": "" - }, - "_alignIconRight": { - "type": "boolean", - "title": "Align icon right", - "description": "Defines whether the icon is aligned to the left or right of the text.", - "default": false + "default": "icon-home" + }, + "_iconAlignment": { + "type": "string", + "title": "Icon alignment", + "description": "Determines how the icon is aligned to the text.", + "default": "auto", + "enum": [ + "auto", + "left", + "right", + "top", + "bottom" + ], + "_backboneForms": "Select" }, "text": { "type": "string", @@ -262,18 +211,24 @@ "translatable": true } }, - "_showTooltip": { - "type": "boolean", - "title": "Show tool tip", - "description": "Defines whether the tooltip renders on hover.", - "default": false - }, - "tooltip": { - "type": "string", - "title": "Tool tip", - "default": "{{displayTitle}}", - "_adapt": { - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Button tooltip", + "default": {}, + "properties": { + "_isEnabled": { + "type": "boolean", + "title": "Enable tooltip for this button", + "default": true + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "_adapt": { + "translatable": true + } + } } }, "_customRouteId": { @@ -315,13 +270,21 @@ "type": "string", "title": "Icon class", "description": "CSS class name to be applied to the button icon. Suggested: icon-controls-up", - "default": "" - }, - "_alignIconRight": { - "type": "boolean", - "title": "Align icon right", - "description": "Defines whether the icon is aligned to the left or right of the text.", - "default": false + "default": "icon-controls-up" + }, + "_iconAlignment": { + "type": "string", + "title": "Icon alignment", + "description": "Determines how the icon is aligned to the text.", + "default": "auto", + "enum": [ + "auto", + "left", + "right", + "top", + "bottom" + ], + "_backboneForms": "Select" }, "text": { "type": "string", @@ -340,18 +303,24 @@ "translatable": true } }, - "_showTooltip": { - "type": "boolean", - "title": "Show tool tip", - "description": "Defines whether the tooltip renders on hover.", - "default": false - }, - "tooltip": { - "type": "string", - "title": "Tool tip", - "default": "{{displayTitle}}", - "_adapt": { - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Button tooltip", + "default": {}, + "properties": { + "_isEnabled": { + "type": "boolean", + "title": "Enable tooltip for this button", + "default": true + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "_adapt": { + "translatable": true + } + } } }, "_customRouteId": { @@ -392,13 +361,21 @@ "type": "string", "title": "Icon class", "description": "CSS class name to be applied to the button icon. Suggested: icon-controls-right", - "default": "" - }, - "_alignIconRight": { - "type": "boolean", - "title": "Align icon right", - "description": "Defines whether the icon is aligned to the left or right of the text.", - "default": true + "default": "icon-controls-right" + }, + "_iconAlignment": { + "type": "string", + "title": "Icon alignment", + "description": "Determines how the icon is aligned to the text.", + "default": "auto", + "enum": [ + "auto", + "left", + "right", + "top", + "bottom" + ], + "_backboneForms": "Select" }, "text": { "type": "string", @@ -417,96 +394,24 @@ "translatable": true } }, - "_showTooltip": { - "type": "boolean", - "title": "Show tool tip", - "description": "Defines whether the tooltip renders on hover.", - "default": false - }, - "tooltip": { - "type": "string", - "title": "Tool tip", - "default": "{{displayTitle}}", - "_adapt": { - "translatable": true - } - }, - "_customRouteId": { - "type": "string", - "title": "Override the route id", - "description": "Enter the Friendly id of the page that the button should direct the user to. For use when non-standard navigation is required.", - "default": "" - } - } - }, - "_sibling": { - "type": "object", - "title": "Sibling buttons", - "description": "Numbered buttons representing each sibling page", - "properties": { - "_isEnabled": { - "type": "boolean", - "title": "Show", - "default": false - }, - "_lockUntilPageComplete": { - "type": "boolean", - "title": "Lock until page complete", - "description": "For use when the standard Adapt locking system doesn't apply, such as in a start page before the main menu", - "default": false - }, - "_order": { - "type": "number", - "title": "Order", - "description": "Button display order", - "default": 0 - }, - "_classes": { - "type": "string", - "title": "Classes", - "default": "" - }, - "_iconClass": { - "type": "string", - "title": "Icon class", - "description": "CSS class name to be applied to the button icon. No icon is recommended.", - "default": "" - }, - "_alignIconRight": { - "type": "boolean", - "title": "Align icon right", - "description": "Defines whether the icon is aligned to the left or right of the text.", - "default": false - }, - "text": { - "type": "string", - "title": "Button text", - "description": "Text that appears on the button. Suggest using the default.", - "default": "{{inc index}}", - "_adapt": { - "translatable": true - } - }, - "ariaLabel": { - "type": "string", - "title": "ARIA label", - "default": "Page {{inc index}}", - "_adapt": { - "translatable": true - } - }, - "_showTooltip": { - "type": "boolean", - "title": "Show tool tip", - "description": "Defines whether the tooltip renders on hover.", - "default": false - }, - "tooltip": { - "type": "string", - "title": "Tool tip", - "default": "{{displayTitle}}", - "_adapt": { - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Button tooltip", + "default": {}, + "properties": { + "_isEnabled": { + "type": "boolean", + "title": "Enable tooltip for this button", + "default": true + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "_adapt": { + "translatable": true + } + } } }, "_customRouteId": { @@ -548,13 +453,21 @@ "type": "string", "title": "Icon class", "description": "CSS class name to be applied to the button icon. Suggested: icon-cross", - "default": "" - }, - "_alignIconRight": { - "type": "boolean", - "title": "Align icon right", - "description": "Defines whether the icon is aligned to the left or right of the text.", - "default": false + "default": "icon-cross" + }, + "_iconAlignment": { + "type": "string", + "title": "Icon alignment", + "description": "Determines how the icon is aligned to the text.", + "default": "auto", + "enum": [ + "auto", + "left", + "right", + "top", + "bottom" + ], + "_backboneForms": "Select" }, "text": { "type": "string", @@ -573,18 +486,24 @@ "translatable": true } }, - "_showTooltip": { - "type": "boolean", - "title": "Show tool tip", - "description": "Defines whether the tooltip renders on hover.", - "default": false - }, - "tooltip": { - "type": "string", - "title": "Tool tip", - "default": "Close window", - "_adapt": { - "translatable": true + "_navTooltip": { + "type": "object", + "title": "Button tooltip", + "default": {}, + "properties": { + "_isEnabled": { + "type": "boolean", + "title": "Enable tooltip for this button", + "default": true + }, + "text": { + "type": "string", + "title": "", + "default": "Home", + "_adapt": { + "translatable": true + } + } } } } 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..fea4c5d --- /dev/null +++ b/templates/pageNav.jsx @@ -0,0 +1,37 @@ +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, + onButtonClick + } = props; + + return ( +
+ + + +
+ + + +
+ +
+ ); +} diff --git a/templates/pageNavItem.jsx b/templates/pageNavItem.jsx new file mode 100644 index 0000000..2702171 --- /dev/null +++ b/templates/pageNavItem.jsx @@ -0,0 +1,66 @@ +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 { + _classes, + _iconAlignment, + _iconClass, + _id, + _index, + _isHidden, + _tooltipId, + ariaLabel, + locked, + onButtonClick, + text, + type + } = props; + + return ( + + ); +} 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}}