@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.59 → 2.0.0-next.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +5557 -4305
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +1204 -25
  4. package/dist/components/navigation-item/navigation-item.css.js +5 -0
  5. package/dist/components/navigation-item/navigation-item.css.js.map +1 -1
  6. package/dist/components/navigation-item/navigation-item.d.ts +18 -0
  7. package/dist/components/navigation-item/navigation-item.d.ts.map +1 -1
  8. package/dist/components/navigation-item/navigation-item.js +49 -1
  9. package/dist/components/navigation-item/navigation-item.js.map +1 -1
  10. package/dist/components/navigation-item-group/navigation-item-group.css.js +8 -0
  11. package/dist/components/navigation-item-group/navigation-item-group.css.js.map +1 -1
  12. package/dist/components/navigation-item-group/navigation-item-group.d.ts +22 -0
  13. package/dist/components/navigation-item-group/navigation-item-group.d.ts.map +1 -1
  14. package/dist/components/navigation-item-group/navigation-item-group.js +63 -1
  15. package/dist/components/navigation-item-group/navigation-item-group.js.map +1 -1
  16. package/dist/components/navigation-menu/navigation-menu.d.ts +29 -1
  17. package/dist/components/navigation-menu/navigation-menu.d.ts.map +1 -1
  18. package/dist/components/navigation-menu/navigation-menu.js +105 -0
  19. package/dist/components/navigation-menu/navigation-menu.js.map +1 -1
  20. package/dist/components/tree-navigation/tree-navigation.css.js +18 -0
  21. package/dist/components/tree-navigation/tree-navigation.css.js.map +1 -0
  22. package/dist/components/tree-navigation/tree-navigation.d.ts +74 -0
  23. package/dist/components/tree-navigation/tree-navigation.d.ts.map +1 -0
  24. package/dist/components/tree-navigation/tree-navigation.js +120 -0
  25. package/dist/components/tree-navigation/tree-navigation.js.map +1 -0
  26. package/dist/components/tree-navigation-group/tree-navigation-group.css.js +22 -0
  27. package/dist/components/tree-navigation-group/tree-navigation-group.css.js.map +1 -0
  28. package/dist/components/tree-navigation-group/tree-navigation-group.d.ts +94 -0
  29. package/dist/components/tree-navigation-group/tree-navigation-group.d.ts.map +1 -0
  30. package/dist/components/tree-navigation-group/tree-navigation-group.js +116 -0
  31. package/dist/components/tree-navigation-group/tree-navigation-group.js.map +1 -0
  32. package/dist/components/tree-navigation-item/tree-navigation-item.css.js +429 -0
  33. package/dist/components/tree-navigation-item/tree-navigation-item.css.js.map +1 -0
  34. package/dist/components/tree-navigation-item/tree-navigation-item.d.ts +160 -0
  35. package/dist/components/tree-navigation-item/tree-navigation-item.d.ts.map +1 -0
  36. package/dist/components/tree-navigation-item/tree-navigation-item.js +208 -0
  37. package/dist/components/tree-navigation-item/tree-navigation-item.js.map +1 -0
  38. package/dist/internal/tree-roving-navigator.d.ts +71 -0
  39. package/dist/internal/tree-roving-navigator.d.ts.map +1 -0
  40. package/dist/internal/tree-roving-navigator.js +172 -0
  41. package/dist/internal/tree-roving-navigator.js.map +1 -0
  42. package/package.json +1 -1
  43. package/src/components/navigation-item/navigation-item.css +5 -0
  44. package/src/components/navigation-item/navigation-item.ts +56 -1
  45. package/src/components/navigation-item-group/navigation-item-group.css +8 -0
  46. package/src/components/navigation-item-group/navigation-item-group.ts +71 -1
  47. package/src/components/navigation-menu/navigation-menu.stories.ts +54 -0
  48. package/src/components/navigation-menu/navigation-menu.ts +151 -0
  49. package/src/components/tree-navigation/tree-navigation.css +7 -0
  50. package/src/components/tree-navigation/tree-navigation.stories.ts +162 -0
  51. package/src/components/tree-navigation/tree-navigation.ts +188 -0
  52. package/src/components/tree-navigation-group/tree-navigation-group.css +11 -0
  53. package/src/components/tree-navigation-group/tree-navigation-group.ts +157 -0
  54. package/src/components/tree-navigation-item/tree-navigation-item.css +258 -0
  55. package/src/components/tree-navigation-item/tree-navigation-item.stories.ts +119 -0
  56. package/src/components/tree-navigation-item/tree-navigation-item.ts +307 -0
  57. package/src/internal/tree-roving-navigator.ts +231 -0
@@ -0,0 +1,162 @@
1
+ import type {Meta, StoryObj} from '@storybook/web-components-vite';
2
+ import {ObcTreeNavigation} from './tree-navigation.js';
3
+ import './tree-navigation.js';
4
+ import '../tree-navigation-group/tree-navigation-group.js';
5
+ import '../tree-navigation-item/tree-navigation-item.js';
6
+ import {TreeTerminalType} from '../tree-navigation-item/tree-navigation-item.js';
7
+ import {iconIdToIconHtml} from '../../storybook-util.js';
8
+ import {LitElement, html} from 'lit';
9
+ import {state} from 'lit/decorators.js';
10
+ import {customElement} from '../../decorator.js';
11
+
12
+ /**
13
+ * `<obc-tree-navigation>` builds the guide lines automatically from the nested
14
+ * markup. Write groups and items as real nested elements; the container assigns
15
+ * each row's `branches` from its position. Click a group header to expand or
16
+ * collapse — the guides recompute to match.
17
+ */
18
+ const meta: Meta<typeof ObcTreeNavigation> = {
19
+ title: 'UI Components/Menus and Navigation/Tree Navigation',
20
+ tags: ['autodocs', '6.0'],
21
+ component: 'obc-tree-navigation',
22
+ render: () => html`
23
+ <obc-tree-navigation>
24
+ <obc-tree-navigation-group label="Vessel" expanded>
25
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
26
+ <obc-tree-navigation-group
27
+ label="Engine room"
28
+ expanded
29
+ .terminalType=${TreeTerminalType.aggregatedHeader}
30
+ >
31
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
32
+ <obc-tree-navigation-item label="Main engine" checked>
33
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
34
+ </obc-tree-navigation-item>
35
+ <obc-tree-navigation-group
36
+ label="Auxiliary engine"
37
+ expanded
38
+ hasAlertBadge
39
+ .alertCount=${3}
40
+ >
41
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
42
+ <obc-tree-navigation-item label="Fuel pump">
43
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
44
+ </obc-tree-navigation-item>
45
+ <obc-tree-navigation-item
46
+ label="Starter"
47
+ hasAlertBadge
48
+ .alertCount=${1}
49
+ >
50
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
51
+ </obc-tree-navigation-item>
52
+ <obc-tree-navigation-item label="Oil filter">
53
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
54
+ </obc-tree-navigation-item>
55
+ </obc-tree-navigation-group>
56
+ <obc-tree-navigation-group label="Cooling system" expanded>
57
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
58
+ <obc-tree-navigation-item label="Seawater pump">
59
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
60
+ </obc-tree-navigation-item>
61
+ </obc-tree-navigation-group>
62
+ </obc-tree-navigation-group>
63
+ <obc-tree-navigation-group label="Bridge" expanded>
64
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
65
+ <obc-tree-navigation-item label="Radar">
66
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
67
+ </obc-tree-navigation-item>
68
+ <obc-tree-navigation-item label="Autopilot" disabled>
69
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
70
+ </obc-tree-navigation-item>
71
+ <obc-tree-navigation-item label="ECDIS">
72
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
73
+ </obc-tree-navigation-item>
74
+ </obc-tree-navigation-group>
75
+ <obc-tree-navigation-item label="Deck">
76
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
77
+ </obc-tree-navigation-item>
78
+ </obc-tree-navigation-group>
79
+ <obc-tree-navigation-item label="Shore power">
80
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
81
+ </obc-tree-navigation-item>
82
+ </obc-tree-navigation>
83
+ `,
84
+ } satisfies Meta<ObcTreeNavigation>;
85
+
86
+ export default meta;
87
+ type Story = StoryObj<ObcTreeNavigation>;
88
+
89
+ /**
90
+ * A fully expanded tree. The guide lines — pass-through verticals, intersections,
91
+ * and last-child corners — are all computed by the container from the nested
92
+ * markup. The header rows are groups; the leaves are items.
93
+ */
94
+ export const Default: Story = {};
95
+
96
+ /**
97
+ * `<tree-navigation-selection-demo>` – Wraps the tree to track which leaf is
98
+ * selected. Selection is purely consumer state: it sets `checked` on the chosen
99
+ * item; the container still owns all guide-line logic.
100
+ *
101
+ * Not exported: Storybook treats every module export as a story, so the demo
102
+ * element is kept file-local and registered only via the `@customElement` side
103
+ * effect.
104
+ */
105
+ @customElement('tree-navigation-selection-demo')
106
+ class TreeNavigationSelectionDemo extends LitElement {
107
+ @state() private selectedLabel = 'Main engine';
108
+
109
+ override createRenderRoot() {
110
+ return this;
111
+ }
112
+
113
+ private select(label: string) {
114
+ this.selectedLabel = label;
115
+ }
116
+
117
+ private leaf(label: string) {
118
+ return html`<obc-tree-navigation-item
119
+ label=${label}
120
+ ?checked=${this.selectedLabel === label}
121
+ @click=${() => this.select(label)}
122
+ >
123
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
124
+ </obc-tree-navigation-item>`;
125
+ }
126
+
127
+ override render() {
128
+ return html`
129
+ <obc-tree-navigation>
130
+ <obc-tree-navigation-group label="Vessel" expanded>
131
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
132
+ <obc-tree-navigation-group label="Engine room" expanded>
133
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
134
+ ${this.leaf('Main engine')} ${this.leaf('Auxiliary engine')}
135
+ ${this.leaf('Cooling system')}
136
+ </obc-tree-navigation-group>
137
+ <obc-tree-navigation-group label="Bridge" expanded>
138
+ ${iconIdToIconHtml('placeholder', {slot: 'icon'})}
139
+ ${this.leaf('Radar')} ${this.leaf('Autopilot')}
140
+ </obc-tree-navigation-group>
141
+ ${this.leaf('Deck')}
142
+ </obc-tree-navigation-group>
143
+ </obc-tree-navigation>
144
+ `;
145
+ }
146
+ }
147
+
148
+ declare global {
149
+ interface HTMLElementTagNameMap {
150
+ 'tree-navigation-selection-demo': TreeNavigationSelectionDemo;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * Click a leaf to select it; click a group chevron to expand or collapse. The
156
+ * container keeps the guide lines correct through every change — the consumer
157
+ * only tracks the selected item.
158
+ */
159
+ export const Interactive: Story = {
160
+ render: () =>
161
+ html`<tree-navigation-selection-demo></tree-navigation-selection-demo>`,
162
+ };
@@ -0,0 +1,188 @@
1
+ import {LitElement, html, unsafeCSS} from 'lit';
2
+ import componentStyle from './tree-navigation.css?inline';
3
+ import {customElement} from '../../decorator.js';
4
+ import {
5
+ ObcTreeNavigationItem,
6
+ TreeBranchType,
7
+ } from '../tree-navigation-item/tree-navigation-item.js';
8
+ import {ObcTreeNavigationGroup} from '../tree-navigation-group/tree-navigation-group.js';
9
+ import '../tree-navigation-group/tree-navigation-group.js';
10
+ import {
11
+ TreeRovingNavigator,
12
+ TreeRovingAdapter,
13
+ } from '../../internal/tree-roving-navigator.js';
14
+
15
+ /** A tree row is either a leaf item or an expandable group. */
16
+ type TreeRow = ObcTreeNavigationItem | ObcTreeNavigationGroup;
17
+
18
+ const ITEM_TAG = 'obc-tree-navigation-item';
19
+ const GROUP_TAG = 'obc-tree-navigation-group';
20
+
21
+ function isGroup(el: Element): el is ObcTreeNavigationGroup {
22
+ return el.tagName.toLowerCase() === GROUP_TAG;
23
+ }
24
+
25
+ function isRow(el: Element): el is TreeRow {
26
+ const tag = el.tagName.toLowerCase();
27
+ return tag === ITEM_TAG || tag === GROUP_TAG;
28
+ }
29
+
30
+ /**
31
+ * `<obc-tree-navigation>` – The container for a tree- or file-explorer-style
32
+ * navigation list. Holds nested `<obc-tree-navigation-group>` and
33
+ * `<obc-tree-navigation-item>` rows and computes each row's indentation guide
34
+ * lines automatically from its position in the hierarchy.
35
+ *
36
+ * Consumers write the tree as plain nested markup; the container assigns every
37
+ * row's `branches` array so the guide lines (pass-through verticals, elbows, and
38
+ * the last-child corner) connect correctly — there is no need to compute depth or
39
+ * branch types by hand. It re-derives the guides whenever rows are added, removed,
40
+ * or groups expand and collapse.
41
+ *
42
+ * ## Features
43
+ * - **Automatic guide lines:** Each row receives a `branches` array derived from
44
+ * its depth and whether each ancestor still has siblings below it. Last children
45
+ * get a corner (`└`); rows with siblings below get an intersection (`├`); and
46
+ * ancestor columns become a pass-through (`│`) or blank as appropriate.
47
+ * - **Live updates:** Reacts to slotted content changes and to group expand state,
48
+ * recomputing the guides so they always match the visible structure.
49
+ * - **Composable:** Works with any nesting depth of groups and items.
50
+ *
51
+ * ## Usage Guidelines
52
+ * - Use as the root of a hierarchical navigation list. For flat navigation, use
53
+ * `obc-navigation-menu` instead.
54
+ * - Place `<obc-tree-navigation-group>` for expandable parents and
55
+ * `<obc-tree-navigation-item>` for leaves; nest groups to any depth.
56
+ * - Do not set each row's `branches` manually — the container manages them.
57
+ *
58
+ * ## Slots
59
+ *
60
+ * | Slot Name | Renders When... | Purpose |
61
+ * |-----------|-----------------|----------------------------------------------------|
62
+ * | (default) | Always | Top-level rows (`obc-tree-navigation-group`/`-item`). |
63
+ *
64
+ * @slot - Top-level tree rows (groups and items).
65
+ */
66
+ @customElement('obc-tree-navigation')
67
+ export class ObcTreeNavigation extends LitElement {
68
+ private mutationObserver?: MutationObserver;
69
+
70
+ /** The focusable header item for a row: a leaf is itself; a group's is its shadow header. */
71
+ private innerItem(row: TreeRow): ObcTreeNavigationItem | null {
72
+ return isGroup(row)
73
+ ? (row.shadowRoot?.querySelector<ObcTreeNavigationItem>(ITEM_TAG) ?? null)
74
+ : row;
75
+ }
76
+
77
+ private readonly navigator = new TreeRovingNavigator<TreeRow>(this, {
78
+ getRows: () => this.childRows(this),
79
+ childRows: (row) => this.childRows(row),
80
+ isGroup: (row) => isGroup(row),
81
+ isExpanded: (row) => isGroup(row) && row.hasAttribute('expanded'),
82
+ setExpanded: (row, expanded) => {
83
+ if (isGroup(row)) row.expanded = expanded;
84
+ },
85
+ innerItem: (row) => this.innerItem(row),
86
+ isDisabled: (row) => this.innerItem(row)?.hasAttribute('disabled') ?? false,
87
+ } satisfies TreeRovingAdapter<TreeRow>);
88
+
89
+ override connectedCallback(): void {
90
+ super.connectedCallback();
91
+ // Recompute guides on any structural change: rows added/removed, or a group's
92
+ // `expanded` attribute toggled anywhere in the subtree.
93
+ this.mutationObserver = new MutationObserver(() => this.updateBranches());
94
+ this.mutationObserver.observe(this, {
95
+ childList: true,
96
+ subtree: true,
97
+ attributes: true,
98
+ attributeFilter: ['expanded'],
99
+ });
100
+ this.addEventListener('expand-toggle', this.onExpandToggle);
101
+ this.addEventListener('keydown', this.onKeydown);
102
+ }
103
+
104
+ private onKeydown = (event: KeyboardEvent): void => {
105
+ if (this.navigator.handleKeydown(event)) event.preventDefault();
106
+ };
107
+
108
+ override disconnectedCallback(): void {
109
+ super.disconnectedCallback();
110
+ this.mutationObserver?.disconnect();
111
+ this.mutationObserver = undefined;
112
+ this.removeEventListener('expand-toggle', this.onExpandToggle);
113
+ this.removeEventListener('keydown', this.onKeydown);
114
+ }
115
+
116
+ private onExpandToggle = () => {
117
+ // Defer so the group's `expanded` attribute and slotted children reflect the
118
+ // new state before guides are recomputed.
119
+ queueMicrotask(() => this.updateBranches());
120
+ };
121
+
122
+ override firstUpdated(): void {
123
+ this.updateBranches();
124
+ }
125
+
126
+ /** Direct tree-row children of an element, in document order. */
127
+ private childRows(el: Element): TreeRow[] {
128
+ return Array.from(el.children).filter(isRow);
129
+ }
130
+
131
+ /**
132
+ * Walk the row tree and assign each row's `branches`. Mirrors the guide-line
133
+ * model: a row gets one column per ancestor (pass-through `straight` if that
134
+ * ancestor still has siblings below, else `blank`) followed by its own elbow
135
+ * (`corner` if it is the last child, else `intersection`). Top-level rows draw
136
+ * no columns at all.
137
+ *
138
+ * @param rows - sibling rows at this level, in order
139
+ * @param ancestorHasNextSibling - one flag per ancestor *below the root*: true
140
+ * if that ancestor has a sibling still below it (so its column is a
141
+ * pass-through), false if its subtree has ended (a blank column)
142
+ * @param depth - nesting depth; 0 for top-level rows (which draw no columns)
143
+ */
144
+ private assignBranches(
145
+ rows: TreeRow[],
146
+ ancestorHasNextSibling: boolean[],
147
+ depth: number
148
+ ): void {
149
+ rows.forEach((row, index) => {
150
+ const isLast = index === rows.length - 1;
151
+ row.branches =
152
+ depth === 0
153
+ ? []
154
+ : [
155
+ ...ancestorHasNextSibling.map((hasNext) =>
156
+ hasNext ? TreeBranchType.straight : TreeBranchType.blank
157
+ ),
158
+ isLast ? TreeBranchType.corner : TreeBranchType.intersection,
159
+ ];
160
+
161
+ if (isGroup(row)) {
162
+ const childAncestry =
163
+ depth === 0 ? [] : [...ancestorHasNextSibling, !isLast];
164
+ this.assignBranches(this.childRows(row), childAncestry, depth + 1);
165
+ }
166
+ });
167
+ }
168
+
169
+ private updateBranches(): void {
170
+ this.assignBranches(this.childRows(this), [], 0);
171
+ // Keep the roving tabindex in lockstep with the visible structure.
172
+ this.navigator.refresh();
173
+ }
174
+
175
+ override render() {
176
+ return html`<div role="tree">
177
+ <slot @slotchange=${() => this.updateBranches()}></slot>
178
+ </div>`;
179
+ }
180
+
181
+ static override styles = unsafeCSS(componentStyle);
182
+ }
183
+
184
+ declare global {
185
+ interface HTMLElementTagNameMap {
186
+ 'obc-tree-navigation': ObcTreeNavigation;
187
+ }
188
+ }
@@ -0,0 +1,11 @@
1
+ :host {
2
+ display: block;
3
+ }
4
+
5
+ [part="children"] {
6
+ display: block;
7
+ }
8
+
9
+ [part="children"][hidden] {
10
+ display: none;
11
+ }
@@ -0,0 +1,157 @@
1
+ import {LitElement, html, nothing, unsafeCSS} from 'lit';
2
+ import {property, query} from 'lit/decorators.js';
3
+ import componentStyle from './tree-navigation-group.css?inline';
4
+ import {customElement} from '../../decorator.js';
5
+ import {
6
+ ObcTreeNavigationItem,
7
+ TreeBranchType,
8
+ TreeTerminalType,
9
+ } from '../tree-navigation-item/tree-navigation-item.js';
10
+ import '../tree-navigation-item/tree-navigation-item.js';
11
+ import {BadgeType} from '../badge/badge.js';
12
+
13
+ /**
14
+ * `<obc-tree-navigation-group>` – An expandable parent row in a tree-navigation
15
+ * structure: a header row with an expand/collapse chevron, plus a slot for the
16
+ * child rows it discloses.
17
+ *
18
+ * A group renders an `<obc-tree-navigation-item>` header (carrying its own label,
19
+ * icon, terminal type, and alert badge) followed by its slotted children. When
20
+ * placed inside `<obc-tree-navigation>`, the container computes and assigns the
21
+ * `branches` guide lines for every row automatically from each row's position —
22
+ * a group does not need its depth configured by hand.
23
+ *
24
+ * The group manages only its own open/closed state and forwards header presentation
25
+ * to its internal header item. It does not draw guide lines itself; that is the
26
+ * container's responsibility (see `<obc-tree-navigation>`).
27
+ *
28
+ * ## Features
29
+ * - **Disclosure:** A chevron in the header toggles the slotted children. The open
30
+ * state is held in `expanded` and reflected so the container and CSS can react.
31
+ * - **Header presentation:** `label`, the `icon` slot, `terminalType`, and the
32
+ * alert badge (`hasAlertBadge`, `alertCount`, `alertType`) are forwarded to the
33
+ * header row.
34
+ * - **Selection:** `checked` marks the group's header as the current item.
35
+ * - **Automatic guides:** Inside `<obc-tree-navigation>`, the header's `branches`
36
+ * are assigned by the container; nested groups continue the guide columns down.
37
+ *
38
+ * ## Usage Guidelines
39
+ * - Nest `<obc-tree-navigation-item>` (leaves) and further `<obc-tree-navigation-group>`
40
+ * elements as children to build the hierarchy.
41
+ * - Always place groups and items inside an `<obc-tree-navigation>` container so the
42
+ * guide lines are computed; using a group standalone draws a header with no guides.
43
+ * - Provide a header icon via the `icon` slot (forwarded to the header row).
44
+ *
45
+ * ## Slots
46
+ *
47
+ * | Slot Name | Renders When... | Purpose |
48
+ * |-----------|--------------------------|--------------------------------------------------------------|
49
+ * | icon | `hasIcon` is true | Leading icon for the group header row. |
50
+ * | (default) | Always | Child rows (`obc-tree-navigation-item` / `-group`). |
51
+ *
52
+ * @slot icon - Leading icon for the group header (shown when `hasIcon` is true).
53
+ * @slot - Child rows disclosed when the group is expanded.
54
+ * @fires expand-toggle {CustomEvent<boolean>} Fired when the header is activated; detail is the next `expanded` value.
55
+ */
56
+ @customElement('obc-tree-navigation-group')
57
+ export class ObcTreeNavigationGroup extends LitElement {
58
+ /** The text label displayed for the group header row. */
59
+ @property({type: String}) label = 'Group';
60
+
61
+ /**
62
+ * Guide line to draw for each ancestor level of the header row. Normally set by
63
+ * the parent `<obc-tree-navigation>` container from the group's position; only
64
+ * set it manually when using a group outside the container.
65
+ */
66
+ @property({type: Array}) branches: TreeBranchType[] = [];
67
+
68
+ /** Whether the group is expanded, disclosing its children. Rotates the chevron. */
69
+ @property({type: Boolean, reflect: true}) expanded = false;
70
+
71
+ /** Whether the group header is the current selection. */
72
+ @property({type: Boolean, reflect: true}) checked = false;
73
+
74
+ /** Disables the group header, removing it from the tab order and dimming it. */
75
+ @property({type: Boolean, reflect: true}) disabled = false;
76
+
77
+ /** Whether the group header shows a leading icon (provided via the `icon` slot). */
78
+ @property({type: Boolean, attribute: false}) hasIcon = true;
79
+
80
+ /**
81
+ * Terminal type for the header row, controlling the alert-header marker shown in
82
+ * the terminal. One of `regular` (default), `aggregated-header`, or `group-header`.
83
+ */
84
+ @property({type: String}) terminalType: string = TreeTerminalType.regular;
85
+
86
+ /** Whether a trailing alert counter badge is shown on the header row. */
87
+ @property({type: Boolean}) hasAlertBadge = false;
88
+
89
+ /** The number shown in the header's alert badge when `hasAlertBadge` is true. */
90
+ @property({type: Number}) alertCount = 0;
91
+
92
+ /** The severity/type of the header's alert badge. One of the `obc-badge` types (default `alarm`). */
93
+ @property({type: String}) alertType: string = BadgeType.alarm;
94
+
95
+ /**
96
+ * The URL to navigate to when the header is activated. If set, the header row
97
+ * renders as a link; otherwise it acts as a button.
98
+ */
99
+ @property({type: String}) href: string | undefined;
100
+
101
+ @query('obc-tree-navigation-item') private headerItem?: ObcTreeNavigationItem;
102
+
103
+ private onHeaderToggle(event: CustomEvent<boolean>) {
104
+ this.expanded = event.detail;
105
+ this.dispatchEvent(
106
+ new CustomEvent<boolean>('expand-toggle', {detail: this.expanded})
107
+ );
108
+ }
109
+
110
+ /** Closes the group (and, recursively, any nested groups inside it). */
111
+ close() {
112
+ this.expanded = false;
113
+ this.querySelectorAll('obc-tree-navigation-group').forEach((group) =>
114
+ group.close()
115
+ );
116
+ }
117
+
118
+ public override focus(options?: FocusOptions): void {
119
+ this.headerItem?.focus(options);
120
+ }
121
+
122
+ override render() {
123
+ return html`
124
+ <obc-tree-navigation-item
125
+ part="header"
126
+ .label=${this.label}
127
+ .branches=${this.branches}
128
+ expandable
129
+ ?expanded=${this.expanded}
130
+ ?checked=${this.checked}
131
+ ?disabled=${this.disabled}
132
+ .hasLeadingIcon=${this.hasIcon}
133
+ .terminalType=${this.terminalType}
134
+ ?hasAlertBadge=${this.hasAlertBadge}
135
+ .alertCount=${this.alertCount}
136
+ .alertType=${this.alertType}
137
+ .href=${this.href}
138
+ @expand-toggle=${this.onHeaderToggle}
139
+ >
140
+ ${this.hasIcon ? html`<slot name="icon" slot="icon"></slot>` : nothing}
141
+ </obc-tree-navigation-item>
142
+ <div part="children" role="group" ?hidden=${!this.expanded}>
143
+ <slot></slot>
144
+ </div>
145
+ `;
146
+ }
147
+
148
+ static override styles = unsafeCSS(componentStyle);
149
+ }
150
+
151
+ export type ObcTreeNavigationGroupExpandToggleEvent = CustomEvent<boolean>;
152
+
153
+ declare global {
154
+ interface HTMLElementTagNameMap {
155
+ 'obc-tree-navigation-group': ObcTreeNavigationGroup;
156
+ }
157
+ }