feat: Enhance MegaMenu with localization, caching, and new properties
- Added localization support for loading states, empty navigation, and submenu actions in both German and English. - Introduced new properties in IMenuItem for description, openInNewWindow, and external links. - Updated SPTermStorePickerService to handle cache versioning and language-specific caching. - Implemented a new MegaMenuCore service for handling term properties, cache management, and navigation URL resolution. - Refactored TaxonomyNavigationService to build menu hierarchy and filter hidden terms. - Added automated tests for MegaMenu core functionalities and static asset validation. - Removed unused ItemDictionary service. - Created a comprehensive ToDo document outlining the implementation status and future tasks.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* SharePoint MegaMenu for Classic Pages
|
||||
* Version: 2.1.0
|
||||
* Version: 2.2.0
|
||||
*
|
||||
* This version uses standalone services that replicate the SPFx logic
|
||||
* without requiring the SharePoint Framework.
|
||||
@@ -25,6 +25,41 @@
|
||||
config.containerId = config.containerId || 's4-titlerow';
|
||||
config.debug = config.debug === true;
|
||||
|
||||
function getLanguageLcid() {
|
||||
return Number(window._spPageContextInfo && _spPageContextInfo.currentLanguage) || 1033;
|
||||
}
|
||||
|
||||
function getStrings() {
|
||||
return getLanguageLcid() === 1031 ? {
|
||||
navigationLabel: 'Hauptnavigation', openNavigation: 'Hauptnavigation öffnen', closeNavigation: 'Hauptnavigation schließen',
|
||||
openSubmenu: 'Untermenü öffnen für', closeSubmenu: 'Untermenü schließen für', submenuLabel: 'Untermenü',
|
||||
externalLink: 'externer Link', loading: 'Navigation wird geladen…', missing: 'Die Navigation ist nicht konfiguriert.',
|
||||
empty: 'Die Navigation enthält keine sichtbaren Einträge.', error: 'Die Navigation konnte nicht geladen werden.'
|
||||
} : {
|
||||
navigationLabel: 'Main navigation', openNavigation: 'Open main navigation', closeNavigation: 'Close main navigation',
|
||||
openSubmenu: 'Open submenu for', closeSubmenu: 'Close submenu for', submenuLabel: 'submenu',
|
||||
externalLink: 'external link', loading: 'Loading navigation…', missing: 'Navigation is not configured.',
|
||||
empty: 'The navigation does not contain any visible entries.', error: 'Navigation could not be loaded.'
|
||||
};
|
||||
}
|
||||
|
||||
function showStatus(message) {
|
||||
var existing = document.querySelector('.megamenu-classic-container');
|
||||
var container = existing || document.createElement('div');
|
||||
container.className = 'megamenu-classic-container';
|
||||
container.innerHTML = '';
|
||||
var status = document.createElement('div');
|
||||
status.className = 'mega-menu-status';
|
||||
status.setAttribute('role', 'status');
|
||||
status.textContent = message;
|
||||
container.appendChild(status);
|
||||
if (!existing) {
|
||||
var anchor = document.getElementById(config.containerId) || document.body.firstChild;
|
||||
anchor.parentNode.insertBefore(container, anchor.nextSibling);
|
||||
}
|
||||
return container;
|
||||
}
|
||||
|
||||
function log(message, data) {
|
||||
if (config.debug && console && console.log) {
|
||||
console.log('[MegaMenu Classic] ' + message, data || '');
|
||||
@@ -94,16 +129,16 @@
|
||||
callback(props);
|
||||
} catch (e) {
|
||||
log('Error parsing UserCustomAction properties: ' + e.message);
|
||||
callback({ termSetName: 'Navigation', menuMode: 'megaMenu', debug: false });
|
||||
callback({});
|
||||
}
|
||||
} else {
|
||||
log('No UserCustomAction found - using defaults');
|
||||
callback({ termSetName: 'Navigation', menuMode: 'megaMenu', debug: false });
|
||||
callback({});
|
||||
}
|
||||
})
|
||||
.catch(function(error) {
|
||||
log('Error reading configuration: ' + error.message);
|
||||
callback({ termSetName: 'Navigation', menuMode: 'megaMenu', debug: false });
|
||||
callback({});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -120,20 +155,39 @@
|
||||
readMegaMenuConfiguration(function(props) {
|
||||
config.debug = props.debug === true || config.debug === true;
|
||||
log('Using configuration:', props);
|
||||
var strings = getStrings();
|
||||
var termSetIdentifier = props.termSetId || props.termSetName;
|
||||
if (!termSetIdentifier) {
|
||||
showStatus(strings.missing);
|
||||
return;
|
||||
}
|
||||
var loadingContainer = showStatus(strings.loading);
|
||||
|
||||
// Create context
|
||||
var context = createClassicContext();
|
||||
|
||||
// Create taxonomy service using standalone implementation
|
||||
var taxonomyService = new window.MegaMenuServices.TaxonomyNavigationService(
|
||||
context,
|
||||
props.termSetId || props.termSetName
|
||||
context,
|
||||
termSetIdentifier,
|
||||
{
|
||||
cacheMinutes: props.cacheMinutes,
|
||||
cacheVersion: props.cacheVersion,
|
||||
languageLcid: getLanguageLcid()
|
||||
}
|
||||
);
|
||||
|
||||
// Load menu items
|
||||
taxonomyService.getMenuItems()
|
||||
.then(function(menuItems) {
|
||||
log('Menu items loaded:', menuItems);
|
||||
if (!menuItems || menuItems.length === 0) {
|
||||
showStatus(strings.empty);
|
||||
return;
|
||||
}
|
||||
if (loadingContainer && loadingContainer.parentNode) {
|
||||
loadingContainer.parentNode.removeChild(loadingContainer);
|
||||
}
|
||||
|
||||
// Find target container
|
||||
var container = document.getElementById(config.containerId);
|
||||
@@ -160,7 +214,8 @@
|
||||
var renderer = new window.MegaMenuServices.MegaMenuRenderer(
|
||||
context,
|
||||
menuItems,
|
||||
props.menuMode === 'flyout' ? 'flyout' : 'megaMenu'
|
||||
props.menuMode === 'flyout' ? 'flyout' : 'megaMenu',
|
||||
strings
|
||||
);
|
||||
|
||||
// Render the menu
|
||||
@@ -170,21 +225,7 @@
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.error('[MegaMenu] Error loading menu items:', error);
|
||||
|
||||
// Show error message to user
|
||||
var container = document.getElementById(config.containerId);
|
||||
if (container) {
|
||||
var errorDiv = document.createElement('div');
|
||||
errorDiv.style.cssText = 'background:#ffebee;color:#c62828;padding:10px;border:1px solid #ef5350;margin:5px 0;';
|
||||
errorDiv.innerHTML = '<strong>MegaMenu Error:</strong> ' + error.message +
|
||||
'<br><small>Check browser console for details.</small>';
|
||||
|
||||
if (container.nextSibling) {
|
||||
container.parentNode.insertBefore(errorDiv, container.nextSibling);
|
||||
} else {
|
||||
container.parentNode.appendChild(errorDiv);
|
||||
}
|
||||
}
|
||||
showStatus(strings.error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user