@oicl/openbridge-webcomponents-full-bundle 2.0.0-next.58 → 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 (213) hide show
  1. package/bundle/openbridge-webcomponents.bundle.js +8144 -5241
  2. package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
  3. package/custom-elements.json +1937 -78
  4. package/dist/alert-severity.d.ts +38 -0
  5. package/dist/alert-severity.d.ts.map +1 -0
  6. package/dist/alert-severity.js +176 -0
  7. package/dist/alert-severity.js.map +1 -0
  8. package/dist/ar/building-blocks/poi-header/poi-header.css.js +38 -7
  9. package/dist/ar/building-blocks/poi-header/poi-header.css.js.map +1 -1
  10. package/dist/ar/building-blocks/poi-header/poi-header.d.ts +7 -2
  11. package/dist/ar/building-blocks/poi-header/poi-header.d.ts.map +1 -1
  12. package/dist/ar/building-blocks/poi-header/poi-header.js +5 -0
  13. package/dist/ar/building-blocks/poi-header/poi-header.js.map +1 -1
  14. package/dist/ar/poi-button/poi-button.css.js +110 -32
  15. package/dist/ar/poi-button/poi-button.css.js.map +1 -1
  16. package/dist/ar/poi-button/poi-button.d.ts +7 -2
  17. package/dist/ar/poi-button/poi-button.d.ts.map +1 -1
  18. package/dist/ar/poi-button/poi-button.js +15 -0
  19. package/dist/ar/poi-button/poi-button.js.map +1 -1
  20. package/dist/ar/types.d.ts +6 -1
  21. package/dist/ar/types.d.ts.map +1 -1
  22. package/dist/ar/types.js +5 -0
  23. package/dist/ar/types.js.map +1 -1
  24. package/dist/automation/automation-badge/automation-badge.d.ts +9 -1
  25. package/dist/automation/automation-badge/automation-badge.d.ts.map +1 -1
  26. package/dist/automation/automation-badge/automation-badge.js +119 -12
  27. package/dist/automation/automation-badge/automation-badge.js.map +1 -1
  28. package/dist/automation/automation-button/abstract-automation-button-storybook-helpers.d.ts +25 -1
  29. package/dist/automation/automation-button/abstract-automation-button-storybook-helpers.d.ts.map +1 -1
  30. package/dist/automation/automation-button/abstract-automation-button-storybook-helpers.js +7 -2
  31. package/dist/automation/automation-button/abstract-automation-button-storybook-helpers.js.map +1 -1
  32. package/dist/automation/automation-button/abstract-automation-button.d.ts +10 -3
  33. package/dist/automation/automation-button/abstract-automation-button.d.ts.map +1 -1
  34. package/dist/automation/automation-button/abstract-automation-button.js +23 -2
  35. package/dist/automation/automation-button/abstract-automation-button.js.map +1 -1
  36. package/dist/automation/automation-button/automation-button.css.js +9 -4
  37. package/dist/automation/automation-button/automation-button.css.js.map +1 -1
  38. package/dist/automation/automation-button/automation-button.d.ts +4 -2
  39. package/dist/automation/automation-button/automation-button.d.ts.map +1 -1
  40. package/dist/automation/automation-button/automation-button.js +24 -6
  41. package/dist/automation/automation-button/automation-button.js.map +1 -1
  42. package/dist/automation/automation-tank/automation-tank.d.ts +3 -2
  43. package/dist/automation/automation-tank/automation-tank.d.ts.map +1 -1
  44. package/dist/automation/automation-tank/automation-tank.js +13 -2
  45. package/dist/automation/automation-tank/automation-tank.js.map +1 -1
  46. package/dist/components/accordion-card/accordion-card.css.js +34 -0
  47. package/dist/components/accordion-card/accordion-card.css.js.map +1 -1
  48. package/dist/components/accordion-card/accordion-card.d.ts +3 -2
  49. package/dist/components/accordion-card/accordion-card.d.ts.map +1 -1
  50. package/dist/components/accordion-card/accordion-card.js +4 -3
  51. package/dist/components/accordion-card/accordion-card.js.map +1 -1
  52. package/dist/components/alert-button/alert-button.css.js +744 -7
  53. package/dist/components/alert-button/alert-button.css.js.map +1 -1
  54. package/dist/components/alert-button/alert-button.d.ts +3 -1
  55. package/dist/components/alert-button/alert-button.d.ts.map +1 -1
  56. package/dist/components/alert-button/alert-button.js +33 -31
  57. package/dist/components/alert-button/alert-button.js.map +1 -1
  58. package/dist/components/alert-frame/alert-frame.css.js +116 -29
  59. package/dist/components/alert-frame/alert-frame.css.js.map +1 -1
  60. package/dist/components/alert-frame/alert-frame.d.ts +42 -15
  61. package/dist/components/alert-frame/alert-frame.d.ts.map +1 -1
  62. package/dist/components/alert-frame/alert-frame.js +68 -17
  63. package/dist/components/alert-frame/alert-frame.js.map +1 -1
  64. package/dist/components/alert-frame/critical-badge.d.ts +12 -0
  65. package/dist/components/alert-frame/critical-badge.d.ts.map +1 -0
  66. package/dist/components/alert-frame/critical-badge.js +47 -0
  67. package/dist/components/alert-frame/critical-badge.js.map +1 -0
  68. package/dist/components/alert-frame/diagnostic-badge.d.ts +12 -0
  69. package/dist/components/alert-frame/diagnostic-badge.d.ts.map +1 -0
  70. package/dist/components/alert-frame/diagnostic-badge.js +60 -0
  71. package/dist/components/alert-frame/diagnostic-badge.js.map +1 -0
  72. package/dist/components/alert-icon/alert-icon.d.ts +6 -1
  73. package/dist/components/alert-icon/alert-icon.d.ts.map +1 -1
  74. package/dist/components/alert-icon/alert-icon.js +96 -38
  75. package/dist/components/alert-icon/alert-icon.js.map +1 -1
  76. package/dist/components/alert-icon/icons/icon-critical.d.ts +3 -0
  77. package/dist/components/alert-icon/icons/icon-critical.d.ts.map +1 -0
  78. package/dist/components/alert-icon/icons/icon-critical.js +26 -0
  79. package/dist/components/alert-icon/icons/icon-critical.js.map +1 -0
  80. package/dist/components/alert-list-details/alert-list-details.d.ts.map +1 -1
  81. package/dist/components/alert-list-details/alert-list-details.js +6 -7
  82. package/dist/components/alert-list-details/alert-list-details.js.map +1 -1
  83. package/dist/components/badge/badge.css.js +50 -2
  84. package/dist/components/badge/badge.css.js.map +1 -1
  85. package/dist/components/badge/badge.d.ts +13 -2
  86. package/dist/components/badge/badge.d.ts.map +1 -1
  87. package/dist/components/badge/badge.js +54 -0
  88. package/dist/components/badge/badge.js.map +1 -1
  89. package/dist/components/navigation-item/navigation-item.css.js +5 -0
  90. package/dist/components/navigation-item/navigation-item.css.js.map +1 -1
  91. package/dist/components/navigation-item/navigation-item.d.ts +18 -0
  92. package/dist/components/navigation-item/navigation-item.d.ts.map +1 -1
  93. package/dist/components/navigation-item/navigation-item.js +49 -1
  94. package/dist/components/navigation-item/navigation-item.js.map +1 -1
  95. package/dist/components/navigation-item-group/navigation-item-group.css.js +8 -0
  96. package/dist/components/navigation-item-group/navigation-item-group.css.js.map +1 -1
  97. package/dist/components/navigation-item-group/navigation-item-group.d.ts +22 -0
  98. package/dist/components/navigation-item-group/navigation-item-group.d.ts.map +1 -1
  99. package/dist/components/navigation-item-group/navigation-item-group.js +63 -1
  100. package/dist/components/navigation-item-group/navigation-item-group.js.map +1 -1
  101. package/dist/components/navigation-menu/navigation-menu.d.ts +29 -1
  102. package/dist/components/navigation-menu/navigation-menu.d.ts.map +1 -1
  103. package/dist/components/navigation-menu/navigation-menu.js +105 -0
  104. package/dist/components/navigation-menu/navigation-menu.js.map +1 -1
  105. package/dist/components/tree-navigation/tree-navigation.css.js +18 -0
  106. package/dist/components/tree-navigation/tree-navigation.css.js.map +1 -0
  107. package/dist/components/tree-navigation/tree-navigation.d.ts +74 -0
  108. package/dist/components/tree-navigation/tree-navigation.d.ts.map +1 -0
  109. package/dist/components/tree-navigation/tree-navigation.js +120 -0
  110. package/dist/components/tree-navigation/tree-navigation.js.map +1 -0
  111. package/dist/components/tree-navigation-group/tree-navigation-group.css.js +22 -0
  112. package/dist/components/tree-navigation-group/tree-navigation-group.css.js.map +1 -0
  113. package/dist/components/tree-navigation-group/tree-navigation-group.d.ts +94 -0
  114. package/dist/components/tree-navigation-group/tree-navigation-group.d.ts.map +1 -0
  115. package/dist/components/tree-navigation-group/tree-navigation-group.js +116 -0
  116. package/dist/components/tree-navigation-group/tree-navigation-group.js.map +1 -0
  117. package/dist/components/tree-navigation-item/tree-navigation-item.css.js +429 -0
  118. package/dist/components/tree-navigation-item/tree-navigation-item.css.js.map +1 -0
  119. package/dist/components/tree-navigation-item/tree-navigation-item.d.ts +160 -0
  120. package/dist/components/tree-navigation-item/tree-navigation-item.d.ts.map +1 -0
  121. package/dist/components/tree-navigation-item/tree-navigation-item.js +208 -0
  122. package/dist/components/tree-navigation-item/tree-navigation-item.js.map +1 -0
  123. package/dist/index.js +2 -1
  124. package/dist/internal/tree-roving-navigator.d.ts +71 -0
  125. package/dist/internal/tree-roving-navigator.d.ts.map +1 -0
  126. package/dist/internal/tree-roving-navigator.js +172 -0
  127. package/dist/internal/tree-roving-navigator.js.map +1 -0
  128. package/dist/manual-icon/icon-alerts-critical-twotone.d.ts +30 -0
  129. package/dist/manual-icon/icon-alerts-critical-twotone.d.ts.map +1 -0
  130. package/dist/manual-icon/icon-alerts-critical-twotone.js +55 -0
  131. package/dist/manual-icon/icon-alerts-critical-twotone.js.map +1 -0
  132. package/dist/manual-icon/icon-alerts-diagnostic-twotone.d.ts +30 -0
  133. package/dist/manual-icon/icon-alerts-diagnostic-twotone.d.ts.map +1 -0
  134. package/dist/manual-icon/icon-alerts-diagnostic-twotone.js +55 -0
  135. package/dist/manual-icon/icon-alerts-diagnostic-twotone.js.map +1 -0
  136. package/dist/navigation-instruments/readout/readout.css.js +3 -18
  137. package/dist/navigation-instruments/readout/readout.css.js.map +1 -1
  138. package/dist/navigation-instruments/readout/readout.d.ts +6 -7
  139. package/dist/navigation-instruments/readout/readout.d.ts.map +1 -1
  140. package/dist/navigation-instruments/readout/readout.js +54 -52
  141. package/dist/navigation-instruments/readout/readout.js.map +1 -1
  142. package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js +9 -36
  143. package/dist/navigation-instruments/readout-list-item/readout-list-item.css.js.map +1 -1
  144. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts +8 -8
  145. package/dist/navigation-instruments/readout-list-item/readout-list-item.d.ts.map +1 -1
  146. package/dist/navigation-instruments/readout-list-item/readout-list-item.js +49 -42
  147. package/dist/navigation-instruments/readout-list-item/readout-list-item.js.map +1 -1
  148. package/dist/openbridge.css +155 -0
  149. package/dist/types.d.ts +7 -1
  150. package/dist/types.d.ts.map +1 -1
  151. package/dist/types.js +18 -7
  152. package/dist/types.js.map +1 -1
  153. package/package.json +1 -1
  154. package/src/alert-severity.ts +225 -0
  155. package/src/ar/building-blocks/poi-header/poi-header.css +38 -7
  156. package/src/ar/building-blocks/poi-header/poi-header.ts +6 -1
  157. package/src/ar/poi-button/poi-button.css +126 -32
  158. package/src/ar/poi-button/poi-button.ts +16 -1
  159. package/src/ar/types.ts +5 -0
  160. package/src/automation/automation-badge/automation-badge.ts +124 -17
  161. package/src/automation/automation-button/abstract-automation-button-storybook-helpers.ts +5 -0
  162. package/src/automation/automation-button/abstract-automation-button.ts +21 -3
  163. package/src/automation/automation-button/automation-button.css +9 -4
  164. package/src/automation/automation-button/automation-button.ts +25 -7
  165. package/src/automation/automation-tank/automation-tank.stories.ts +22 -0
  166. package/src/automation/automation-tank/automation-tank.ts +12 -3
  167. package/src/components/accordion-card/accordion-card.css +33 -0
  168. package/src/components/accordion-card/accordion-card.stories.ts +2 -1
  169. package/src/components/accordion-card/accordion-card.ts +3 -4
  170. package/src/components/alert-button/alert-button.css +89 -5
  171. package/src/components/alert-button/alert-button.stories.ts +43 -0
  172. package/src/components/alert-button/alert-button.ts +43 -32
  173. package/src/components/alert-frame/alert-frame.css +116 -29
  174. package/src/components/alert-frame/alert-frame.stories.ts +63 -3
  175. package/src/components/alert-frame/alert-frame.ts +114 -23
  176. package/src/components/alert-frame/critical-badge.ts +39 -0
  177. package/src/components/alert-frame/diagnostic-badge.ts +52 -0
  178. package/src/components/alert-icon/alert-icon.stories.ts +41 -0
  179. package/src/components/alert-icon/alert-icon.ts +101 -38
  180. package/src/components/alert-icon/icons/icon-critical.ts +23 -0
  181. package/src/components/alert-list-details/alert-list-details.stories.ts +67 -0
  182. package/src/components/alert-list-details/alert-list-details.ts +11 -10
  183. package/src/components/badge/badge.css +52 -2
  184. package/src/components/badge/badge.stories.ts +75 -0
  185. package/src/components/badge/badge.ts +70 -2
  186. package/src/components/navigation-item/navigation-item.css +5 -0
  187. package/src/components/navigation-item/navigation-item.ts +56 -1
  188. package/src/components/navigation-item-group/navigation-item-group.css +8 -0
  189. package/src/components/navigation-item-group/navigation-item-group.ts +71 -1
  190. package/src/components/navigation-menu/navigation-menu.stories.ts +54 -0
  191. package/src/components/navigation-menu/navigation-menu.ts +151 -0
  192. package/src/components/tree-navigation/tree-navigation.css +7 -0
  193. package/src/components/tree-navigation/tree-navigation.stories.ts +162 -0
  194. package/src/components/tree-navigation/tree-navigation.ts +188 -0
  195. package/src/components/tree-navigation-group/tree-navigation-group.css +11 -0
  196. package/src/components/tree-navigation-group/tree-navigation-group.ts +157 -0
  197. package/src/components/tree-navigation-item/tree-navigation-item.css +258 -0
  198. package/src/components/tree-navigation-item/tree-navigation-item.stories.ts +119 -0
  199. package/src/components/tree-navigation-item/tree-navigation-item.ts +307 -0
  200. package/src/internal/tree-roving-navigator.ts +231 -0
  201. package/src/main.css +1 -0
  202. package/src/manual-icon/icon-alerts-critical-twotone.ts +60 -0
  203. package/src/manual-icon/icon-alerts-diagnostic-twotone.ts +60 -0
  204. package/src/mixins/alert.css +29 -0
  205. package/src/navigation-instruments/readout/readout.css +3 -24
  206. package/src/navigation-instruments/readout/readout.stories.ts +57 -4
  207. package/src/navigation-instruments/readout/readout.ts +69 -63
  208. package/src/navigation-instruments/readout-list-item/readout-list-item.css +9 -42
  209. package/src/navigation-instruments/readout-list-item/readout-list-item.stories.ts +57 -14
  210. package/src/navigation-instruments/readout-list-item/readout-list-item.ts +54 -44
  211. package/src/palettes/critical.md +95 -0
  212. package/src/palettes/manual.css +165 -0
  213. package/src/types.ts +20 -3
@@ -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
+ }
@@ -0,0 +1,258 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ :host {
6
+ display: block;
7
+ }
8
+
9
+ .wrapper {
10
+ display: flex;
11
+ align-items: center;
12
+ width: 100%;
13
+ height: var(--menu-navigation-components-tree-navigation-item-touch-target);
14
+ user-select: none;
15
+
16
+ @mixin outline-inward;
17
+ @mixin style style=flat visibleWrapperClass=.visible-wrapper;
18
+ @mixin font-body;
19
+ color: var(--on-flat-active-color);
20
+
21
+ .visible-wrapper {
22
+ display: flex;
23
+ align-items: center;
24
+ width: 100%;
25
+ height: 100%;
26
+ border-radius: var(--global-border-radius-border-radius-base);
27
+ padding: 0
28
+ var(--menu-navigation-components-tree-navigation-item-padding-horizontal);
29
+ }
30
+
31
+ /*
32
+ * A checked row is not re-selectable, so it stays on the amplified enabled
33
+ * style with no hover/pressed feedback.
34
+ */
35
+ &.checked {
36
+ @mixin style style=amplified visibleWrapperClass=.visible-wrapper;
37
+ cursor: default;
38
+
39
+ & .visible-wrapper,
40
+ &:hover .visible-wrapper,
41
+ &:active .visible-wrapper {
42
+ background-color: var(--amplified-enabled-background-color);
43
+ border-color: var(--amplified-enabled-border-color);
44
+ }
45
+
46
+ & .label {
47
+ @mixin font-body-active;
48
+ color: var(--on-flat-active-color);
49
+ }
50
+
51
+ & .leading-icon {
52
+ color: var(--on-amplified-active-color);
53
+ }
54
+ }
55
+
56
+ /* Guide lines are tree structure, not row state, so they do not dim here. */
57
+ &.disabled {
58
+ color: var(--on-flat-disabled-color);
59
+
60
+ & .leading-icon {
61
+ color: var(--on-flat-disabled-color);
62
+ }
63
+ }
64
+ }
65
+
66
+ .tree-node-row {
67
+ display: flex;
68
+ align-items: center;
69
+ height: 100%;
70
+ flex-shrink: 0;
71
+ }
72
+
73
+ .branch {
74
+ position: relative;
75
+ height: 100%;
76
+ width: var(--menu-navigation-components-branch-width);
77
+ flex-shrink: 0;
78
+ }
79
+
80
+ /*
81
+ * Guide lines all run on a rail at 75% of each 32px column and share the faint
82
+ * outline color. Verticals and the dropdown bleed 1px past the row edges so they
83
+ * cross the visible-wrapper's 1px transparent flat border and meet the adjacent
84
+ * row's line with no seam gap.
85
+ */
86
+ .branch-vertical {
87
+ position: absolute;
88
+ top: -1px;
89
+ bottom: -1px;
90
+ left: 75%;
91
+ width: 1px;
92
+ transform: translateX(-0.5px);
93
+ background: var(--border-outline-color);
94
+ }
95
+
96
+ .branch-horizontal {
97
+ position: absolute;
98
+ top: 50%;
99
+ transform: translateY(-0.5px);
100
+ left: 75%;
101
+ right: 0;
102
+ height: 1px;
103
+ background: var(--border-outline-color);
104
+ }
105
+
106
+ /*
107
+ * Corner (└, last child): a single bordered box replaces the straight rects so
108
+ * the bend can be rounded. The box bleeds 1px past the column's right edge so its
109
+ * bottom border overlaps the terminal connector with no break, and translateY
110
+ * drops it 0.5px so that border lands on the connector's pixel row (else the join
111
+ * reads as a `---___` step). The radius stays tight — the stub is only 8px, so a
112
+ * larger fillet would consume the whole flat run.
113
+ */
114
+ .branch-corner .branch-vertical,
115
+ .branch-corner .branch-horizontal {
116
+ display: none;
117
+ }
118
+
119
+ .branch-corner .branch-elbow {
120
+ position: absolute;
121
+ top: -1px;
122
+ bottom: 50%;
123
+ left: 75%;
124
+ right: -1px;
125
+ transform: translate(-0.5px, 0.5px);
126
+ border-left: 1px solid var(--border-outline-color);
127
+ border-bottom: 1px solid var(--border-outline-color);
128
+ border-bottom-left-radius: var(
129
+ --menu-navigation-components-branch-corner-radius,
130
+ 2px
131
+ );
132
+ }
133
+
134
+ .terminal {
135
+ position: relative;
136
+ height: 100%;
137
+ width: var(--global-size-spacing-touch-target-min);
138
+ flex-shrink: 0;
139
+ display: flex;
140
+ align-items: center;
141
+ justify-content: center;
142
+ }
143
+
144
+ /* Stub entering the terminal, continuing the guide line up to the chevron gap. */
145
+ .terminal-connector {
146
+ position: absolute;
147
+ top: 50%;
148
+ transform: translateY(-0.5px);
149
+ left: 0;
150
+ width: 25%;
151
+ height: 1px;
152
+ background: var(--border-outline-color);
153
+ }
154
+
155
+ /* On a leaf (no chevron) the stub runs further, stopping ~13px before the icon. */
156
+ .terminal:not(:has(.chevron)) .terminal-connector {
157
+ width: calc(100% - 13px);
158
+ }
159
+
160
+ /* Descends from below the chevron to connect an expanded node to its children. */
161
+ .terminal-dropdown {
162
+ position: absolute;
163
+ left: 50%;
164
+ transform: translateX(-0.5px);
165
+ top: 75%;
166
+ bottom: -1px;
167
+ width: 1px;
168
+ background: var(--border-outline-color);
169
+ }
170
+
171
+ /* Visual expand/collapse indicator only — not a separate control. */
172
+ .chevron {
173
+ position: relative;
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: center;
177
+ width: var(--menu-navigation-components-tree-navigation-item-icon-size);
178
+ height: var(--menu-navigation-components-tree-navigation-item-icon-size);
179
+ color: var(--element-neutral-color);
180
+ transition: transform 100ms ease-in-out;
181
+ }
182
+
183
+ @media (prefers-reduced-motion: reduce) {
184
+ .chevron {
185
+ transition: none;
186
+ }
187
+ }
188
+
189
+ .wrapper.disabled .chevron {
190
+ color: var(--on-flat-disabled-color);
191
+ }
192
+
193
+ :host([expanded]) .chevron {
194
+ transform: rotate(90deg);
195
+ }
196
+
197
+ /* Alert-header marker for non-regular terminal types, at the terminal's top-right. */
198
+ .terminal-header {
199
+ position: absolute;
200
+ inset: 0;
201
+ display: flex;
202
+ justify-content: flex-end;
203
+ align-items: flex-start;
204
+ padding: var(--menu-navigation-components-terminal-padding-aggregated-icon);
205
+ color: var(--element-neutral-color);
206
+ pointer-events: none;
207
+ }
208
+
209
+ .terminal-header > * {
210
+ width: var(--menu-navigation-components-tree-navigation-item-icon-size);
211
+ height: var(--menu-navigation-components-tree-navigation-item-icon-size);
212
+ }
213
+
214
+ .wrapper.disabled .terminal-header {
215
+ color: var(--on-flat-disabled-color);
216
+ }
217
+
218
+ .label-container {
219
+ display: flex;
220
+ flex: 1;
221
+ gap: var(
222
+ --menu-navigation-components-tree-navigation-item-padding-horizontal
223
+ );
224
+ align-items: center;
225
+ min-width: 0;
226
+ padding-right: var(
227
+ --menu-navigation-components-tree-navigation-item-padding-horizontal
228
+ );
229
+ }
230
+
231
+ .leading-icon {
232
+ position: relative;
233
+ flex-shrink: 0;
234
+ display: flex;
235
+ align-items: center;
236
+ justify-content: center;
237
+ width: var(--menu-navigation-components-tree-navigation-item-icon-size);
238
+ height: var(--menu-navigation-components-tree-navigation-item-icon-size);
239
+ color: var(--on-flat-neutral-color);
240
+ }
241
+
242
+ ::slotted([slot="icon"]) {
243
+ display: block;
244
+ width: var(--menu-navigation-components-tree-navigation-item-icon-size);
245
+ height: var(--menu-navigation-components-tree-navigation-item-icon-size);
246
+ }
247
+
248
+ .label {
249
+ flex: 1;
250
+ overflow: hidden;
251
+ text-overflow: ellipsis;
252
+ white-space: nowrap;
253
+ min-width: 0;
254
+ }
255
+
256
+ .alert-badge {
257
+ flex-shrink: 0;
258
+ }