dev-3.0 #2
@@ -4,7 +4,7 @@ Zentrale, barrierearme Portalnavigation aus SharePoint Managed Metadata für Sha
|
||||
|
||||
| Eigenschaft | Wert |
|
||||
|---|---|
|
||||
| Version | 2.2.0 |
|
||||
| Version | 2.2.2 |
|
||||
| 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,4 +1,4 @@
|
||||
# MegaMenu – Arbeitsplan Version 2.2
|
||||
# MegaMenu – Arbeitsplan Version 2.2.2
|
||||
|
||||
Status: Implementierung abgeschlossen; Ship-Build auf der vorgesehenen Node-8-Buildumgebung ausstehend
|
||||
|
||||
|
||||
@@ -59,6 +59,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 +123,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 +267,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 +378,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;
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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.2.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true
|
||||
},
|
||||
|
||||
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mega-menu",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mega-menu",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.2",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
|
||||
@@ -70,6 +70,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 +141,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 +318,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 +471,7 @@ $menuLayer: var(--megaMenuZIndex, 6000);
|
||||
.menu-item-toggle {
|
||||
width: 44px;
|
||||
margin: 0;
|
||||
border-left: 1px solid $neutralLight;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.mega-menu {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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_ : [];
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user