@vaadin/side-nav
Advanced tools
Comparing version 24.4.0-dev.223e39f050 to 24.4.0-dev.4b20a0c55
{ | ||
"name": "@vaadin/side-nav", | ||
"version": "24.4.0-dev.223e39f050", | ||
"version": "24.4.0-dev.4b20a0c55", | ||
"publishConfig": { | ||
@@ -38,7 +38,7 @@ "access": "public" | ||
"@open-wc/dedupe-mixin": "^1.3.0", | ||
"@vaadin/a11y-base": "24.4.0-dev.223e39f050", | ||
"@vaadin/component-base": "24.4.0-dev.223e39f050", | ||
"@vaadin/vaadin-lumo-styles": "24.4.0-dev.223e39f050", | ||
"@vaadin/vaadin-material-styles": "24.4.0-dev.223e39f050", | ||
"@vaadin/vaadin-themable-mixin": "24.4.0-dev.223e39f050", | ||
"@vaadin/a11y-base": "24.4.0-dev.4b20a0c55", | ||
"@vaadin/component-base": "24.4.0-dev.4b20a0c55", | ||
"@vaadin/vaadin-lumo-styles": "24.4.0-dev.4b20a0c55", | ||
"@vaadin/vaadin-material-styles": "24.4.0-dev.4b20a0c55", | ||
"@vaadin/vaadin-themable-mixin": "24.4.0-dev.4b20a0c55", | ||
"lit": "^3.0.0" | ||
@@ -56,3 +56,3 @@ }, | ||
], | ||
"gitHead": "5e2e3bfc811c95aed9354235fab93fdbf43eb354" | ||
"gitHead": "b79c81e5f6fd24684b34ee0dc434e94d943ea13e" | ||
} |
@@ -7,4 +7,3 @@ # @vaadin/side-nav | ||
[![npm version](https://badgen.net/npm/v/@vaadin/vaadin-side-nav)](https://www.npmjs.com/package/@vaadin/vaadin-side-nav) | ||
[![Discord](https://img.shields.io/discord/732335336448852018?label=discord)](https://discord.gg/PHmkCKC) | ||
[![npm version](https://badgen.net/npm/v/@vaadin/side-nav)](https://www.npmjs.com/package/@vaadin/side-nav) | ||
@@ -11,0 +10,0 @@ ```html |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -5,0 +5,0 @@ */ |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -5,0 +5,0 @@ */ |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -5,0 +5,0 @@ */ |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -97,8 +97,28 @@ */ | ||
/** | ||
* Whether the path of the item matches the current path. | ||
* Set when the item is appended to DOM or when navigated back | ||
* to the page that contains this item using the browser. | ||
* Whether the item's path matches the current browser URL. | ||
* | ||
* A match occurs when both share the same base origin (like https://example.com), | ||
* the same path (like /path/to/page), and the browser URL contains all | ||
* the query parameters with the same values from the item's path. | ||
* | ||
* The state is updated when the item is added to the DOM or when the browser | ||
* navigates to a new page. | ||
*/ | ||
readonly current: boolean; | ||
/** | ||
* The target of the link. Works only when `path` is set. | ||
*/ | ||
target: string | null | undefined; | ||
/** | ||
* Whether to exclude the item from client-side routing. When enabled, | ||
* this causes the item to behave like a regular anchor, causing a full | ||
* page reload. This only works with supported routers, such as the one | ||
* provided in Vaadin apps, or when using the side nav `onNavigate` hook. | ||
* | ||
* @attr {boolean} router-ignore | ||
*/ | ||
routerIgnore: boolean; | ||
addEventListener<K extends keyof SideNavItemEventMap>( | ||
@@ -105,0 +125,0 @@ type: K, |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -118,6 +118,11 @@ */ | ||
/** | ||
* Whether the path of the item matches the current path. | ||
* Set when the item is appended to DOM or when navigated back | ||
* to the page that contains this item using the browser. | ||
* Whether the item's path matches the current browser URL. | ||
* | ||
* A match occurs when both share the same base origin (like https://example.com), | ||
* the same path (like /path/to/page), and the browser URL contains at least | ||
* all the query parameters with the same values from the item's path. | ||
* | ||
* The state is updated when the item is added to the DOM or when the browser | ||
* navigates to a new page. | ||
* | ||
* @type {boolean} | ||
@@ -131,2 +136,21 @@ */ | ||
}, | ||
/** | ||
* The target of the link. Works only when `path` is set. | ||
*/ | ||
target: String, | ||
/** | ||
* Whether to exclude the item from client-side routing. When enabled, | ||
* this causes the item to behave like a regular anchor, causing a full | ||
* page reload. This only works with supported routers, such as the one | ||
* provided in Vaadin apps, or when using the side nav `onNavigate` hook. | ||
* | ||
* @type {boolean} | ||
* @attr {boolean} router-ignore | ||
*/ | ||
routerIgnore: { | ||
type: Boolean, | ||
value: false, | ||
}, | ||
}; | ||
@@ -189,2 +213,3 @@ } | ||
window.addEventListener('popstate', this.__boundUpdateCurrent); | ||
window.addEventListener('side-nav-location-changed', this.__boundUpdateCurrent); | ||
} | ||
@@ -196,2 +221,3 @@ | ||
window.removeEventListener('popstate', this.__boundUpdateCurrent); | ||
window.removeEventListener('side-nav-location-changed', this.__boundUpdateCurrent); | ||
} | ||
@@ -208,2 +234,4 @@ | ||
href="${ifDefined(this.disabled ? null : this.path)}" | ||
target="${ifDefined(this.target)}" | ||
?router-ignore="${this.routerIgnore}" | ||
part="link" | ||
@@ -257,2 +285,3 @@ aria-current="${this.current ? 'page' : 'false'}" | ||
if (this.current) { | ||
this.__expandParentItems(); | ||
this.expanded = this._items.length > 0; | ||
@@ -263,2 +292,16 @@ } | ||
/** @private */ | ||
__expandParentItems() { | ||
const parentItem = this.__getParentItem(); | ||
if (parentItem) { | ||
parentItem.__expandParentItems(); | ||
} | ||
this.expanded = true; | ||
} | ||
/** @private */ | ||
__getParentItem() { | ||
return this.parentElement instanceof SideNavItem ? this.parentElement : null; | ||
} | ||
/** @private */ | ||
__isCurrent() { | ||
@@ -268,6 +311,5 @@ if (this.path == null) { | ||
} | ||
return ( | ||
matchPaths(document.location.pathname, this.path) || | ||
this.pathAliases.some((alias) => matchPaths(document.location.pathname, alias)) | ||
); | ||
const browserPath = `${document.location.pathname}${document.location.search}`; | ||
return matchPaths(browserPath, this.path) || this.pathAliases.some((alias) => matchPaths(browserPath, alias)); | ||
} | ||
@@ -274,0 +316,0 @@ } |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -10,2 +10,3 @@ */ | ||
import { SideNavChildrenMixin, type SideNavI18n } from './vaadin-side-nav-children-mixin.js'; | ||
import type { SideNavItem } from './vaadin-side-nav-item.js'; | ||
@@ -25,2 +26,11 @@ export type { SideNavI18n }; | ||
export type NavigateEvent = { | ||
path: SideNavItem['path']; | ||
target: SideNavItem['target']; | ||
current: SideNavItem['current']; | ||
expanded: SideNavItem['expanded']; | ||
pathAliases: SideNavItem['pathAliases']; | ||
originalEvent: MouseEvent; | ||
}; | ||
/** | ||
@@ -88,2 +98,36 @@ * `<vaadin-side-nav>` is a Web Component for navigation menus. | ||
/** | ||
* Callback function for router integration. | ||
* | ||
* When a side nav item link is clicked, this function is called and the default click action is cancelled. | ||
* This delegates the responsibility of navigation to the function's logic. | ||
* | ||
* The click event action is not cancelled in the following cases: | ||
* - The click event has a modifier (e.g. `metaKey`, `shiftKey`) | ||
* - The click event is on an external link | ||
* - The click event is on a link with `target="_blank"` | ||
* - The function explicitly returns `false` | ||
* | ||
* The function receives an object with the properties of the clicked side-nav item: | ||
* - `path`: The path of the navigation item. | ||
* - `target`: The target of the navigation item. | ||
* - `current`: A boolean indicating whether the navigation item is currently selected. | ||
* - `expanded`: A boolean indicating whether the navigation item is expanded. | ||
* - `pathAliases`: An array of path aliases for the navigation item. | ||
* - `originalEvent`: The original DOM event that triggered the navigation. | ||
* | ||
* Also see the `location` property for updating the highlighted navigation item on route change. | ||
*/ | ||
onNavigate?: ((event: NavigateEvent) => boolean) | ((event: NavigateEvent) => void); | ||
/** | ||
* A change to this property triggers an update of the highlighted item in the side navigation. While it typically | ||
* corresponds to the browser's URL, the specific value assigned to the property is irrelevant. The component has | ||
* its own internal logic for determining which item is highlighted. | ||
* | ||
* The main use case for this property is when the side navigation is used with a client-side router. In this case, | ||
* the component needs to be informed about route changes so it can update the highlighted item. | ||
*/ | ||
location: any; | ||
addEventListener<K extends keyof SideNavEventMap>( | ||
@@ -90,0 +134,0 @@ type: K, |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -106,2 +106,44 @@ */ | ||
}, | ||
/** | ||
* Callback function for router integration. | ||
* | ||
* When a side nav item link is clicked, this function is called and the default click action is cancelled. | ||
* This delegates the responsibility of navigation to the function's logic. | ||
* | ||
* The click event action is not cancelled in the following cases: | ||
* - The click event has a modifier (e.g. `metaKey`, `shiftKey`) | ||
* - The click event is on an external link | ||
* - The click event is on a link with `target="_blank"` | ||
* - The function explicitly returns `false` | ||
* | ||
* The function receives an object with the properties of the clicked side-nav item: | ||
* - `path`: The path of the navigation item. | ||
* - `target`: The target of the navigation item. | ||
* - `current`: A boolean indicating whether the navigation item is currently selected. | ||
* - `expanded`: A boolean indicating whether the navigation item is expanded. | ||
* - `pathAliases`: An array of path aliases for the navigation item. | ||
* - `originalEvent`: The original DOM event that triggered the navigation. | ||
* | ||
* Also see the `location` property for updating the highlighted navigation item on route change. | ||
* | ||
* @type {function(Object): boolean | undefined} | ||
*/ | ||
onNavigate: { | ||
attribute: false, | ||
}, | ||
/** | ||
* A change to this property triggers an update of the highlighted item in the side navigation. While it typically | ||
* corresponds to the browser's URL, the specific value assigned to the property is irrelevant. The component has | ||
* its own internal logic for determining which item is highlighted. | ||
* | ||
* The main use case for this property is when the side navigation is used with a client-side router. In this case, | ||
* the component needs to be informed about route changes so it can update the highlighted item. | ||
* | ||
* @type {any} | ||
*/ | ||
location: { | ||
observer: '__locationChanged', | ||
}, | ||
}; | ||
@@ -118,2 +160,3 @@ } | ||
this._labelId = `side-nav-label-${generateUniqueId()}`; | ||
this.addEventListener('click', this.__onClick); | ||
} | ||
@@ -209,5 +252,62 @@ | ||
/** @private */ | ||
__locationChanged() { | ||
window.dispatchEvent(new CustomEvent('side-nav-location-changed')); | ||
} | ||
/** @private */ | ||
__toggleCollapsed() { | ||
this.collapsed = !this.collapsed; | ||
} | ||
/** @private */ | ||
__onClick(e) { | ||
if (!this.onNavigate) { | ||
return; | ||
} | ||
const hasModifier = e.metaKey || e.shiftKey; | ||
if (hasModifier) { | ||
// Allow default action for clicks with modifiers | ||
return; | ||
} | ||
const composedPath = e.composedPath(); | ||
const item = composedPath.find((el) => el.localName && el.localName.includes('side-nav-item')); | ||
const anchor = composedPath.find((el) => el instanceof HTMLAnchorElement); | ||
if (!item || !item.shadowRoot.contains(anchor)) { | ||
// Not a click on a side-nav-item anchor | ||
return; | ||
} | ||
const isRelative = anchor.href && anchor.href.startsWith(location.origin); | ||
if (!isRelative) { | ||
// Allow default action for external links | ||
return; | ||
} | ||
if (item.target === '_blank') { | ||
// Allow default action for links with target="_blank" | ||
return; | ||
} | ||
if (item.routerIgnore) { | ||
// Allow default action when client-side routing is ignored | ||
return; | ||
} | ||
// Call the onNavigate callback | ||
const result = this.onNavigate({ | ||
path: item.path, | ||
target: item.target, | ||
current: item.current, | ||
expanded: item.expanded, | ||
pathAliases: item.pathAliases, | ||
originalEvent: e, | ||
}); | ||
if (result !== false) { | ||
// Cancel the default action if the callback didn't return false | ||
e.preventDefault(); | ||
} | ||
} | ||
} | ||
@@ -214,0 +314,0 @@ |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -5,0 +5,0 @@ */ |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -5,0 +5,0 @@ */ |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -5,0 +5,0 @@ */ |
/** | ||
* @license | ||
* Copyright (c) 2023 Vaadin Ltd. | ||
* Copyright (c) 2023 - 2024 Vaadin Ltd. | ||
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/ | ||
@@ -5,0 +5,0 @@ */ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
60441
1467
81
+ Added@vaadin/a11y-base@24.4.0-dev.4b20a0c55(transitive)
+ Added@vaadin/component-base@24.4.0-dev.4b20a0c55(transitive)
+ Added@vaadin/icon@24.4.0-dev.4b20a0c55(transitive)
+ Added@vaadin/vaadin-lumo-styles@24.4.0-dev.4b20a0c55(transitive)
+ Added@vaadin/vaadin-material-styles@24.4.0-dev.4b20a0c55(transitive)
+ Added@vaadin/vaadin-themable-mixin@24.4.0-dev.4b20a0c55(transitive)
- Removed@vaadin/a11y-base@24.4.0-dev.223e39f050(transitive)
- Removed@vaadin/component-base@24.4.0-dev.223e39f050(transitive)
- Removed@vaadin/icon@24.4.0-dev.223e39f050(transitive)
- Removed@vaadin/vaadin-lumo-styles@24.4.0-dev.223e39f050(transitive)
- Removed@vaadin/vaadin-material-styles@24.4.0-dev.223e39f050(transitive)
- Removed@vaadin/vaadin-themable-mixin@24.4.0-dev.223e39f050(transitive)