@neovici/cosmoz-treenode-navigator
v8.1.6
Published
A Pion component that lets you navigate and search through hierarchically structured data-nodes and select one of them.
Downloads
3,155
Readme
cosmoz-treenode-navigator
A PionJS-based web component for navigating, searching, and selecting nodes in a hierarchical tree structure.
Installation
npm install @neovici/cosmoz-treenode-navigatorQuick Start
import { component, html } from '@pionjs/pion';
import '@neovici/cosmoz-treenode-navigator/cosmoz-treenode-button-view';
import { DefaultTree } from '@neovici/cosmoz-tree';
const treeData = {
1: {
name: 'Root',
pathLocator: '1',
children: {
2: {
name: 'Child A',
pathLocator: '1.2',
children: {},
},
3: {
name: 'Child B',
pathLocator: '1.3',
children: {
4: { name: 'Grandchild', pathLocator: '1.3.4', children: {} },
},
},
},
},
};
const tree = new DefaultTree(treeData);
const MyApp = () => html`
<cosmoz-treenode-button-view
.tree=${tree}
show-reset
@node-path-changed=${(e) => console.log('Selected:', e.detail.value)}
></cosmoz-treenode-button-view>
`;
customElements.define('my-app', component(MyApp));Tree Data Structure
The component expects a Tree object from @neovici/cosmoz-tree, typically instantiated with new DefaultTree(data).
The data is a nested object keyed by node IDs:
{
"1": {
"name": "Root",
"pathLocator": "1",
"children": {
"7": {
"name": "child seven",
"pathLocator": "1.7",
"children": {}
},
"8": {
"name": "child eight",
"pathLocator": "1.8",
"children": {
"9": {
"name": "child nine",
"pathLocator": "1.8.9",
"children": {}
}
}
}
}
}
}The property names name and children are configurable via DefaultTree options (searchProperty, childProperty). The pathLocatorSeparator defaults to ".".
Components
<cosmoz-treenode-button-view>
A trigger button that opens a dialog containing the tree navigator. This is the main component most consumers will use.
Properties / Attributes
| Property | Attribute | Type | Default | Description |
| ----------------------- | ------------------- | --------- | ------- | ----------------------------------------------- |
| tree | -- | Tree | -- | The tree data structure (set via JS) |
| nodePath | -- | string | '' | Selected node's path locator (two-way bindable) |
| opened | -- | boolean | false | Whether the dialog is open (two-way bindable) |
| showReset | show-reset | boolean | false | Show the reset/clear button |
| searchMinLength | search-min-length | number | 3 | Minimum characters to trigger search |
| searchDebounceTimeout | -- | number | 500 | Debounce timeout (ms) before search triggers |
Events
| Event | Detail | Description |
| ------------------- | -------------------- | ----------------------------------------- |
| node-path-changed | { value: string } | Fired when the selected node path changes |
| opened-changed | { value: boolean } | Fired when the dialog opens or closes |
Slots
| Slot | Description |
| ----------- | ------------------------------------------------------------ |
| prefix | Button icon (defaults to folder icon, override to customize) |
| suffix | Content after the button label |
| (default) | Passed through to the inner <cosmoz-treenode-navigator> |
CSS Parts
| Part | Description |
| --------------- | ------------------------------------------------------------------------------ |
| actions | Container for the open button and optional reset button |
| action-open | The main trigger button (cosmoz-button) |
| action-reset | The reset/clear button (cosmoz-button, visible when showReset && nodePath) |
| dialog | The <dialog> element |
| header | Dialog header |
| heading | Dialog heading (<h1>) |
| main | Dialog main content area |
| footer | Dialog footer |
| select-button | The "Select" confirmation button |
| cancel-button | The "Cancel" button |
Container Query Support
The host element is declared as a CSS container (container-type: inline-size). When the component's width shrinks to 80px or less, the selected node path text is automatically hidden, leaving only the icon. This allows graceful degradation in narrow layouts without media queries.
/* Built-in behavior */
:host {
container-type: inline-size;
}
@container (max-width: 80px) {
.path-text {
display: none;
}
}<cosmoz-treenode-navigator>
The inner navigator component that provides tree browsing, search, and keyboard navigation. Typically used inside <cosmoz-treenode-button-view>, but can be used standalone.
Properties
| Property | Type | Default | Description |
| ----------------------- | --------- | ----------- | ----------------------------------------------- |
| tree | Tree | -- | The tree data structure (set via JS) |
| nodePath | string | '' | Selected node's path locator (two-way bindable) |
| highlightedNodePath | string | '' | Currently highlighted node's path (notify only) |
| opened | boolean | undefined | Controls keyboard listeners and scroll behavior |
| searchMinLength | number | 3 | Minimum characters to trigger search |
| searchDebounceTimeout | number | 500 | Debounce timeout (ms) before search triggers |
Events
| Event | Detail | Description |
| ------------------------------- | ------------------- | ----------------------------------------- |
| node-path-changed | { value: string } | Fired when the selected node path changes |
| highlighted-node-path-changed | { value: string } | Fired when the highlighted node changes |
CSS Custom Properties
| Property | Default | Description |
| ----------------------------------------------------- | ------------------------------- | ------------------------------------------- |
| --cosmoz-treenode-navigator-icon-color | currentColor | Color of navigation icons (home, arrow) |
| --cosmoz-treenode-navigator-select-node-icon-color | var(--primary-color, #3a91e2) | Color of the selected/highlighted node icon |
| --cosmoz-treenode-navigator-list-item-focused-color | #f0f8ff | Background color of focused list items |
| --cosmoz-treenode-navigator-list-height | 50vh | Height of the scrollable node list |
| --cz-text-color | inherit | General text color |
| --cz-bg-color | #f5f5f5 | Section header background color |
Internationalization
All UI text is managed via i18next. The English text is used as the translation key, so it works out of the box when no translations are loaded (i18next returns the key as-is).
Consumers must initialize i18next before using the components:
import i18next from 'i18next';
i18next.init({ lng: 'en', resources: {} });To provide translations, use i18next.addResourceBundle() or any i18next backend:
i18next.addResourceBundle('sv', 'translation', {
'Select a node': 'Valj en nod',
'Search or navigate to chosen destination':
'Sok eller navigera till vald destination',
'Search...': 'Sok...',
'Click to search again but globally': 'Klicka for att soka igen men globalt',
Select: 'Valj',
Cancel: 'Avbryt',
});Translation Keys
| Key | Used in |
| ------------------------------------------ | ------------------------------------------- |
| Select a node | Button placeholder when no node is selected |
| Search or navigate to chosen destination | Dialog heading |
| Search... | Search input placeholder |
| Click to search again but globally | Global search button label |
| Select | Dialog confirm button |
| Cancel | Dialog cancel button |
Migrating from v7
This version includes breaking changes to the component API.
Property Changes
| Before | After | Notes |
| ------------------------- | -------------------- | ------------------------------------------------------------ |
| noReset (opt-out) | showReset (opt-in) | Inverted logic: set show-reset to display the reset button |
| selectedNode | removed | Use nodePath instead |
| highlightedNode | removed | Use highlightedNodePath (on the navigator) instead |
| dialogText | removed | Now managed via i18next.t() |
| buttonTextPlaceholder | removed | Now managed via i18next.t() |
| searchPlaceholder | removed | Now managed via i18next.t() |
| searchGlobalPlaceholder | removed | Now managed via i18next.t() |
nodePath is now the single source of truth for the selected node. Dialog navigation no longer affects the selected node until explicitly confirmed via the Select button.
Dependency Changes
| Before | After |
| ------------------------- | ----------------------------- |
| @neovici/cosmoz-i18next | i18next (direct dependency) |
All UI text is now translated via i18next.t() instead of being passed as component properties. See Internationalization for details.
Selector Changes
All buttons have been converted to cosmoz-button components. Update any CSS selectors accordingly:
| Before | After |
| ------------------------------ | ------------------------------------- |
| button.action-open | cosmoz-button[part="action-open"] |
| button.action-reset | cosmoz-button[part="action-reset"] |
| button[part="select-button"] | cosmoz-button[part="select-button"] |
| button[part="cancel-button"] | cosmoz-button[part="cancel-button"] |
| button.btn-ghost | cosmoz-button[variant="link"] |
Slot Changes
| Before | After | Notes |
| --------------- | -------- | ---------------------------------------------------------- |
| button-before | prefix | Now uses default slot content instead of JS-based toggling |
| button-after | suffix | Renamed for consistency |
Development
| Command | Description |
| ------------------------- | -------------------------------------------------- |
| npm start | Start Storybook dev server on port 8000 |
| npm test | Run all tests (unit + storybook interaction tests) |
| npm run test:unit | Run unit tests only |
| npm run test:storybook | Run storybook interaction tests only |
| npm run test:watch | Run tests in watch mode |
| npm run lint | Lint with ESLint |
| npm run build | Compile TypeScript |
| npm run storybook:build | Build static Storybook |
Tests use Vitest with two projects: unit (jsdom) for helper function tests, and storybook (Playwright browser mode) for component interaction tests via Storybook play functions.
