@oicl/openbridge-webcomponents 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.
- package/bundle/openbridge-webcomponents.bundle.js +5557 -4305
- package/bundle/openbridge-webcomponents.bundle.js.map +1 -1
- package/custom-elements.json +1204 -25
- package/dist/components/navigation-item/navigation-item.css.js +5 -0
- package/dist/components/navigation-item/navigation-item.css.js.map +1 -1
- package/dist/components/navigation-item/navigation-item.d.ts +18 -0
- package/dist/components/navigation-item/navigation-item.d.ts.map +1 -1
- package/dist/components/navigation-item/navigation-item.js +49 -1
- package/dist/components/navigation-item/navigation-item.js.map +1 -1
- package/dist/components/navigation-item-group/navigation-item-group.css.js +8 -0
- package/dist/components/navigation-item-group/navigation-item-group.css.js.map +1 -1
- package/dist/components/navigation-item-group/navigation-item-group.d.ts +22 -0
- package/dist/components/navigation-item-group/navigation-item-group.d.ts.map +1 -1
- package/dist/components/navigation-item-group/navigation-item-group.js +63 -1
- package/dist/components/navigation-item-group/navigation-item-group.js.map +1 -1
- package/dist/components/navigation-menu/navigation-menu.d.ts +29 -1
- package/dist/components/navigation-menu/navigation-menu.d.ts.map +1 -1
- package/dist/components/navigation-menu/navigation-menu.js +105 -0
- package/dist/components/navigation-menu/navigation-menu.js.map +1 -1
- package/dist/components/tree-navigation/tree-navigation.css.js +18 -0
- package/dist/components/tree-navigation/tree-navigation.css.js.map +1 -0
- package/dist/components/tree-navigation/tree-navigation.d.ts +74 -0
- package/dist/components/tree-navigation/tree-navigation.d.ts.map +1 -0
- package/dist/components/tree-navigation/tree-navigation.js +120 -0
- package/dist/components/tree-navigation/tree-navigation.js.map +1 -0
- package/dist/components/tree-navigation-group/tree-navigation-group.css.js +22 -0
- package/dist/components/tree-navigation-group/tree-navigation-group.css.js.map +1 -0
- package/dist/components/tree-navigation-group/tree-navigation-group.d.ts +94 -0
- package/dist/components/tree-navigation-group/tree-navigation-group.d.ts.map +1 -0
- package/dist/components/tree-navigation-group/tree-navigation-group.js +116 -0
- package/dist/components/tree-navigation-group/tree-navigation-group.js.map +1 -0
- package/dist/components/tree-navigation-item/tree-navigation-item.css.js +429 -0
- package/dist/components/tree-navigation-item/tree-navigation-item.css.js.map +1 -0
- package/dist/components/tree-navigation-item/tree-navigation-item.d.ts +160 -0
- package/dist/components/tree-navigation-item/tree-navigation-item.d.ts.map +1 -0
- package/dist/components/tree-navigation-item/tree-navigation-item.js +208 -0
- package/dist/components/tree-navigation-item/tree-navigation-item.js.map +1 -0
- package/dist/internal/tree-roving-navigator.d.ts +71 -0
- package/dist/internal/tree-roving-navigator.d.ts.map +1 -0
- package/dist/internal/tree-roving-navigator.js +172 -0
- package/dist/internal/tree-roving-navigator.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import '../../icons/icon-chevron-right-google.js';
|
|
3
|
+
import '../../icons/icon-alert-header-aggregated-iec.js';
|
|
4
|
+
import '../../icons/icon-alert-header-group-iec.js';
|
|
5
|
+
import '../badge/badge.js';
|
|
6
|
+
/**
|
|
7
|
+
* Guide line drawn for one indentation column. Normally computed by
|
|
8
|
+
* `obc-tree-navigation`; rarely set by hand.
|
|
9
|
+
*
|
|
10
|
+
* - `straight`: vertical pass-through (`│`)
|
|
11
|
+
* - `intersection`: vertical + stub for a non-last child (`├`)
|
|
12
|
+
* - `corner`: half vertical + stub for the last child (`└`)
|
|
13
|
+
* - `blank`: empty spacer
|
|
14
|
+
*/
|
|
15
|
+
export declare enum TreeBranchType {
|
|
16
|
+
straight = "straight",
|
|
17
|
+
intersection = "intersection",
|
|
18
|
+
corner = "corner",
|
|
19
|
+
blank = "blank"
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Terminal type for a tree item, controlling the alert-header marker shown in
|
|
23
|
+
* the terminal (top-right, by the chevron).
|
|
24
|
+
*
|
|
25
|
+
* - `regular`: No marker — a plain expand/collapse terminal.
|
|
26
|
+
* - `aggregatedHeader`: Shows the aggregated alert-header marker, indicating the
|
|
27
|
+
* node aggregates alerts from its descendants.
|
|
28
|
+
* - `groupHeader`: Shows the group alert-header marker, indicating a grouped set
|
|
29
|
+
* of alerts beneath the node.
|
|
30
|
+
*/
|
|
31
|
+
export declare enum TreeTerminalType {
|
|
32
|
+
regular = "regular",
|
|
33
|
+
aggregatedHeader = "aggregated-header",
|
|
34
|
+
groupHeader = "group-header"
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* `<obc-tree-navigation-item>` – A single row in a tree- or file-explorer-style
|
|
38
|
+
* navigation list, with indentation guide lines, an optional expand/collapse
|
|
39
|
+
* chevron, a leading icon, a label, and an optional alert badge.
|
|
40
|
+
*
|
|
41
|
+
* Each row represents one node in a hierarchy. Depth is expressed through the
|
|
42
|
+
* `branches` array: one entry per ancestor level, each describing the guide line
|
|
43
|
+
* to draw for that level. The expand chevron lets a node disclose its children,
|
|
44
|
+
* emitting `expand-toggle` so a parent can manage the open/closed state.
|
|
45
|
+
*
|
|
46
|
+
* ## Features
|
|
47
|
+
* - **Indentation guides:** Render one 32px guide column per `branches` entry —
|
|
48
|
+
* pass-through vertical lines for ancestors that continue, and an elbow
|
|
49
|
+
* (`intersection`) connecting the row to its parent.
|
|
50
|
+
* - **Expand/collapse:** Set `expandable` to show a chevron that rotates when
|
|
51
|
+
* `expanded`. The chevron is a visual indicator only — activating the row
|
|
52
|
+
* fires `expand-toggle` with the next state; manage `expanded` in response.
|
|
53
|
+
* When `expanded`, a vertical guide descends from the chevron to connect to
|
|
54
|
+
* the revealed child rows below.
|
|
55
|
+
* - **Terminal type:** `terminalType` adds an alert-header marker in the terminal
|
|
56
|
+
* (`aggregatedHeader` or `groupHeader`), indicating the node heads a set of
|
|
57
|
+
* aggregated or grouped alerts.
|
|
58
|
+
* - **Leading icon:** Provide an icon via the `icon` slot (shown when
|
|
59
|
+
* `hasLeadingIcon`).
|
|
60
|
+
* - **Alert badge:** A trailing counter badge (e.g. an alarm count), toggled with
|
|
61
|
+
* `hasAlertBadge` and configured via `alertCount` and `alertType`.
|
|
62
|
+
* - **Checked state:** `checked` highlights the current selection using the
|
|
63
|
+
* amplified elevation style. A checked row is the current item and is not
|
|
64
|
+
* re-selectable — it shows no hover/pressed feedback and fires no `click` or
|
|
65
|
+
* navigation. An expandable checked row still toggles (fires `expand-toggle`),
|
|
66
|
+
* so a group that is the current selection can be opened and closed. It stays
|
|
67
|
+
* keyboard-focusable.
|
|
68
|
+
* - **Link or button:** Set `href` to render the row as a link; otherwise it acts
|
|
69
|
+
* as a button.
|
|
70
|
+
*
|
|
71
|
+
* ## Usage Guidelines
|
|
72
|
+
* - Use for hierarchical navigation such as file trees, layer panels, or nested
|
|
73
|
+
* menus. For flat navigation lists, use `obc-navigation-item` instead.
|
|
74
|
+
* - Build `branches` from the row's ancestry: `intersection` for the level that
|
|
75
|
+
* owns this row, `straight` for ancestors with siblings still to come, and
|
|
76
|
+
* `blank` for ancestors whose subtrees have ended. When placed inside
|
|
77
|
+
* `obc-tree-navigation`, the container computes `branches` automatically.
|
|
78
|
+
* - Keep one row `checked` at a time within a tree to mark the current location.
|
|
79
|
+
* - Leaf nodes should leave `expandable` as `false` so no chevron is shown.
|
|
80
|
+
*
|
|
81
|
+
* ## Slots
|
|
82
|
+
*
|
|
83
|
+
* | Slot Name | Renders When... | Purpose |
|
|
84
|
+
* |----------------|---------------------------------|-------------------------------------------------------------------------|
|
|
85
|
+
* | icon | `hasLeadingIcon` is true | Leading icon for the row, e.g. `<obi-placeholder slot="icon">`. |
|
|
86
|
+
*
|
|
87
|
+
* @slot icon - Leading icon slot (shown when `hasLeadingIcon` is true).
|
|
88
|
+
* @fires expand-toggle {CustomEvent<boolean>} Fired when an expandable row is activated; detail is the next `expanded` value.
|
|
89
|
+
* @fires click {CustomEvent<void>} Fired when the row is activated.
|
|
90
|
+
*/
|
|
91
|
+
export declare class ObcTreeNavigationItem extends LitElement {
|
|
92
|
+
/** The text label displayed for the row. */
|
|
93
|
+
label: string;
|
|
94
|
+
/**
|
|
95
|
+
* Guide line for each ancestor level, outermost first; one 32px column per
|
|
96
|
+
* entry. Computed by `obc-tree-navigation` — rarely set by hand.
|
|
97
|
+
*/
|
|
98
|
+
branches: TreeBranchType[];
|
|
99
|
+
/** Whether the row shows an expand/collapse chevron. Leave false for leaf nodes. */
|
|
100
|
+
expandable: boolean;
|
|
101
|
+
/** Whether the node is expanded. Rotates the chevron and sets `aria-expanded`. */
|
|
102
|
+
expanded: boolean;
|
|
103
|
+
/**
|
|
104
|
+
* Whether the row is the current selection. Applies the amplified style and
|
|
105
|
+
* makes the row inert to re-selection (no hover/pressed feedback, no `click`
|
|
106
|
+
* or navigation); it remains keyboard-focusable. An expandable checked row
|
|
107
|
+
* still fires `expand-toggle` so a selected group can open and close.
|
|
108
|
+
*/
|
|
109
|
+
checked: boolean;
|
|
110
|
+
/** Disables the row, removing it from the tab order and dimming its appearance. */
|
|
111
|
+
disabled: boolean;
|
|
112
|
+
/**
|
|
113
|
+
* Whether the row is in the tab order. A tree container manages this as a
|
|
114
|
+
* roving tabindex (one row focusable at a time); standalone rows stay tabbable.
|
|
115
|
+
*/
|
|
116
|
+
focusable: boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Whether the row shows a leading icon (provided via the `icon` slot).
|
|
119
|
+
*/
|
|
120
|
+
hasLeadingIcon: boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Terminal type, controlling the alert-header marker shown in the terminal.
|
|
123
|
+
* One of `regular` (default), `aggregated-header`, or `group-header`.
|
|
124
|
+
*/
|
|
125
|
+
terminalType: string;
|
|
126
|
+
/** Whether a trailing alert counter badge is shown. */
|
|
127
|
+
hasAlertBadge: boolean;
|
|
128
|
+
/** The number shown in the alert badge when `hasAlertBadge` is true. */
|
|
129
|
+
alertCount: number;
|
|
130
|
+
/** The severity/type of the alert badge. One of the `obc-badge` types (default `alarm`). */
|
|
131
|
+
alertType: string;
|
|
132
|
+
/**
|
|
133
|
+
* The URL to navigate to when the row is activated. If set, the row renders as
|
|
134
|
+
* a link; otherwise it acts as a button.
|
|
135
|
+
*/
|
|
136
|
+
href: string | undefined;
|
|
137
|
+
private wrapperElement?;
|
|
138
|
+
/** Focuses the row's interactive wrapper (the host itself is not focusable). */
|
|
139
|
+
focus(options?: FocusOptions): void;
|
|
140
|
+
/** A root-level row has no ancestor columns, so it draws no connector lines. */
|
|
141
|
+
private get isRoot();
|
|
142
|
+
/**
|
|
143
|
+
* All ancestor columns are blank spacers — the row is indented but draws no
|
|
144
|
+
* guide lines, so the terminal connector and dropdown are suppressed too.
|
|
145
|
+
*/
|
|
146
|
+
private get isBlankAncestry();
|
|
147
|
+
private activate;
|
|
148
|
+
private handleKeydown;
|
|
149
|
+
private renderBranch;
|
|
150
|
+
private renderTerminalHeader;
|
|
151
|
+
render(): import('lit-html').TemplateResult<1>;
|
|
152
|
+
static styles: import('lit').CSSResult;
|
|
153
|
+
}
|
|
154
|
+
export type ObcTreeNavigationItemExpandToggleEvent = CustomEvent<boolean>;
|
|
155
|
+
declare global {
|
|
156
|
+
interface HTMLElementTagNameMap {
|
|
157
|
+
'obc-tree-navigation-item': ObcTreeNavigationItem;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=tree-navigation-item.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-navigation-item.d.ts","sourceRoot":"","sources":["../../../src/components/tree-navigation-item/tree-navigation-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAA2B,MAAM,KAAK,CAAC;AAMzD,OAAO,0CAA0C,CAAC;AAClD,OAAO,iDAAiD,CAAC;AACzD,OAAO,4CAA4C,CAAC;AACpD,OAAO,mBAAmB,CAAC;AAG3B;;;;;;;;GAQG;AACH,oBAAY,cAAc;IACxB,QAAQ,aAAa;IACrB,YAAY,iBAAiB;IAC7B,MAAM,WAAW;IACjB,KAAK,UAAU;CAChB;AAED;;;;;;;;;GASG;AACH,oBAAY,gBAAgB;IAC1B,OAAO,YAAY;IACnB,gBAAgB,sBAAsB;IACtC,WAAW,iBAAiB;CAC7B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,qBACa,qBAAsB,SAAQ,UAAU;IACnD,4CAA4C;IAClB,KAAK,SAAe;IAE9C;;;OAGG;IACsB,QAAQ,EAAE,cAAc,EAAE,CAAM;IAEzD,oFAAoF;IACzD,UAAU,UAAS;IAE9C,kFAAkF;IACxC,QAAQ,UAAS;IAE3D;;;;;OAKG;IACuC,OAAO,UAAS;IAE1D,mFAAmF;IACzC,QAAQ,UAAS;IAE3D;;;OAGG;IAC0C,SAAS,UAAQ;IAE9D;;OAEG;IAC0C,cAAc,UAAQ;IAEnE;;;OAGG;IACuB,YAAY,EAAE,MAAM,CAA4B;IAE1E,uDAAuD;IAC5B,aAAa,UAAS;IAEjD,wEAAwE;IAC9C,UAAU,SAAK;IAEzC,4FAA4F;IAClE,SAAS,EAAE,MAAM,CAAmB;IAE9D;;;OAGG;IACuB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAEhC,OAAO,CAAC,cAAc,CAAC,CAAc;IAExD,gFAAgF;IAChE,KAAK,CAAC,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAInD,gFAAgF;IAChF,OAAO,KAAK,MAAM,GAEjB;IAED;;;OAGG;IACH,OAAO,KAAK,eAAe,GAK1B;IAED,OAAO,CAAC,QAAQ;IAiBhB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,oBAAoB;IAcnB,MAAM;IA2Df,OAAgB,MAAM,0BAA6B;CACpD;AAED,MAAM,MAAM,sCAAsC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAE1E,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,0BAA0B,EAAE,qBAAqB,CAAC;KACnD;CACF"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { unsafeCSS, LitElement, nothing, html } from "lit";
|
|
2
|
+
import { property, query } from "lit/decorators.js";
|
|
3
|
+
import componentStyle from "./tree-navigation-item.css.js";
|
|
4
|
+
import { classMap } from "lit/directives/class-map.js";
|
|
5
|
+
import { ifDefined } from "lit/directives/if-defined.js";
|
|
6
|
+
import { customElement } from "../../decorator.js";
|
|
7
|
+
import "../../icons/icon-chevron-right-google.js";
|
|
8
|
+
import "../../icons/icon-alert-header-aggregated-iec.js";
|
|
9
|
+
import "../../icons/icon-alert-header-group-iec.js";
|
|
10
|
+
import { BadgeType } from "../badge/badge.js";
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
13
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
14
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
15
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
16
|
+
if (decorator = decorators[i])
|
|
17
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
18
|
+
if (kind && result) __defProp(target, key, result);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
var TreeBranchType = /* @__PURE__ */ ((TreeBranchType2) => {
|
|
22
|
+
TreeBranchType2["straight"] = "straight";
|
|
23
|
+
TreeBranchType2["intersection"] = "intersection";
|
|
24
|
+
TreeBranchType2["corner"] = "corner";
|
|
25
|
+
TreeBranchType2["blank"] = "blank";
|
|
26
|
+
return TreeBranchType2;
|
|
27
|
+
})(TreeBranchType || {});
|
|
28
|
+
var TreeTerminalType = /* @__PURE__ */ ((TreeTerminalType2) => {
|
|
29
|
+
TreeTerminalType2["regular"] = "regular";
|
|
30
|
+
TreeTerminalType2["aggregatedHeader"] = "aggregated-header";
|
|
31
|
+
TreeTerminalType2["groupHeader"] = "group-header";
|
|
32
|
+
return TreeTerminalType2;
|
|
33
|
+
})(TreeTerminalType || {});
|
|
34
|
+
let ObcTreeNavigationItem = class extends LitElement {
|
|
35
|
+
constructor() {
|
|
36
|
+
super(...arguments);
|
|
37
|
+
this.label = "List item";
|
|
38
|
+
this.branches = [];
|
|
39
|
+
this.expandable = false;
|
|
40
|
+
this.expanded = false;
|
|
41
|
+
this.checked = false;
|
|
42
|
+
this.disabled = false;
|
|
43
|
+
this.focusable = true;
|
|
44
|
+
this.hasLeadingIcon = true;
|
|
45
|
+
this.terminalType = "regular";
|
|
46
|
+
this.hasAlertBadge = false;
|
|
47
|
+
this.alertCount = 0;
|
|
48
|
+
this.alertType = BadgeType.alarm;
|
|
49
|
+
}
|
|
50
|
+
/** Focuses the row's interactive wrapper (the host itself is not focusable). */
|
|
51
|
+
focus(options) {
|
|
52
|
+
this.wrapperElement?.focus(options);
|
|
53
|
+
}
|
|
54
|
+
/** A root-level row has no ancestor columns, so it draws no connector lines. */
|
|
55
|
+
get isRoot() {
|
|
56
|
+
return this.branches.length === 0;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* All ancestor columns are blank spacers — the row is indented but draws no
|
|
60
|
+
* guide lines, so the terminal connector and dropdown are suppressed too.
|
|
61
|
+
*/
|
|
62
|
+
get isBlankAncestry() {
|
|
63
|
+
return this.branches.length > 0 && this.branches.every(
|
|
64
|
+
(b) => b === "blank"
|
|
65
|
+
/* blank */
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
activate() {
|
|
69
|
+
if (this.disabled) return;
|
|
70
|
+
if (this.expandable) {
|
|
71
|
+
this.dispatchEvent(
|
|
72
|
+
new CustomEvent("expand-toggle", { detail: !this.expanded })
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
if (this.checked) return;
|
|
76
|
+
this.dispatchEvent(new CustomEvent("click"));
|
|
77
|
+
if (this.href !== void 0) {
|
|
78
|
+
window.location.href = this.href;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
handleKeydown(event) {
|
|
82
|
+
if (this.disabled) return;
|
|
83
|
+
if (event.key !== "Enter" && event.key !== " ") return;
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
this.activate();
|
|
86
|
+
}
|
|
87
|
+
renderBranch(type) {
|
|
88
|
+
const hasVertical = type === "straight" || type === "intersection" || type === "corner";
|
|
89
|
+
const hasHorizontal = type === "intersection" || type === "corner";
|
|
90
|
+
return html`<div
|
|
91
|
+
class=${classMap({ branch: true, ["branch-" + type]: true })}
|
|
92
|
+
>
|
|
93
|
+
${hasVertical ? html`<div class="branch-vertical"></div>` : nothing}
|
|
94
|
+
${hasHorizontal ? html`<div class="branch-horizontal"></div>` : nothing}
|
|
95
|
+
${type === "corner" ? html`<div class="branch-elbow"></div>` : nothing}
|
|
96
|
+
</div>`;
|
|
97
|
+
}
|
|
98
|
+
renderTerminalHeader() {
|
|
99
|
+
if (this.terminalType === "aggregated-header") {
|
|
100
|
+
return html`<div class="terminal-header" aria-hidden="true">
|
|
101
|
+
<obi-alert-header-aggregated-iec></obi-alert-header-aggregated-iec>
|
|
102
|
+
</div>`;
|
|
103
|
+
}
|
|
104
|
+
if (this.terminalType === "group-header") {
|
|
105
|
+
return html`<div class="terminal-header" aria-hidden="true">
|
|
106
|
+
<obi-alert-header-group-iec></obi-alert-header-group-iec>
|
|
107
|
+
</div>`;
|
|
108
|
+
}
|
|
109
|
+
return nothing;
|
|
110
|
+
}
|
|
111
|
+
render() {
|
|
112
|
+
return html`
|
|
113
|
+
<div
|
|
114
|
+
class=${classMap({
|
|
115
|
+
wrapper: true,
|
|
116
|
+
checked: this.checked,
|
|
117
|
+
disabled: this.disabled,
|
|
118
|
+
"has-icon": this.hasLeadingIcon
|
|
119
|
+
})}
|
|
120
|
+
role="treeitem"
|
|
121
|
+
aria-expanded=${ifDefined(this.expandable ? this.expanded : void 0)}
|
|
122
|
+
aria-current=${ifDefined(this.checked ? "page" : void 0)}
|
|
123
|
+
aria-selected=${ifDefined(this.checked ? "true" : void 0)}
|
|
124
|
+
aria-disabled=${ifDefined(this.disabled ? "true" : void 0)}
|
|
125
|
+
tabindex=${this.disabled ? -1 : this.focusable ? 0 : -1}
|
|
126
|
+
@click=${this.activate}
|
|
127
|
+
@keydown=${this.handleKeydown}
|
|
128
|
+
>
|
|
129
|
+
<div class="visible-wrapper">
|
|
130
|
+
<div class="tree-node-row">
|
|
131
|
+
${this.branches.map((branch) => this.renderBranch(branch))}
|
|
132
|
+
<div class="terminal">
|
|
133
|
+
${this.isRoot || this.isBlankAncestry ? nothing : html`<div class="terminal-connector"></div>`}
|
|
134
|
+
${!this.isRoot && !this.isBlankAncestry && this.expandable && this.expanded ? html`<div class="terminal-dropdown"></div>` : nothing}
|
|
135
|
+
${this.expandable ? html`<div class="chevron" aria-hidden="true">
|
|
136
|
+
<obi-chevron-right-google></obi-chevron-right-google>
|
|
137
|
+
</div>` : nothing}
|
|
138
|
+
${this.renderTerminalHeader()}
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
<div class="label-container">
|
|
142
|
+
${this.hasLeadingIcon ? html`<div class="leading-icon">
|
|
143
|
+
<slot name="icon"></slot>
|
|
144
|
+
</div>` : nothing}
|
|
145
|
+
<span part="label" class="label">${this.label}</span>
|
|
146
|
+
</div>
|
|
147
|
+
${this.hasAlertBadge ? html`<obc-badge
|
|
148
|
+
class="alert-badge"
|
|
149
|
+
.type=${this.alertType}
|
|
150
|
+
.number=${this.alertCount}
|
|
151
|
+
></obc-badge>` : nothing}
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
`;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
ObcTreeNavigationItem.styles = unsafeCSS(componentStyle);
|
|
158
|
+
__decorateClass([
|
|
159
|
+
property({ type: String })
|
|
160
|
+
], ObcTreeNavigationItem.prototype, "label", 2);
|
|
161
|
+
__decorateClass([
|
|
162
|
+
property({ type: Array })
|
|
163
|
+
], ObcTreeNavigationItem.prototype, "branches", 2);
|
|
164
|
+
__decorateClass([
|
|
165
|
+
property({ type: Boolean })
|
|
166
|
+
], ObcTreeNavigationItem.prototype, "expandable", 2);
|
|
167
|
+
__decorateClass([
|
|
168
|
+
property({ type: Boolean, reflect: true })
|
|
169
|
+
], ObcTreeNavigationItem.prototype, "expanded", 2);
|
|
170
|
+
__decorateClass([
|
|
171
|
+
property({ type: Boolean, reflect: true })
|
|
172
|
+
], ObcTreeNavigationItem.prototype, "checked", 2);
|
|
173
|
+
__decorateClass([
|
|
174
|
+
property({ type: Boolean, reflect: true })
|
|
175
|
+
], ObcTreeNavigationItem.prototype, "disabled", 2);
|
|
176
|
+
__decorateClass([
|
|
177
|
+
property({ type: Boolean, attribute: false })
|
|
178
|
+
], ObcTreeNavigationItem.prototype, "focusable", 2);
|
|
179
|
+
__decorateClass([
|
|
180
|
+
property({ type: Boolean, attribute: false })
|
|
181
|
+
], ObcTreeNavigationItem.prototype, "hasLeadingIcon", 2);
|
|
182
|
+
__decorateClass([
|
|
183
|
+
property({ type: String })
|
|
184
|
+
], ObcTreeNavigationItem.prototype, "terminalType", 2);
|
|
185
|
+
__decorateClass([
|
|
186
|
+
property({ type: Boolean })
|
|
187
|
+
], ObcTreeNavigationItem.prototype, "hasAlertBadge", 2);
|
|
188
|
+
__decorateClass([
|
|
189
|
+
property({ type: Number })
|
|
190
|
+
], ObcTreeNavigationItem.prototype, "alertCount", 2);
|
|
191
|
+
__decorateClass([
|
|
192
|
+
property({ type: String })
|
|
193
|
+
], ObcTreeNavigationItem.prototype, "alertType", 2);
|
|
194
|
+
__decorateClass([
|
|
195
|
+
property({ type: String })
|
|
196
|
+
], ObcTreeNavigationItem.prototype, "href", 2);
|
|
197
|
+
__decorateClass([
|
|
198
|
+
query(".wrapper")
|
|
199
|
+
], ObcTreeNavigationItem.prototype, "wrapperElement", 2);
|
|
200
|
+
ObcTreeNavigationItem = __decorateClass([
|
|
201
|
+
customElement("obc-tree-navigation-item")
|
|
202
|
+
], ObcTreeNavigationItem);
|
|
203
|
+
export {
|
|
204
|
+
ObcTreeNavigationItem,
|
|
205
|
+
TreeBranchType,
|
|
206
|
+
TreeTerminalType
|
|
207
|
+
};
|
|
208
|
+
//# sourceMappingURL=tree-navigation-item.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-navigation-item.js","sources":["../../../src/components/tree-navigation-item/tree-navigation-item.ts"],"sourcesContent":["import {LitElement, html, nothing, unsafeCSS} from 'lit';\nimport {property, query} from 'lit/decorators.js';\nimport componentStyle from './tree-navigation-item.css?inline';\nimport {classMap} from 'lit/directives/class-map.js';\nimport {ifDefined} from 'lit/directives/if-defined.js';\nimport {customElement} from '../../decorator.js';\nimport '../../icons/icon-chevron-right-google.js';\nimport '../../icons/icon-alert-header-aggregated-iec.js';\nimport '../../icons/icon-alert-header-group-iec.js';\nimport '../badge/badge.js';\nimport {BadgeType} from '../badge/badge.js';\n\n/**\n * Guide line drawn for one indentation column. Normally computed by\n * `obc-tree-navigation`; rarely set by hand.\n *\n * - `straight`: vertical pass-through (`│`)\n * - `intersection`: vertical + stub for a non-last child (`├`)\n * - `corner`: half vertical + stub for the last child (`└`)\n * - `blank`: empty spacer\n */\nexport enum TreeBranchType {\n straight = 'straight',\n intersection = 'intersection',\n corner = 'corner',\n blank = 'blank',\n}\n\n/**\n * Terminal type for a tree item, controlling the alert-header marker shown in\n * the terminal (top-right, by the chevron).\n *\n * - `regular`: No marker — a plain expand/collapse terminal.\n * - `aggregatedHeader`: Shows the aggregated alert-header marker, indicating the\n * node aggregates alerts from its descendants.\n * - `groupHeader`: Shows the group alert-header marker, indicating a grouped set\n * of alerts beneath the node.\n */\nexport enum TreeTerminalType {\n regular = 'regular',\n aggregatedHeader = 'aggregated-header',\n groupHeader = 'group-header',\n}\n\n/**\n * `<obc-tree-navigation-item>` – A single row in a tree- or file-explorer-style\n * navigation list, with indentation guide lines, an optional expand/collapse\n * chevron, a leading icon, a label, and an optional alert badge.\n *\n * Each row represents one node in a hierarchy. Depth is expressed through the\n * `branches` array: one entry per ancestor level, each describing the guide line\n * to draw for that level. The expand chevron lets a node disclose its children,\n * emitting `expand-toggle` so a parent can manage the open/closed state.\n *\n * ## Features\n * - **Indentation guides:** Render one 32px guide column per `branches` entry —\n * pass-through vertical lines for ancestors that continue, and an elbow\n * (`intersection`) connecting the row to its parent.\n * - **Expand/collapse:** Set `expandable` to show a chevron that rotates when\n * `expanded`. The chevron is a visual indicator only — activating the row\n * fires `expand-toggle` with the next state; manage `expanded` in response.\n * When `expanded`, a vertical guide descends from the chevron to connect to\n * the revealed child rows below.\n * - **Terminal type:** `terminalType` adds an alert-header marker in the terminal\n * (`aggregatedHeader` or `groupHeader`), indicating the node heads a set of\n * aggregated or grouped alerts.\n * - **Leading icon:** Provide an icon via the `icon` slot (shown when\n * `hasLeadingIcon`).\n * - **Alert badge:** A trailing counter badge (e.g. an alarm count), toggled with\n * `hasAlertBadge` and configured via `alertCount` and `alertType`.\n * - **Checked state:** `checked` highlights the current selection using the\n * amplified elevation style. A checked row is the current item and is not\n * re-selectable — it shows no hover/pressed feedback and fires no `click` or\n * navigation. An expandable checked row still toggles (fires `expand-toggle`),\n * so a group that is the current selection can be opened and closed. It stays\n * keyboard-focusable.\n * - **Link or button:** Set `href` to render the row as a link; otherwise it acts\n * as a button.\n *\n * ## Usage Guidelines\n * - Use for hierarchical navigation such as file trees, layer panels, or nested\n * menus. For flat navigation lists, use `obc-navigation-item` instead.\n * - Build `branches` from the row's ancestry: `intersection` for the level that\n * owns this row, `straight` for ancestors with siblings still to come, and\n * `blank` for ancestors whose subtrees have ended. When placed inside\n * `obc-tree-navigation`, the container computes `branches` automatically.\n * - Keep one row `checked` at a time within a tree to mark the current location.\n * - Leaf nodes should leave `expandable` as `false` so no chevron is shown.\n *\n * ## Slots\n *\n * | Slot Name | Renders When... | Purpose |\n * |----------------|---------------------------------|-------------------------------------------------------------------------|\n * | icon | `hasLeadingIcon` is true | Leading icon for the row, e.g. `<obi-placeholder slot=\"icon\">`. |\n *\n * @slot icon - Leading icon slot (shown when `hasLeadingIcon` is true).\n * @fires expand-toggle {CustomEvent<boolean>} Fired when an expandable row is activated; detail is the next `expanded` value.\n * @fires click {CustomEvent<void>} Fired when the row is activated.\n */\n@customElement('obc-tree-navigation-item')\nexport class ObcTreeNavigationItem extends LitElement {\n /** The text label displayed for the row. */\n @property({type: String}) label = 'List item';\n\n /**\n * Guide line for each ancestor level, outermost first; one 32px column per\n * entry. Computed by `obc-tree-navigation` — rarely set by hand.\n */\n @property({type: Array}) branches: TreeBranchType[] = [];\n\n /** Whether the row shows an expand/collapse chevron. Leave false for leaf nodes. */\n @property({type: Boolean}) expandable = false;\n\n /** Whether the node is expanded. Rotates the chevron and sets `aria-expanded`. */\n @property({type: Boolean, reflect: true}) expanded = false;\n\n /**\n * Whether the row is the current selection. Applies the amplified style and\n * makes the row inert to re-selection (no hover/pressed feedback, no `click`\n * or navigation); it remains keyboard-focusable. An expandable checked row\n * still fires `expand-toggle` so a selected group can open and close.\n */\n @property({type: Boolean, reflect: true}) checked = false;\n\n /** Disables the row, removing it from the tab order and dimming its appearance. */\n @property({type: Boolean, reflect: true}) disabled = false;\n\n /**\n * Whether the row is in the tab order. A tree container manages this as a\n * roving tabindex (one row focusable at a time); standalone rows stay tabbable.\n */\n @property({type: Boolean, attribute: false}) focusable = true;\n\n /**\n * Whether the row shows a leading icon (provided via the `icon` slot).\n */\n @property({type: Boolean, attribute: false}) hasLeadingIcon = true;\n\n /**\n * Terminal type, controlling the alert-header marker shown in the terminal.\n * One of `regular` (default), `aggregated-header`, or `group-header`.\n */\n @property({type: String}) terminalType: string = TreeTerminalType.regular;\n\n /** Whether a trailing alert counter badge is shown. */\n @property({type: Boolean}) hasAlertBadge = false;\n\n /** The number shown in the alert badge when `hasAlertBadge` is true. */\n @property({type: Number}) alertCount = 0;\n\n /** The severity/type of the alert badge. One of the `obc-badge` types (default `alarm`). */\n @property({type: String}) alertType: string = BadgeType.alarm;\n\n /**\n * The URL to navigate to when the row is activated. If set, the row renders as\n * a link; otherwise it acts as a button.\n */\n @property({type: String}) href: string | undefined;\n\n @query('.wrapper') private wrapperElement?: HTMLElement;\n\n /** Focuses the row's interactive wrapper (the host itself is not focusable). */\n public override focus(options?: FocusOptions): void {\n this.wrapperElement?.focus(options);\n }\n\n /** A root-level row has no ancestor columns, so it draws no connector lines. */\n private get isRoot(): boolean {\n return this.branches.length === 0;\n }\n\n /**\n * All ancestor columns are blank spacers — the row is indented but draws no\n * guide lines, so the terminal connector and dropdown are suppressed too.\n */\n private get isBlankAncestry(): boolean {\n return (\n this.branches.length > 0 &&\n this.branches.every((b) => b === TreeBranchType.blank)\n );\n }\n\n private activate() {\n if (this.disabled) return;\n // An expandable row always toggles, even when checked — a group that is the\n // current selection must still open and close. A checked leaf, however, is\n // the current selection and is inert (no re-selection click or navigation).\n if (this.expandable) {\n this.dispatchEvent(\n new CustomEvent<boolean>('expand-toggle', {detail: !this.expanded})\n );\n }\n if (this.checked) return;\n this.dispatchEvent(new CustomEvent('click'));\n if (this.href !== undefined) {\n window.location.href = this.href;\n }\n }\n\n private handleKeydown(event: KeyboardEvent) {\n if (this.disabled) return;\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n this.activate();\n }\n\n private renderBranch(type: TreeBranchType) {\n const hasVertical =\n type === TreeBranchType.straight ||\n type === TreeBranchType.intersection ||\n type === TreeBranchType.corner;\n const hasHorizontal =\n type === TreeBranchType.intersection || type === TreeBranchType.corner;\n return html`<div\n class=${classMap({branch: true, ['branch-' + type]: true})}\n >\n ${hasVertical ? html`<div class=\"branch-vertical\"></div>` : nothing}\n ${hasHorizontal ? html`<div class=\"branch-horizontal\"></div>` : nothing}\n ${type === TreeBranchType.corner\n ? html`<div class=\"branch-elbow\"></div>`\n : nothing}\n </div>`;\n }\n\n private renderTerminalHeader() {\n if (this.terminalType === TreeTerminalType.aggregatedHeader) {\n return html`<div class=\"terminal-header\" aria-hidden=\"true\">\n <obi-alert-header-aggregated-iec></obi-alert-header-aggregated-iec>\n </div>`;\n }\n if (this.terminalType === TreeTerminalType.groupHeader) {\n return html`<div class=\"terminal-header\" aria-hidden=\"true\">\n <obi-alert-header-group-iec></obi-alert-header-group-iec>\n </div>`;\n }\n return nothing;\n }\n\n override render() {\n return html`\n <div\n class=${classMap({\n wrapper: true,\n checked: this.checked,\n disabled: this.disabled,\n 'has-icon': this.hasLeadingIcon,\n })}\n role=\"treeitem\"\n aria-expanded=${ifDefined(this.expandable ? this.expanded : undefined)}\n aria-current=${ifDefined(this.checked ? 'page' : undefined)}\n aria-selected=${ifDefined(this.checked ? 'true' : undefined)}\n aria-disabled=${ifDefined(this.disabled ? 'true' : undefined)}\n tabindex=${this.disabled ? -1 : this.focusable ? 0 : -1}\n @click=${this.activate}\n @keydown=${this.handleKeydown}\n >\n <div class=\"visible-wrapper\">\n <div class=\"tree-node-row\">\n ${this.branches.map((branch) => this.renderBranch(branch))}\n <div class=\"terminal\">\n ${this.isRoot || this.isBlankAncestry\n ? nothing\n : html`<div class=\"terminal-connector\"></div>`}\n ${!this.isRoot &&\n !this.isBlankAncestry &&\n this.expandable &&\n this.expanded\n ? html`<div class=\"terminal-dropdown\"></div>`\n : nothing}\n ${this.expandable\n ? html`<div class=\"chevron\" aria-hidden=\"true\">\n <obi-chevron-right-google></obi-chevron-right-google>\n </div>`\n : nothing}\n ${this.renderTerminalHeader()}\n </div>\n </div>\n <div class=\"label-container\">\n ${this.hasLeadingIcon\n ? html`<div class=\"leading-icon\">\n <slot name=\"icon\"></slot>\n </div>`\n : nothing}\n <span part=\"label\" class=\"label\">${this.label}</span>\n </div>\n ${this.hasAlertBadge\n ? html`<obc-badge\n class=\"alert-badge\"\n .type=${this.alertType}\n .number=${this.alertCount}\n ></obc-badge>`\n : nothing}\n </div>\n </div>\n `;\n }\n\n static override styles = unsafeCSS(componentStyle);\n}\n\nexport type ObcTreeNavigationItemExpandToggleEvent = CustomEvent<boolean>;\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'obc-tree-navigation-item': ObcTreeNavigationItem;\n }\n}\n"],"names":["TreeBranchType","TreeTerminalType"],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ObcTreeNavigationItem } from '../components/tree-navigation-item/tree-navigation-item.js';
|
|
2
|
+
/**
|
|
3
|
+
* Host-specific row resolution. The two tree hosts differ only in row tags,
|
|
4
|
+
* expand-state storage, and DOM shape, so injecting these hooks lets one
|
|
5
|
+
* navigator drive both.
|
|
6
|
+
*/
|
|
7
|
+
export interface TreeRovingAdapter<Row extends HTMLElement = HTMLElement> {
|
|
8
|
+
/** Top-level rows, in document order. */
|
|
9
|
+
getRows(): Row[];
|
|
10
|
+
/** Direct child rows of a row, in document order. */
|
|
11
|
+
childRows(row: Row): Row[];
|
|
12
|
+
isGroup(row: Row): boolean;
|
|
13
|
+
isExpanded(row: Row): boolean;
|
|
14
|
+
setExpanded(row: Row, expanded: boolean): void;
|
|
15
|
+
/** The focusable `obc-tree-navigation-item` for a row (a leaf is itself). */
|
|
16
|
+
innerItem(row: Row): ObcTreeNavigationItem | null;
|
|
17
|
+
/** Defaults to false when omitted. */
|
|
18
|
+
isDisabled?(row: Row): boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Implements the [WAI-ARIA Tree View](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/)
|
|
22
|
+
* keyboard pattern for a tree host: a single tab stop (roving tabindex) plus
|
|
23
|
+
* Up/Down, Left/Right (collapse/expand → parent/child), and Home/End. Type-ahead
|
|
24
|
+
* and `*` are intentionally out of scope.
|
|
25
|
+
*
|
|
26
|
+
* The host owns the DOM and the event wiring; this class owns the navigation
|
|
27
|
+
* logic. The host calls {@link handleKeydown} from its `keydown` listener and
|
|
28
|
+
* {@link refresh} whenever the structure changes, and supplies a
|
|
29
|
+
* {@link TreeRovingAdapter} for row resolution. Focus only moves on user keys;
|
|
30
|
+
* `refresh` re-points the roving tabindex without stealing focus.
|
|
31
|
+
*/
|
|
32
|
+
export declare class TreeRovingNavigator<Row extends HTMLElement = HTMLElement> {
|
|
33
|
+
private readonly adapter;
|
|
34
|
+
private readonly host;
|
|
35
|
+
/** The row that currently holds the single tab stop (roving tabindex). */
|
|
36
|
+
private activeRow?;
|
|
37
|
+
constructor(host: HTMLElement, adapter: TreeRovingAdapter<Row>);
|
|
38
|
+
private isDisabled;
|
|
39
|
+
/**
|
|
40
|
+
* Depth-first list of every visible row: descend into a group only when it is
|
|
41
|
+
* expanded. Disabled rows are included so {@link refresh} can re-point to a
|
|
42
|
+
* visible ancestor; arrow navigation filters them out via {@link navigableRows}.
|
|
43
|
+
*/
|
|
44
|
+
private visibleRows;
|
|
45
|
+
/** Visible rows that arrow navigation can land on (enabled rows). */
|
|
46
|
+
private navigableRows;
|
|
47
|
+
/** The parent row of a row within this tree, or undefined for a top-level row. */
|
|
48
|
+
private parentRow;
|
|
49
|
+
/**
|
|
50
|
+
* Rebuild the roving tabindex: exactly one navigable row is `focusable`. Keeps
|
|
51
|
+
* the active row if still navigable; else falls back to its nearest navigable
|
|
52
|
+
* ancestor, else the first navigable row. Never moves focus.
|
|
53
|
+
*/
|
|
54
|
+
refresh(): void;
|
|
55
|
+
private setActiveRow;
|
|
56
|
+
/**
|
|
57
|
+
* The originating tree row for a keydown. A group's header lives in the
|
|
58
|
+
* group's own shadow root, so the host-root check resolves a header to its
|
|
59
|
+
* group rather than the inner header item.
|
|
60
|
+
*/
|
|
61
|
+
private rowFromEvent;
|
|
62
|
+
/** Whether an element is one of this tree's rows (group or leaf). */
|
|
63
|
+
private isRow;
|
|
64
|
+
/** Handle a `keydown`; returns true if the key was consumed. */
|
|
65
|
+
handleKeydown(event: KeyboardEvent): boolean;
|
|
66
|
+
private moveByOffset;
|
|
67
|
+
private moveToEdge;
|
|
68
|
+
private onArrowRight;
|
|
69
|
+
private onArrowLeft;
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=tree-roving-navigator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-roving-navigator.d.ts","sourceRoot":"","sources":["../../src/internal/tree-roving-navigator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,qBAAqB,EAAC,MAAM,4DAA4D,CAAC;AAEjG;;;;GAIG;AACH,MAAM,WAAW,iBAAiB,CAAC,GAAG,SAAS,WAAW,GAAG,WAAW;IACtE,yCAAyC;IACzC,OAAO,IAAI,GAAG,EAAE,CAAC;IACjB,qDAAqD;IACrD,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC;IAC3B,OAAO,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC;IAC3B,UAAU,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC;IAC9B,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/C,6EAA6E;IAC7E,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,qBAAqB,GAAG,IAAI,CAAC;IAClD,sCAAsC;IACtC,UAAU,CAAC,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC;CAChC;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,mBAAmB,CAAC,GAAG,SAAS,WAAW,GAAG,WAAW;IACpE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAyB;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IAEnC,0EAA0E;IAC1E,OAAO,CAAC,SAAS,CAAC,CAAM;gBAEZ,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,iBAAiB,CAAC,GAAG,CAAC;IAK9D,OAAO,CAAC,UAAU;IAIlB;;;;OAIG;IACH,OAAO,CAAC,WAAW;IAcnB,qEAAqE;IACrE,OAAO,CAAC,aAAa;IAIrB,kFAAkF;IAClF,OAAO,CAAC,SAAS;IAkBjB;;;;OAIG;IACH,OAAO,IAAI,IAAI;IAuBf,OAAO,CAAC,YAAY;IAcpB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAepB,qEAAqE;IACrE,OAAO,CAAC,KAAK;IAWb,gEAAgE;IAChE,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO;IA4B5C,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,UAAU;IAMlB,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,WAAW;CASpB"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
class TreeRovingNavigator {
|
|
2
|
+
constructor(host, adapter) {
|
|
3
|
+
this.adapter = adapter;
|
|
4
|
+
this.host = host;
|
|
5
|
+
}
|
|
6
|
+
isDisabled(row) {
|
|
7
|
+
return this.adapter.isDisabled?.(row) ?? false;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Depth-first list of every visible row: descend into a group only when it is
|
|
11
|
+
* expanded. Disabled rows are included so {@link refresh} can re-point to a
|
|
12
|
+
* visible ancestor; arrow navigation filters them out via {@link navigableRows}.
|
|
13
|
+
*/
|
|
14
|
+
visibleRows() {
|
|
15
|
+
const out = [];
|
|
16
|
+
const walk = (rows) => {
|
|
17
|
+
for (const row of rows) {
|
|
18
|
+
out.push(row);
|
|
19
|
+
if (this.adapter.isGroup(row) && this.adapter.isExpanded(row)) {
|
|
20
|
+
walk(this.adapter.childRows(row));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
walk(this.adapter.getRows());
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/** Visible rows that arrow navigation can land on (enabled rows). */
|
|
28
|
+
navigableRows() {
|
|
29
|
+
return this.visibleRows().filter((row) => !this.isDisabled(row));
|
|
30
|
+
}
|
|
31
|
+
/** The parent row of a row within this tree, or undefined for a top-level row. */
|
|
32
|
+
parentRow(target) {
|
|
33
|
+
let found;
|
|
34
|
+
const walk = (rows, parent) => {
|
|
35
|
+
for (const row of rows) {
|
|
36
|
+
if (row === target) {
|
|
37
|
+
found = parent;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (this.adapter.isGroup(row)) {
|
|
41
|
+
walk(this.adapter.childRows(row), row);
|
|
42
|
+
if (found !== void 0) return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
walk(this.adapter.getRows(), void 0);
|
|
47
|
+
return found;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Rebuild the roving tabindex: exactly one navigable row is `focusable`. Keeps
|
|
51
|
+
* the active row if still navigable; else falls back to its nearest navigable
|
|
52
|
+
* ancestor, else the first navigable row. Never moves focus.
|
|
53
|
+
*/
|
|
54
|
+
refresh() {
|
|
55
|
+
const navigable = this.navigableRows();
|
|
56
|
+
if (navigable.length === 0) {
|
|
57
|
+
this.activeRow = void 0;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
let next;
|
|
61
|
+
if (this.activeRow && navigable.includes(this.activeRow)) {
|
|
62
|
+
next = this.activeRow;
|
|
63
|
+
} else if (this.activeRow) {
|
|
64
|
+
let ancestor = this.parentRow(this.activeRow);
|
|
65
|
+
while (ancestor && !navigable.includes(ancestor)) {
|
|
66
|
+
ancestor = this.parentRow(ancestor);
|
|
67
|
+
}
|
|
68
|
+
next = ancestor ?? navigable[0];
|
|
69
|
+
} else {
|
|
70
|
+
next = navigable[0];
|
|
71
|
+
}
|
|
72
|
+
this.setActiveRow(next, false);
|
|
73
|
+
}
|
|
74
|
+
setActiveRow(row, moveFocus) {
|
|
75
|
+
this.activeRow = row;
|
|
76
|
+
for (const candidate of this.visibleRows()) {
|
|
77
|
+
const item2 = this.adapter.innerItem(candidate);
|
|
78
|
+
if (item2) item2.focusable = candidate === row;
|
|
79
|
+
}
|
|
80
|
+
if (!moveFocus) return;
|
|
81
|
+
const item = this.adapter.innerItem(row);
|
|
82
|
+
if (item) item.focus();
|
|
83
|
+
else queueMicrotask(() => this.adapter.innerItem(row)?.focus());
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The originating tree row for a keydown. A group's header lives in the
|
|
87
|
+
* group's own shadow root, so the host-root check resolves a header to its
|
|
88
|
+
* group rather than the inner header item.
|
|
89
|
+
*/
|
|
90
|
+
rowFromEvent(event) {
|
|
91
|
+
const root = this.host.getRootNode();
|
|
92
|
+
for (const target of event.composedPath()) {
|
|
93
|
+
if (target instanceof HTMLElement && target.getRootNode() === root && this.isRow(target)) {
|
|
94
|
+
return target;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return void 0;
|
|
98
|
+
}
|
|
99
|
+
/** Whether an element is one of this tree's rows (group or leaf). */
|
|
100
|
+
isRow(el) {
|
|
101
|
+
const tops = this.adapter.getRows();
|
|
102
|
+
const stack = [...tops];
|
|
103
|
+
while (stack.length) {
|
|
104
|
+
const row = stack.pop();
|
|
105
|
+
if (row === el) return true;
|
|
106
|
+
if (this.adapter.isGroup(row)) stack.push(...this.adapter.childRows(row));
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
/** Handle a `keydown`; returns true if the key was consumed. */
|
|
111
|
+
handleKeydown(event) {
|
|
112
|
+
const row = this.rowFromEvent(event);
|
|
113
|
+
if (!row) return false;
|
|
114
|
+
switch (event.key) {
|
|
115
|
+
case "ArrowDown":
|
|
116
|
+
this.moveByOffset(row, 1);
|
|
117
|
+
return true;
|
|
118
|
+
case "ArrowUp":
|
|
119
|
+
this.moveByOffset(row, -1);
|
|
120
|
+
return true;
|
|
121
|
+
case "ArrowRight":
|
|
122
|
+
this.onArrowRight(row);
|
|
123
|
+
return true;
|
|
124
|
+
case "ArrowLeft":
|
|
125
|
+
this.onArrowLeft(row);
|
|
126
|
+
return true;
|
|
127
|
+
case "Home":
|
|
128
|
+
this.moveToEdge(true);
|
|
129
|
+
return true;
|
|
130
|
+
case "End":
|
|
131
|
+
this.moveToEdge(false);
|
|
132
|
+
return true;
|
|
133
|
+
default:
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
moveByOffset(row, offset) {
|
|
138
|
+
const rows = this.navigableRows();
|
|
139
|
+
const index = rows.indexOf(row);
|
|
140
|
+
if (index === -1) return;
|
|
141
|
+
const target = rows[index + offset];
|
|
142
|
+
if (target) this.setActiveRow(target, true);
|
|
143
|
+
}
|
|
144
|
+
moveToEdge(first) {
|
|
145
|
+
const rows = this.navigableRows();
|
|
146
|
+
if (rows.length === 0) return;
|
|
147
|
+
this.setActiveRow(first ? rows[0] : rows[rows.length - 1], true);
|
|
148
|
+
}
|
|
149
|
+
onArrowRight(row) {
|
|
150
|
+
if (!this.adapter.isGroup(row)) return;
|
|
151
|
+
if (!this.adapter.isExpanded(row)) {
|
|
152
|
+
this.adapter.setExpanded(row, true);
|
|
153
|
+
this.refresh();
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const child = this.adapter.childRows(row).find((c) => !this.isDisabled(c));
|
|
157
|
+
if (child) this.setActiveRow(child, true);
|
|
158
|
+
}
|
|
159
|
+
onArrowLeft(row) {
|
|
160
|
+
if (this.adapter.isGroup(row) && this.adapter.isExpanded(row)) {
|
|
161
|
+
this.adapter.setExpanded(row, false);
|
|
162
|
+
this.refresh();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const parent = this.parentRow(row);
|
|
166
|
+
if (parent) this.setActiveRow(parent, true);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
export {
|
|
170
|
+
TreeRovingNavigator
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=tree-roving-navigator.js.map
|