2 Commits
12 changed files with 115 additions and 30 deletions
+7 -1
View File
@@ -4,7 +4,7 @@ Zentrale, barrierearme Portalnavigation aus SharePoint Managed Metadata für Sha
| Eigenschaft | Wert |
|---|---|
| Version | 2.2.0 |
| Version | 2.2.3 |
| SharePoint Framework | 1.4.1 |
| Node.js für den Build | 8.17.0 |
| Moderne Seiten | SPFx Application Customizer |
@@ -272,6 +272,12 @@ MegaMenu/
## Versionshistorie
### 2.2.1
- Terms mit `_Sys_Nav_ExcludedProviders = GlobalNavigationTaxonomyProvider` werden einschließlich ihres Unterbaums
aus der modernen und klassischen globalen Navigation ausgeblendet.
- Das Cache-Schema wurde erhöht, damit die neue Sichtbarkeitsregel sofort greift.
### 2.2.0
- mobiles Hamburger-/Accordion-Menü für Modern und Classic
+1 -1
View File
@@ -1,4 +1,4 @@
# MegaMenu – Arbeitsplan Version 2.2
# MegaMenu – Arbeitsplan Version 2.2.3
Status: Implementierung abgeschlossen; Ship-Build auf der vorgesehenen Node-8-Buildumgebung ausstehend
+21 -8
View File
@@ -1,4 +1,4 @@
/* SharePoint MegaMenu 2.2.0 - Classic pages */
/* SharePoint MegaMenu 2.2.3 - Classic pages */
:root {
--megaMenuBarHeight: 44px;
--megaMenuContentWidth: 1200px;
@@ -8,10 +8,13 @@
.megamenu-classic-container {
position: relative;
box-sizing: border-box;
width: 100%;
margin: 0;
padding: 0;
overflow: visible;
border-top: 1px solid #edebe9;
border-bottom: 1px solid #edebe9;
z-index: var(--megaMenuZIndex);
font-family: "Segoe UI", Arial, sans-serif;
}
@@ -22,8 +25,8 @@
width: 100%;
min-height: var(--megaMenuBarHeight);
overflow: visible;
border-top: 1px solid #edebe9;
border-bottom: 1px solid #edebe9;
border-top: 0;
border-bottom: 0;
background: #fff;
color: #323130;
}
@@ -59,6 +62,15 @@
padding: 0;
}
.mega-menu-top-item:hover,
.mega-menu-top-item:focus-within,
.mega-menu-top-item:active,
.mega-menu-top-item.is-open,
.mega-menu-top-item.is-active {
background: #f3f2f1;
color: #323130;
}
.mega-menu-mode-flyout .mega-menu-top-item {
position: relative;
}
@@ -114,8 +126,8 @@
.menu-item-text:hover,
.menu-item-toggle:hover,
.menu-item-toggle[aria-expanded="true"] {
background: #f3f2f1;
color: #323130;
background: transparent;
color: inherit;
text-decoration: none;
}
@@ -258,8 +270,9 @@
outline: none;
}
.flyout-link:hover,
.flyout-item:focus-within > .flyout-link {
.flyout-item:hover > .flyout-link,
.flyout-item:focus-within > .flyout-link,
.flyout-item:active > .flyout-link {
background: #f3f2f1;
color: #323130;
}
@@ -368,7 +381,7 @@
border-bottom: 1px solid #edebe9;
}
.menu-item-toggle { width: 44px; margin: 0; border-left: 1px solid #edebe9; }
.menu-item-toggle { width: 44px; margin: 0; border-left: 0; }
.mega-menu {
position: static;
+20 -5
View File
@@ -122,7 +122,7 @@
_cacheKey() {
const normalize = value => encodeURIComponent(String(value || '').trim().toLowerCase());
return 'MegaMenu:TermSet:v3:' + normalize(this._siteCollectionUrl) + ':' + normalize(this.termSetIdentifier) + ':' + this._languageLcid + ':' + normalize(this._cacheVersion);
return 'MegaMenu:TermSet:v4:' + normalize(this._siteCollectionUrl) + ':' + normalize(this.termSetIdentifier) + ':' + this._languageLcid + ':' + normalize(this._cacheVersion);
}
_getCache(allowExpired) {
@@ -190,7 +190,9 @@
while (enumerator.moveNext()) {
const term = enumerator.get_current();
const properties = this._getCustomProperties(term);
if (!term.get_isAvailableForTagging() || term.get_isDeprecated() || this._asBoolean(properties['MegaMenu.Hidden'])) {
if (!term.get_isAvailableForTagging() || term.get_isDeprecated() ||
this._asBoolean(properties['MegaMenu.Hidden']) ||
this._isExcludedFromGlobalNavigation(properties)) {
continue;
}
processedTerms.push({
@@ -215,6 +217,7 @@
_Sys_Nav_SimpleLinkUrl: props._Sys_Nav_SimpleLinkUrl || null,
_Sys_Nav_TargetUrl: props._Sys_Nav_TargetUrl || null,
_Sys_Nav_HoverText: props._Sys_Nav_HoverText || null,
_Sys_Nav_ExcludedProviders: props._Sys_Nav_ExcludedProviders || null,
'MegaMenu.Hidden': props['MegaMenu.Hidden'] || null,
'MegaMenu.OpenInNewWindow': props['MegaMenu.OpenInNewWindow'] || null,
'MegaMenu.Description': props['MegaMenu.Description'] || null
@@ -228,13 +231,24 @@
return value === true || ['true', '1', 'yes', 'ja'].indexOf(String(value || '').toLowerCase()) >= 0;
}
_isExcludedFromGlobalNavigation(properties) {
const value = String((properties || {})._Sys_Nav_ExcludedProviders || '').replace(/["']/g, '');
return value.split(/[;,|]/).some(provider =>
provider.trim().toLowerCase() === 'globalnavigationtaxonomyprovider'
);
}
_processTerms(termsData) {
const itemsDict = new ItemDictionary();
const itemsByPath = {};
const menuItems = [];
const visibleTerms = termsData.filter(termData =>
!this._asBoolean((termData.LocalCustomProperties || {})['MegaMenu.Hidden']) &&
!this._isExcludedFromGlobalNavigation(termData.LocalCustomProperties)
);
// Create MenuItem objects
termsData.forEach(termData => {
visibleTerms.forEach(termData => {
const menuItem = new MenuItem(termData, 0, this._siteCollectionUrl);
itemsDict.Add(termData.Id, menuItem);
itemsByPath[termData.PathOfTerm] = menuItem;
@@ -245,7 +259,7 @@
});
// Build hierarchy
termsData.forEach(termData => {
visibleTerms.forEach(termData => {
if (termData.PathOfTerm && termData.PathOfTerm.split(';').length > 1) {
const menuItem = itemsDict.Get(termData.Id);
const pathParts = termData.PathOfTerm.split(';');
@@ -484,6 +498,7 @@
renderer.closeAll(panel);
toggle.setAttribute('aria-expanded', String(open));
toggle.setAttribute('aria-label', (open ? renderer.strings.closeSubmenu : renderer.strings.openSubmenu) + ' ' + renderer.getToggleLabel(toggle));
parent.classList.toggle('is-open', open);
panel.setAttribute('aria-expanded', String(open));
panel.classList.toggle('js-open', open);
};
@@ -499,7 +514,7 @@
});
let closeTimer;
parent.addEventListener('mouseenter', () => { window.clearTimeout(closeTimer); setOpen(true); });
parent.addEventListener('mouseleave', () => { closeTimer = window.setTimeout(() => setOpen(false), 180); });
parent.addEventListener('mouseleave', () => { closeTimer = window.setTimeout(() => setOpen(false), 0); });
});
const flyoutToggles = document.querySelectorAll('#Mega-Menu .flyout-toggle');
+1 -1
View File
@@ -3,7 +3,7 @@
"solution": {
"name": "mega-menu-client-side-solution",
"id": "f4660e06-ce08-43ee-bfb7-5c4464e01133",
"version": "2.2.0.0",
"version": "2.2.3.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mega-menu",
"version": "2.2.0",
"version": "2.2.3",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "mega-menu",
"version": "2.2.0",
"version": "2.2.3",
"private": true,
"main": "lib/index.js",
"engines": {
+19 -6
View File
@@ -22,8 +22,11 @@ $menuLayer: var(--megaMenuZIndex, 6000);
#CustomNavigation {
position: relative;
box-sizing: border-box;
width: 100%;
overflow: visible;
border-top: 1px solid #edebe9;
border-bottom: 1px solid #edebe9;
z-index: $menuLayer;
font-family: 'Segoe UI', 'Segoe UI Web (West European)', -apple-system, BlinkMacSystemFont, Roboto, 'Helvetica Neue', sans-serif;
}
@@ -43,8 +46,8 @@ $menuLayer: var(--megaMenuZIndex, 6000);
width: 100%;
min-height: var(--megaMenuBarHeight, 44px);
overflow: visible;
border-top: 1px solid $neutralLight;
border-bottom: 1px solid $neutralLight;
border-top: 0;
border-bottom: 0;
background: $white;
color: $neutralPrimary;
z-index: $menuLayer;
@@ -70,6 +73,15 @@ $menuLayer: var(--megaMenuZIndex, 6000);
padding: 0;
}
.mega-menu-top-item:hover,
.mega-menu-top-item:focus-within,
.mega-menu-top-item:active,
.mega-menu-top-item.is-open,
.mega-menu-top-item.is-active {
background: $neutralLighter;
color: $neutralPrimary;
}
.mega-menu-mobile-toggle {
display: none;
}
@@ -132,8 +144,8 @@ $menuLayer: var(--megaMenuZIndex, 6000);
.menu-item-text:hover,
.menu-item-toggle:hover,
.menu-item-toggle[aria-expanded='true'] {
background: $neutralLighter;
color: $neutralPrimary;
background: transparent;
color: inherit;
text-decoration: none;
}
@@ -309,8 +321,9 @@ $menuLayer: var(--megaMenuZIndex, 6000);
outline: none;
}
.flyout-link:hover,
.flyout-item:hover > .flyout-link,
.flyout-item:focus-within > .flyout-link,
.flyout-item:active > .flyout-link,
.flyout-item.is-active > .flyout-link {
background: $neutralLighter;
color: $neutralPrimary;
@@ -461,7 +474,7 @@ $menuLayer: var(--megaMenuZIndex, 6000);
.menu-item-toggle {
width: 44px;
margin: 0;
border-left: 1px solid $neutralLight;
border-left: 0;
}
.mega-menu {
+4 -2
View File
@@ -23,8 +23,8 @@ export interface IMegaMenuRendererStrings {
}
export class MegaMenuRenderer {
private static readonly hoverOpenDelayMs: number = 140;
private static readonly hoverCloseDelayMs: number = 180;
private static readonly hoverOpenDelayMs: number = 0;
private static readonly hoverCloseDelayMs: number = 0;
private _container: HTMLElement | undefined;
private _documentKeydownHandler: ((event: KeyboardEvent) => void) | undefined;
private _documentClickHandler: ((event: MouseEvent) => void) | undefined;
@@ -657,6 +657,7 @@ export class MegaMenuRenderer {
const label: string = this.getTriggerLabel(trigger);
trigger.setAttribute('aria-expanded', 'true');
trigger.setAttribute('aria-label', this.strings.closeSubmenu + ' ' + label);
(trigger.parentElement as HTMLElement).classList.add('is-open');
menu.setAttribute('aria-expanded', 'true');
menu.classList.add('js-open');
this.announce(label + ' ' + this.strings.opened);
@@ -667,6 +668,7 @@ export class MegaMenuRenderer {
const label: string = this.getTriggerLabel(trigger);
trigger.setAttribute('aria-expanded', 'false');
trigger.setAttribute('aria-label', this.strings.openSubmenu + ' ' + label);
(trigger.parentElement as HTMLElement).classList.remove('is-open');
menu.setAttribute('aria-expanded', 'false');
menu.classList.remove('js-open');
this.announce(label + ' ' + this.strings.closed);
+15 -2
View File
@@ -1,13 +1,16 @@
import { ITerm, ITermLabelCollection } from './ISPTermStorePickerService';
export const MegaMenuCacheSchemaVersion: string = '3';
export const MegaMenuCacheSchemaVersion: string = '4';
export const MegaMenuTermProperties = {
hidden: 'MegaMenu.Hidden',
openInNewWindow: 'MegaMenu.OpenInNewWindow',
description: 'MegaMenu.Description'
description: 'MegaMenu.Description',
excludedProviders: '_Sys_Nav_ExcludedProviders'
};
export const GlobalNavigationTaxonomyProvider: string = 'GlobalNavigationTaxonomyProvider';
export interface IMegaMenuCacheEnvelope<T> {
expiresAt: number;
value: T;
@@ -48,6 +51,16 @@ export function isTermHidden(term: ITerm): boolean {
return parseBooleanProperty(getTermProperty(term, MegaMenuTermProperties.hidden));
}
export function isTermExcludedFromGlobalNavigation(term: ITerm): boolean {
const value: string = getTermProperty(term, MegaMenuTermProperties.excludedProviders);
if (!value) {
return false;
}
const providers: string[] = value.replace(/["']/g, '').split(/[;,|]/);
const expected: string = GlobalNavigationTaxonomyProvider.toLowerCase();
return providers.some((provider: string): boolean => provider.trim().toLowerCase() === expected);
}
export function getLocalizedTermLabel(term: ITerm, languageLcid: number): string {
const labels: ITermLabelCollection | undefined = term && term.Labels;
const items = labels && labels._Child_Items_ ? labels._Child_Items_ : [];
+9 -2
View File
@@ -6,7 +6,12 @@ import { ApplicationCustomizerContext } from '@microsoft/sp-application-base';
import SPTermStorePickerService from './SPTermStorePickerService';
import { ITerm, ITermSet } from './ISPTermStorePickerService';
import { debugLog, debugWarn } from './MegaMenuDebug';
import { buildMenuHierarchy, IMegaMenuHierarchyEntry, isTermHidden } from './MegaMenuCore';
import {
buildMenuHierarchy,
IMegaMenuHierarchyEntry,
isTermExcludedFromGlobalNavigation,
isTermHidden
} from './MegaMenuCore';
const LOG_SOURCE: string = 'TaxonomyNavigationService';
@@ -70,7 +75,9 @@ export class TaxonomyNavigationService implements ITaxonomyNavigationService {
throw new Error('The configured navigation term set was not found or contains no terms.');
}
termset.Terms.filter((term: ITerm) => !isTermHidden(term)).forEach((term: ITerm) => {
termset.Terms.filter((term: ITerm) =>
!isTermHidden(term) && !isTermExcludedFromGlobalNavigation(term)
).forEach((term: ITerm) => {
const menuItem: IMenuItem = new MenuItem(term, 0, siteCollectionUrl, this.languageLcid);
entries.push({
id: term.Id,
+16
View File
@@ -6,6 +6,7 @@ import {
getTermProperty,
isExternalNavigationUrl,
isNavigationUrlActive,
isTermExcludedFromGlobalNavigation,
isTermHidden,
MegaMenuTermProperties,
parseBooleanProperty,
@@ -31,6 +32,21 @@ function term(values: any): ITerm { // tslint:disable-line:no-any
assert('boolean property accepts German yes', parseBooleanProperty('ja'));
assert('boolean property rejects false', !parseBooleanProperty('false'));
assert('hidden local term property', isTermHidden(term({ LocalCustomProperties: { 'MegaMenu.Hidden': 'true' } })));
assert('global navigation provider excludes term', isTermExcludedFromGlobalNavigation(term({
LocalCustomProperties: { '_Sys_Nav_ExcludedProviders': 'GlobalNavigationTaxonomyProvider' }
})));
assert('global navigation provider is matched case-insensitively in provider list',
isTermExcludedFromGlobalNavigation(term({
LocalCustomProperties: {
'_Sys_Nav_ExcludedProviders': 'CurrentNavigationTaxonomyProvider; globalnavigationtaxonomyprovider'
}
})));
assert('different excluded provider keeps term visible', !isTermExcludedFromGlobalNavigation(term({
LocalCustomProperties: { '_Sys_Nav_ExcludedProviders': 'CurrentNavigationTaxonomyProvider' }
})));
assert('missing excluded provider keeps term visible', !isTermExcludedFromGlobalNavigation(term({
LocalCustomProperties: {}
})));
assert('description local term property', getTermProperty(term({ LocalCustomProperties: { 'MegaMenu.Description': 'Forms' } }), MegaMenuTermProperties.description) === 'Forms');
assert('open-in-new-window local term property', parseBooleanProperty(getTermProperty(term({ LocalCustomProperties: { 'MegaMenu.OpenInNewWindow': '1' } }), MegaMenuTermProperties.openInNewWindow)));