MediaWiki:Gadget-wix-nav.js: Difference between revisions
Appearance
Content deleted Content added
No edit summary |
No edit summary |
||
| (2 intermediate revisions by the same user not shown) | |||
| Line 154: | Line 154: | ||
} |
} |
||
// 2. Detect current page |
// 2. Detect current page and its index |
||
var currentPage = mw.config.get( 'wgPageName' ).replace( / /g, '_' ); |
var currentPage = mw.config.get( 'wgPageName' ).replace( / /g, '_' ); |
||
var currentLabel = 'Navigate to\u2026'; |
var currentLabel = 'Navigate to\u2026'; |
||
var currentIdx = -1; |
|||
pages.forEach( function ( entry ) { |
pages.forEach( function ( entry, i ) { |
||
var normalizedPage = entry[ 1 ].replace( / /g, '_' ); |
var normalizedPage = entry[ 1 ].replace( / /g, '_' ); |
||
if ( normalizedPage === currentPage ) { |
if ( normalizedPage === currentPage ) { |
||
currentLabel = entry[ 0 ]; |
currentLabel = entry[ 0 ]; |
||
currentIdx = i; |
|||
} |
} |
||
} ); |
} ); |
||
| Line 168: | Line 170: | ||
wix.empty( container ); |
wix.empty( container ); |
||
var |
var dropdown = wix.el( 'div', { |
||
className: 'wix-dropdown', |
className: 'wix-dropdown', |
||
'role': 'navigation', |
'role': 'navigation', |
||
| Line 213: | Line 215: | ||
} ); |
} ); |
||
dropdown.appendChild( toggle ); |
|||
dropdown.appendChild( panel ); |
|||
| ⚫ | |||
// 4. Back / Next buttons |
|||
var isFirst = currentIdx <= 0; |
|||
var isLast = currentIdx >= pages.length - 1; |
|||
var btnBack = wix.el( 'a', { |
|||
className: 'wix-dropdown-nav-btn' |
|||
}, [ wix.el( 'span', { className: 'wix-dropdown-nav-arrow', textContent: '\u25BC' } ) ] ); |
|||
var btnNext = wix.el( 'a', { |
|||
className: 'wix-dropdown-nav-btn' |
|||
}, [ wix.el( 'span', { className: 'wix-dropdown-nav-arrow wix-dropdown-nav-arrow--next', textContent: '\u25BC' } ) ] ); |
|||
if ( isFirst || currentIdx === -1 ) { |
|||
btnBack.classList.add( 'wix-dropdown-nav-btn--disabled' ); |
|||
btnBack.removeAttribute( 'href' ); |
|||
} else { |
|||
btnBack.setAttribute( 'href', mw.util.getUrl( pages[ currentIdx - 1 ][ 1 ] ) ); |
|||
} |
|||
if ( isLast || currentIdx === -1 ) { |
|||
btnNext.classList.add( 'wix-dropdown-nav-btn--disabled' ); |
|||
btnNext.removeAttribute( 'href' ); |
|||
} else { |
|||
btnNext.setAttribute( 'href', mw.util.getUrl( pages[ currentIdx + 1 ][ 1 ] ) ); |
|||
} |
|||
// 5. Assemble: Back | Dropdown | Next |
|||
var row = wix.el( 'div', { className: 'wix-dropdown-row' }, [ |
|||
btnBack, |
|||
dropdown, |
|||
btnNext |
|||
] ); |
|||
| ⚫ | |||
// 6. Make nav buttons square (width = height, measured after layout) |
|||
setTimeout( function () { |
|||
var h = toggle.offsetHeight; |
|||
if ( h ) { |
|||
btnBack.style.width = h + 'px'; |
|||
btnNext.style.width = h + 'px'; |
|||
} |
|||
}, 0 ); |
|||
// |
// 7. Toggle open/close |
||
var isOpen = false; |
var isOpen = false; |
||
| Line 226: | Line 271: | ||
} ); |
} ); |
||
// |
// 7. Close on click outside |
||
document.addEventListener( 'click', function ( e ) { |
document.addEventListener( 'click', function ( e ) { |
||
if ( isOpen && ! |
if ( isOpen && !dropdown.contains( e.target ) ) { |
||
isOpen = false; |
isOpen = false; |
||
toggle.classList.remove( 'wix-dropdown-toggle--open' ); |
toggle.classList.remove( 'wix-dropdown-toggle--open' ); |
||
Latest revision as of 20:29, 6 April 2026
/* ================================================================
WIX-NAV.JS — Wiki Interactive Experience: Training Navigation
================================================================
Depends on: ext.gadget.wix-core (window.wix must exist)
Finds every container with data-wix-module="nav", reads the
page list from data-wix-pages (JSON), and builds a horizontal
navigation bar with the current page highlighted.
DOM contract (set by wikitext snippet):
─────────────────────────────────────────
Container: [data-wix-module="nav"]
data-wix-pages JSON array of [label, pageName] pairs
Example:
data-wix-pages='[["Module 1","Internal:Training/IFRS17/module-1"],
["Module 2","Internal:Training/IFRS17/module-2"]]'
================================================================ */
( function () {
'use strict';
/* ── SVG Icon Registry ───────────────────────────────────────
28×28 viewBox, stroke-based icons for nav cards.
Add new entries as new subjects appear on the wiki.
──────────────────────────────────────────────────────────── */
var NAV_ICONS = {
shield: '<path d="M14 3L4 7v6c0 5.5 4.3 10.6 10 12 5.7-1.4 10-6.5 10-12V7L14 3z"/><path d="M10 14l3 3 5-6"/>',
document: '<rect x="6" y="3" width="16" height="22" rx="2"/><path d="M10 8h8M10 12h8M10 16h5"/>',
pen: '<path d="M5 23l1.5-5L19 5.5a2 2 0 012.8 2.8L9.5 20.8z"/><path d="M15 9l3 3"/>',
clipboard: '<path d="M9 3h10a2 2 0 012 2v18a2 2 0 01-2 2H9a2 2 0 01-2-2V5a2 2 0 012-2z"/><path d="M11 3v2h6V3"/><path d="M11 10h6M11 14h6M11 18h4"/>',
layers: '<path d="M14 3L3 9l11 6 11-6L14 3z"/><path d="M3 14l11 6 11-6"/><path d="M3 19l11 6 11-6"/>',
warning: '<path d="M14 4L3 23h22L14 4z"/><path d="M14 10v6"/><circle cx="14" cy="19" r="1" fill="currentColor" stroke="none"/>',
chart: '<path d="M4 24V4"/><path d="M4 20h20"/><path d="M7 16l5-5 4 3 6-7"/>',
building: '<path d="M5 24V8l9-5 9 5v16"/><path d="M5 24h18"/><path d="M11 24v-6h6v6"/><path d="M10 10h2M16 10h2M10 14h2M16 14h2"/>',
tag: '<path d="M4 4h10l10 10-10 10L4 14V4z"/><circle cx="9" cy="9" r="1.5" fill="currentColor" stroke="none"/>',
coins: '<ellipse cx="14" cy="20" rx="8" ry="3"/><path d="M6 20v-4c0-1.7 3.6-3 8-3s8 1.3 8 3v4"/><path d="M6 16v-4c0-1.7 3.6-3 8-3s8 1.3 8 3v4"/>',
globe: '<circle cx="14" cy="14" r="10"/><ellipse cx="14" cy="14" rx="4.5" ry="10"/><path d="M4.5 10h19M4.5 18h19"/>',
heart: '<path d="M14 24s-8-5.5-8-11a5 5 0 0110 0 5 5 0 0110 0c0 5.5-8 11-8 11z"/>',
car: '<path d="M4 18h20M6.5 11l2-4.5h11L21.5 11M5 18v2.5a1 1 0 001 1h2a1 1 0 001-1V18M19 18v2.5a1 1 0 001 1h2a1 1 0 001-1V18"/><rect x="4" y="11" width="20" height="7" rx="2"/>',
home: '<path d="M4 13l10-8 10 8"/><path d="M6 12v10a1 1 0 001 1h14a1 1 0 001-1V12"/><path d="M11 23v-6h6v6"/>',
briefcase: '<rect x="3" y="10" width="22" height="13" rx="2"/><path d="M10 10V7a2 2 0 012-2h4a2 2 0 012 2v3"/><path d="M3 16h22"/>',
health: '<rect x="4" y="4" width="20" height="20" rx="4"/><path d="M14 9v10M9 14h10"/>',
scale: '<path d="M14 3v20"/><path d="M5 8l9-3 9 3"/><path d="M2 16a5 5 0 005 0 5 5 0 005 0"/><path d="M16 16a5 5 0 005 0 5 5 0 005 0"/><path d="M5 8v8M23 8v8"/>',
book: '<path d="M4 4a2 2 0 012-2h5a1 1 0 011 1v22a1 1 0 01-1-1C11 22 8 22 6 22a2 2 0 01-2-2V4z"/><path d="M24 4a2 2 0 00-2-2h-5a1 1 0 00-1 1v22a1 1 0 001-1c0-2 3-2 5-2a2 2 0 002-2V4z"/>',
umbrella: '<path d="M14 3a10 10 0 0110 10H14V23a2 2 0 01-4 0"/><path d="M4 13a10 10 0 0110-10"/>',
people: '<circle cx="10" cy="8" r="3"/><path d="M4 20v-2a4 4 0 018 0v2"/><circle cx="19" cy="8" r="3"/><path d="M16 20v-2a4 4 0 018 0v2"/>',
lock: '<rect x="7" y="13" width="14" height="11" rx="2"/><path d="M10 13V9a4 4 0 018 0v4"/><circle cx="14" cy="19" r="1.5" fill="currentColor" stroke="none"/>',
gear: '<circle cx="14" cy="14" r="4"/><path d="M14 2v3M14 23v3M2 14h3M23 14h3M5.1 5.1l2.1 2.1M20.8 20.8l2.1 2.1M5.1 22.9l2.1-2.1M20.8 7.2l2.1-2.1"/>'
};
var ARROW_SVG = '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7h8M8 4l3 3-3 3"/></svg>';
var FALLBACK_ICON = '<circle cx="14" cy="14" r="8"/>';
/* ── Guard ──────────────────────────────────────────────────── */
mw.hook( 'wikipage.content' ).add( function () {
var navContainers = wix.initModules( 'nav' );
navContainers.forEach( initNav );
var dropdownContainers = wix.initModules( 'nav-dropdown' );
dropdownContainers.forEach( initNavDropdown );
var cardContainers = wix.initModules( 'nav-cards' );
cardContainers.forEach( initNavCards );
} );
/* ── Main init ──────────────────────────────────────────────── */
function initNav( container ) {
// 1. Read and parse page list
var raw = wix.data( container, 'wix-pages', '[]' );
var pages;
try {
pages = JSON.parse( raw );
} catch ( e ) {
return;
}
if ( !pages.length ) {
return;
}
// 2. Detect current page
var currentPage = mw.config.get( 'wgPageName' ).replace( / /g, '_' );
// 3. Build menu
wix.empty( container );
var menu = wix.el( 'div', {
className: 'wix-menu',
'role': 'navigation',
'aria-label': 'Training navigation'
} );
var currentBtn = null;
pages.forEach( function ( entry ) {
var label = entry[ 0 ];
var pageName = entry[ 1 ];
var normalizedPage = pageName.replace( / /g, '_' );
var isCurrent = ( normalizedPage === currentPage );
var btn;
if ( isCurrent ) {
btn = wix.el( 'span', {
className: 'wix-menu-btn wix-menu-btn--current',
textContent: label,
'aria-current': 'page'
} );
currentBtn = btn;
} else {
btn = wix.el( 'a', {
className: 'wix-menu-btn',
textContent: label,
href: mw.util.getUrl( pageName )
} );
}
menu.appendChild( btn );
} );
container.appendChild( menu );
// 4. Scroll current button into view on mobile
if ( currentBtn ) {
setTimeout( function () {
currentBtn.scrollIntoView( {
inline: 'center',
block: 'nearest',
behavior: 'smooth'
} );
}, 0 );
}
}
/* ── Dropdown init ──────────────────────────────────────────── */
function initNavDropdown( container ) {
// 1. Read and parse page list
var raw = wix.data( container, 'wix-pages', '[]' );
var pages;
try {
pages = JSON.parse( raw );
} catch ( e ) {
return;
}
if ( !pages.length ) {
return;
}
// 2. Detect current page and its index
var currentPage = mw.config.get( 'wgPageName' ).replace( / /g, '_' );
var currentLabel = 'Navigate to\u2026';
var currentIdx = -1;
pages.forEach( function ( entry, i ) {
var normalizedPage = entry[ 1 ].replace( / /g, '_' );
if ( normalizedPage === currentPage ) {
currentLabel = entry[ 0 ];
currentIdx = i;
}
} );
// 3. Build dropdown
wix.empty( container );
var dropdown = wix.el( 'div', {
className: 'wix-dropdown',
'role': 'navigation',
'aria-label': 'Training navigation'
} );
// Toggle button
var chevron = wix.el( 'span', {
className: 'wix-dropdown-chevron',
textContent: '\u25BC'
} );
var toggle = wix.el( 'button', {
className: 'wix-dropdown-toggle'
}, [
wix.el( 'span', { textContent: currentLabel } ),
chevron
] );
// Panel
var panel = wix.el( 'div', { className: 'wix-dropdown-panel' } );
pages.forEach( function ( entry ) {
var label = entry[ 0 ];
var pageName = entry[ 1 ];
var normalizedPage = pageName.replace( / /g, '_' );
var isCurrent = ( normalizedPage === currentPage );
var item;
if ( isCurrent ) {
item = wix.el( 'span', {
className: 'wix-dropdown-item wix-dropdown-item--current',
textContent: label,
'aria-current': 'page'
} );
} else {
item = wix.el( 'a', {
className: 'wix-dropdown-item',
textContent: label,
href: mw.util.getUrl( pageName )
} );
}
panel.appendChild( item );
} );
dropdown.appendChild( toggle );
dropdown.appendChild( panel );
// 4. Back / Next buttons
var isFirst = currentIdx <= 0;
var isLast = currentIdx >= pages.length - 1;
var btnBack = wix.el( 'a', {
className: 'wix-dropdown-nav-btn'
}, [ wix.el( 'span', { className: 'wix-dropdown-nav-arrow', textContent: '\u25BC' } ) ] );
var btnNext = wix.el( 'a', {
className: 'wix-dropdown-nav-btn'
}, [ wix.el( 'span', { className: 'wix-dropdown-nav-arrow wix-dropdown-nav-arrow--next', textContent: '\u25BC' } ) ] );
if ( isFirst || currentIdx === -1 ) {
btnBack.classList.add( 'wix-dropdown-nav-btn--disabled' );
btnBack.removeAttribute( 'href' );
} else {
btnBack.setAttribute( 'href', mw.util.getUrl( pages[ currentIdx - 1 ][ 1 ] ) );
}
if ( isLast || currentIdx === -1 ) {
btnNext.classList.add( 'wix-dropdown-nav-btn--disabled' );
btnNext.removeAttribute( 'href' );
} else {
btnNext.setAttribute( 'href', mw.util.getUrl( pages[ currentIdx + 1 ][ 1 ] ) );
}
// 5. Assemble: Back | Dropdown | Next
var row = wix.el( 'div', { className: 'wix-dropdown-row' }, [
btnBack,
dropdown,
btnNext
] );
container.appendChild( row );
// 6. Make nav buttons square (width = height, measured after layout)
setTimeout( function () {
var h = toggle.offsetHeight;
if ( h ) {
btnBack.style.width = h + 'px';
btnNext.style.width = h + 'px';
}
}, 0 );
// 7. Toggle open/close
var isOpen = false;
toggle.addEventListener( 'click', function () {
isOpen = !isOpen;
toggle.classList.toggle( 'wix-dropdown-toggle--open', isOpen );
panel.classList.toggle( 'wix-dropdown-panel--open', isOpen );
} );
// 7. Close on click outside
document.addEventListener( 'click', function ( e ) {
if ( isOpen && !dropdown.contains( e.target ) ) {
isOpen = false;
toggle.classList.remove( 'wix-dropdown-toggle--open' );
panel.classList.remove( 'wix-dropdown-panel--open' );
}
} );
}
/* ── Nav Cards init ────────────────────────────────────────────
Reads data-wix-pages JSON:
[["Label", "PageName", "Description", "icon-name"], ...]
Builds a responsive grid of linked cards with SVG icons.
──────────────────────────────────────────────────────────── */
function buildIconSvg( name ) {
var paths = NAV_ICONS[ name ] || FALLBACK_ICON;
return '<svg width="22" height="22" viewBox="0 0 28 28" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">' + paths + '</svg>';
}
function initNavCards( container ) {
// 1. Read and parse page list
var raw = wix.data( container, 'wix-pages', '[]' );
var pages;
try {
pages = JSON.parse( raw );
} catch ( e ) {
return;
}
if ( !pages.length ) {
return;
}
// 2. Build grid
wix.empty( container );
var grid = wix.el( 'div', {
className: 'wix-nav-cards',
'role': 'navigation',
'aria-label': 'Page navigation'
} );
pages.forEach( function ( entry ) {
var label = entry[ 0 ];
var pageName = entry[ 1 ];
var desc = entry[ 2 ] || '';
var iconName = entry[ 3 ] || '';
// Icon box
var ico = wix.el( 'div', { className: 'wix-nav-card-ico' } );
ico.innerHTML = buildIconSvg( iconName );
// Text
var nameEl = wix.el( 'div', { className: 'wix-nav-card-name', textContent: label } );
var descEl = wix.el( 'div', { className: 'wix-nav-card-desc', textContent: desc } );
var txt = wix.el( 'div', { className: 'wix-nav-card-txt' }, [ nameEl, descEl ] );
// Arrow
var arrow = wix.el( 'div', { className: 'wix-nav-card-arrow' } );
arrow.innerHTML = ARROW_SVG;
// Card link
var card = wix.el( 'a', {
className: 'wix-nav-card',
href: mw.util.getUrl( pageName )
}, [ ico, txt, arrow ] );
grid.appendChild( card );
} );
container.appendChild( grid );
}
}() );