@orbytes/astrolab 0.4.0-next.1 → 0.4.0-next.2

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 (50) hide show
  1. package/README.md +184 -84
  2. package/bin/pin-gallery.mjs +53 -19
  3. package/defaults.mjs +7 -20
  4. package/docs/PIN-CONTRACT.md +76 -10
  5. package/docs/PIN.md +93 -23
  6. package/index.d.ts +1 -7
  7. package/index.mjs +14 -81
  8. package/package.json +2 -2
  9. package/src/Home.astro +7 -8
  10. package/src/LabHead.astro +1 -1
  11. package/src/chrome/ActionsMenu.astro +97 -0
  12. package/src/chrome/ComponentCard.astro +9 -2
  13. package/src/chrome/Nav.astro +36 -10
  14. package/src/chrome/Panel.astro +17 -4
  15. package/src/chrome/Properties.astro +104 -0
  16. package/src/chrome/SectionsTree.astro +128 -0
  17. package/src/chrome/Shell.astro +20 -6
  18. package/src/chrome/StoryView.astro +103 -162
  19. package/src/chrome/Tree.astro +56 -53
  20. package/src/chrome/ViewportControls.astro +136 -61
  21. package/src/chrome/ViewportStage.astro +26 -3
  22. package/src/chrome/icons.ts +9 -0
  23. package/src/chrome/marks-client.ts +26 -53
  24. package/src/chrome/model.ts +14 -0
  25. package/src/chrome/navbar-client.ts +324 -0
  26. package/src/chrome/params-client.ts +434 -0
  27. package/src/chrome/pins-data.ts +42 -9
  28. package/src/chrome/shell-client.ts +99 -3
  29. package/src/chrome/trees.ts +112 -7
  30. package/src/chrome/viewport-client.ts +68 -242
  31. package/src/chrome/views/Assets.astro +21 -6
  32. package/src/chrome/views/Pages.astro +90 -54
  33. package/src/chrome/views/Placeholder.astro +3 -3
  34. package/src/chrome/views/Tasks.astro +12 -40
  35. package/src/core/LICENSE-astrobook +5 -0
  36. package/src/core/utils/kebab-case.ts +2 -2
  37. package/src/pin/board.mjs +25 -15
  38. package/src/pin/index.mjs +34 -20
  39. package/src/pin/tickets.mjs +6 -5
  40. package/src/pin/toolbar.js +81 -3
  41. package/src/shell/Browse.astro +35 -10
  42. package/src/shell/lab-index.ts +5 -4
  43. package/src/shell/lab-params.ts +113 -6
  44. package/src/shell/live-files.mjs +212 -10
  45. package/src/shell/marks.mjs +17 -41
  46. package/src/ui/components/preview-layout.astro +17 -0
  47. package/src/ui/components/theme-script.astro +4 -3
  48. package/src/ui/lab.css +2167 -566
  49. package/virtual.d.ts +0 -4
  50. package/bin/lab-cull.mjs +0 -401
@@ -1,24 +1,33 @@
1
1
  ---
2
2
  // A component's page — <subpath>/dashboard/<storyId>, the route Astrobook's core generates for every
3
- // story (../core/lib/pages/story.astro renders this). REDESIGNED 2026-09-24:
3
+ // story (../core/lib/pages/story.astro renders this).
4
4
  //
5
- // · the story is an <iframe> of its bare route on a canvas you size (./ViewportStage.astro), no
6
- // longer rendered inline beside the chrome — so this page carries none of the site's CSS;
7
- // · the tree stops at the component, and the component's stories are TABS here, a dropdown past
8
- // four; previous / next (and j / k) step through every story of the tier in tree order;
9
- // · the component's marks (responsive, marked for deletion) live in the status menu behind its
10
- // pills, and everything that leaves the lab (VS Code, full screen, staging, a link) in ⋯.
5
+ // · the story is an <iframe> of its bare route on a canvas you size (./ViewportStage.astro), never
6
+ // rendered inline beside the chrome — so this page carries none of the site's CSS;
7
+ // · the tree stops at the component; the component's variants are ALWAYS a dropdown ("Default
8
+ // 1 of 3"), at any count, and previous / next (and j / k) step through every variant of the
9
+ // tier in tree order (decided 2026-09-24: no tabs in the navbar);
10
+ // · the identity is the name, the version and an eye: no pills. The eye opens the Properties
11
+ // panel — live and slot, open pins, the responsive marks, the source file (./Properties.astro);
12
+ // · everything that leaves the lab, and the pin and preview switches, are in ⋯ (./ActionsMenu.astro).
11
13
  //
12
- // The navbar is interim — ops/navbar-brief.md is the design it will be replaced with.
14
+ // THE NAVBAR (Figma `Astrolab` › Lab / Components › Navbar, View=Story) is one grid laid out by a
15
+ // container query on its own width, because the sidebars change the main area and never the
16
+ // viewport: ONE row when the navbar is 1040px wide or more — identity · variants · previous / next
17
+ // · device switch + Viewport field · Comment · Parameters · ⋯ — otherwise TWO rows, the viewport
18
+ // row last, and under 552px three or four, the identity on a row of its own with ⋯. The rules are
19
+ // lab.css › "The navbar" and "The narrow navbar".
13
20
  import Shell from "./Shell.astro";
14
21
  import Tree from "./Tree.astro";
15
22
  import Icon from "./Icon.astro";
23
+ import Properties from "./Properties.astro";
24
+ import ActionsMenu from "./ActionsMenu.astro";
16
25
  import ViewportControls from "./ViewportControls.astro";
17
26
  import ViewportStage from "./ViewportStage.astro";
18
- import { hrefs, labConfig, type Crumb, type TreeNode } from "./model";
27
+ import { hrefs, tasksBase, type Crumb, type TreeNode } from "./model";
19
28
  import { componentsOf, tierTree } from "./trees";
20
29
  import { pinCounts } from "./pins-data";
21
- import { buildLabIndex, isSectionLike, livePillText, liveTitle, tierLabel } from "../shell/lab-index";
30
+ import { buildLabIndex, isSectionLike, tierLabel } from "../shell/lab-index";
22
31
 
23
32
  interface Props {
24
33
  story: string;
@@ -38,21 +47,23 @@ const prev = at > 0 ? sequence[at - 1] : null;
38
47
  const next = at >= 0 && at < sequence.length - 1 ? sequence[at + 1] : null;
39
48
 
40
49
  const pins = await pinCounts();
41
- const openPins = pins.openFor(mod.componentFile);
50
+ const tally = pins.forFile(mod.componentFile);
42
51
  const liveStory = mod.stories.find((s) => s.live);
43
52
  const section = isSectionLike(item);
44
- const tierDef = labConfig.tiers.find((t) => t.id === item.tier);
45
- const cullable = Boolean(tierDef?.cullable);
46
- const blocked = mod.live
47
- ? "live on the site — cannot be marked"
53
+ const variantAt = mod.stories.indexOf(item) + 1;
54
+
55
+ // The Properties panel's "On the site" lines: every page the component is mounted on, or what
56
+ // uses it, or the honest grey "Not on any page".
57
+ const site = liveStory
58
+ ? liveStory.liveOn.map((m) => ({ text: `Live on ${m.page} — slot ${m.slot} of ${m.of}`, live: true }))
48
59
  : mod.usedBy.length
49
- ? `used by ${mod.usedBy.length} live component${mod.usedBy.length === 1 ? "" : "s"} — cannot be marked`
50
- : null;
60
+ ? [{ text: `Used by ${mod.usedBy.length}`, live: true }]
61
+ : [{ text: "Not on any page", live: false }];
51
62
 
52
63
  // Level 2's breadcrumb names the PANEL's place — Site › Sections — as the design does; where the
53
64
  // component sits inside the tier is the tree's job, and which component it is, the navbar's.
54
65
  const crumbs: Crumb[] = [{ label: "Site" }, { label: tierLabel(item.tier), href: hrefs.tier(item.tier) }];
55
- const TAB_LIMIT = 4;
66
+ const plural = (n: number, word: string) => `${n} ${word}${n === 1 ? "" : "s"}`;
56
67
  ---
57
68
 
58
69
  <Shell
@@ -72,180 +83,110 @@ const TAB_LIMIT = 4;
72
83
  >
73
84
  <Tree slot="panel" nodes={tree} />
74
85
 
75
- <Fragment slot="navbar">
76
- <div class="lab-navbar__id">
77
- <h1 class="lab-navbar__title">
78
- {mod.moduleName}
79
- {mod.version && <small>{mod.version}</small>}
80
- </h1>
81
- <button class="lab-status-btn" type="button" popovertarget="lab-status-menu" title="Status and marks">
82
- {liveStory && <span class="lab-pill lab-pill--live" title={liveTitle(liveStory)}>{livePillText(liveStory)}</span>}
83
- {!mod.live && mod.usedBy.length > 0 && <span class="lab-pill lab-pill--used">used by {mod.usedBy.length}</span>}
84
- {!mod.live && !mod.usedBy.length && <span class="lab-pill">unused</span>}
85
- {mod.responsive && (
86
- <span class:list={["lab-pill", mod.responsive.done ? "lab-pill--responsive" : "lab-pill--unresponsive"]} data-lab-resp-pill>
87
- {mod.responsive.done ? "responsive" : "not responsive"}
88
- </span>
89
- )}
90
- <span class="lab-pill lab-pill--marked" data-lab-cull-pill hidden>marked for deletion</span>
91
- {openPins > 0 && <span class="lab-pill lab-pill--brand">{openPins} open pin{openPins === 1 ? "" : "s"}</span>}
92
- <Icon name="chevron-down" size="sm" />
93
- </button>
86
+ <div class="lab-nb" slot="navbar" data-lab-nb>
87
+ <div class="lab-nb__id">
88
+ <div class="lab-nb__line">
89
+ <h1 class="lab-nb__name" title={mod.moduleName}>{mod.moduleName}</h1>
90
+ {mod.version && <span class="lab-nb__tag">{mod.version}</span>}
91
+ <Properties
92
+ site={site}
93
+ pins={tasksBase ? { ...tally, href: `${tasksBase}/all` } : null}
94
+ responsive={mod.responsive ? { file: mod.moduleFile, ...mod.responsive } : null}
95
+ source={mod.componentFile}
96
+ />
97
+ </div>
94
98
  </div>
95
99
 
96
- <nav class="lab-tabs" aria-label="Stories">
97
- {
98
- mod.stories.length <= TAB_LIMIT ? (
100
+ <div class="lab-nb__variants">
101
+ <button
102
+ class="lab-variant-btn"
103
+ type="button"
104
+ popovertarget="lab-variant-menu"
105
+ title={`${plural(mod.stories.length, "variant")} · j / k step`}
106
+ >
107
+ <span class="lab-variant-btn__name">{item.storyName}</span>
108
+ <span class="lab-variant-btn__count">{variantAt} of {mod.stories.length}</span>
109
+ <Icon name="chevron-down" size="sm" />
110
+ </button>
111
+ <div class="lab-menu lab-menu--variants" id="lab-variant-menu" popover role="menu" data-lab-align="end">
112
+ <p class="lab-menu__label">{plural(mod.stories.length, "variant")}</p>
113
+ {
99
114
  mod.stories.map((s) => (
100
115
  <a
101
- class="lab-tab"
116
+ class="lab-menu__item"
117
+ role="menuitemradio"
118
+ aria-checked={String(s.storyId === item.storyId)}
102
119
  href={s.dashboardUrl}
103
- aria-current={s.storyId === item.storyId ? "page" : undefined}
104
120
  data-lab-keep-viewport
105
121
  >
106
- {s.storyName}
122
+ <span class="lab-menu__mark"><Icon name="check" size="sm" class="lab-menu__tick" /></span>
123
+ <span class="lab-menu__text">{s.storyName}</span>
107
124
  </a>
108
125
  ))
109
- ) : (
110
- <>
111
- <button class="lab-btn lab-btn--sm" type="button" popovertarget="lab-story-menu">
112
- {item.storyName}
113
- <span class="lab-tabs__count">{mod.stories.indexOf(item) + 1} / {mod.stories.length}</span>
114
- <Icon name="chevron-down" />
115
- </button>
116
- <div class="lab-menu" id="lab-story-menu" popover role="menu">
117
- <p class="lab-menu__label">{mod.stories.length} stories</p>
118
- {mod.stories.map((s) => (
119
- <a
120
- class="lab-menu__item"
121
- role="menuitemradio"
122
- aria-checked={String(s.storyId === item.storyId)}
123
- href={s.dashboardUrl}
124
- data-lab-keep-viewport
125
- >
126
- {s.storyName}
127
- <Icon name="check" size="sm" class="lab-menu__tick" />
128
- </a>
129
- ))}
130
- </div>
131
- </>
132
- )
133
- }
134
- </nav>
126
+ }
127
+ <hr />
128
+ <p class="lab-menu__note">j / k — next and previous</p>
129
+ </div>
135
130
 
136
- <div class="lab-navbar__tools">
137
- <div class="lab-seg" role="group" aria-label="Previous and next story">
131
+ <div class="lab-nb__step" role="group" aria-label="Previous and next variant">
138
132
  {
139
133
  prev ? (
140
- <a class="lab-btn lab-btn--sm lab-btn--icon" href={prev.dashboardUrl} title={`Previous: ${prev.moduleName} — ${prev.storyName} (k)`} data-lab-step="prev" data-lab-keep-viewport>
141
- <Icon name="chevron-left" />
142
- <span class="lab-visually-hidden">Previous story</span>
134
+ <a
135
+ class="lab-tool lab-tool--icon"
136
+ href={prev.dashboardUrl}
137
+ title={`Previous variant · k — ${prev.moduleName} — ${prev.storyName}`}
138
+ data-lab-step="prev"
139
+ data-lab-keep-viewport
140
+ >
141
+ <Icon name="chevron-left" size="sm" />
142
+ <span class="lab-visually-hidden">Previous variant</span>
143
143
  </a>
144
144
  ) : (
145
- <span class="lab-btn lab-btn--sm lab-btn--icon" aria-disabled="true" data-lab-step><Icon name="chevron-left" /></span>
145
+ <button class="lab-tool lab-tool--icon" type="button" disabled data-lab-step>
146
+ <Icon name="chevron-left" size="sm" />
147
+ <span class="lab-visually-hidden">Previous variant</span>
148
+ </button>
146
149
  )
147
150
  }
148
151
  {
149
152
  next ? (
150
- <a class="lab-btn lab-btn--sm lab-btn--icon" href={next.dashboardUrl} title={`Next: ${next.moduleName} — ${next.storyName} (j)`} data-lab-step="next" data-lab-keep-viewport>
151
- <Icon name="chevron-right" />
152
- <span class="lab-visually-hidden">Next story</span>
153
+ <a
154
+ class="lab-tool lab-tool--icon"
155
+ href={next.dashboardUrl}
156
+ title={`Next variant · j — ${next.moduleName} — ${next.storyName}`}
157
+ data-lab-step="next"
158
+ data-lab-keep-viewport
159
+ >
160
+ <Icon name="chevron-right" size="sm" />
161
+ <span class="lab-visually-hidden">Next variant</span>
153
162
  </a>
154
163
  ) : (
155
- <span class="lab-btn lab-btn--sm lab-btn--icon" aria-disabled="true" data-lab-step><Icon name="chevron-right" /></span>
164
+ <button class="lab-tool lab-tool--icon" type="button" disabled data-lab-step>
165
+ <Icon name="chevron-right" size="sm" />
166
+ <span class="lab-visually-hidden">Next variant</span>
167
+ </button>
156
168
  )
157
169
  }
158
170
  </div>
159
171
  </div>
160
172
 
161
- <ViewportControls defaultW={section ? 1440 : 960} defaultH={section ? 900 : 540} kind={section ? "section" : "component"} />
173
+ <ViewportControls
174
+ defaultW={section ? 1440 : 960}
175
+ defaultH={section ? 900 : 540}
176
+ kind={section ? "section" : "component"}
177
+ comment={Boolean(tasksBase)}
178
+ parameters
179
+ />
162
180
 
163
- <div class="lab-navbar__tools">
164
- <button class="lab-btn lab-btn--sm lab-btn--icon" type="button" popovertarget="lab-actions-menu" title="More">
165
- <Icon name="more" />
166
- <span class="lab-visually-hidden">More actions</span>
167
- </button>
168
- </div>
169
-
170
- <div class="lab-menu" id="lab-actions-menu" popover role="menu" data-lab-align="end">
171
- {
172
- item.editorUrl && (
173
- <a class="lab-menu__item" role="menuitem" href={item.editorUrl}>
174
- <Icon name="code" />
175
- Open in VS Code
176
- </a>
177
- )
178
- }
179
- <a class="lab-menu__item" role="menuitem" href={item.storyUrl} target="_blank" rel="noopener">
180
- <Icon name="expand" />
181
- Open full screen
182
- </a>
183
- {
184
- item.stagingUrl && (
185
- <a class="lab-menu__item" role="menuitem" href={item.stagingUrl} target="_blank" rel="noopener">
186
- <Icon name="external" />
187
- View on staging
188
- </a>
189
- )
190
- }
191
- <button class="lab-menu__item" type="button" role="menuitem" data-lab-copy-link>
192
- <Icon name="link" />
193
- <span data-lab-copy-label>Copy link to this size</span>
194
- </button>
195
- </div>
196
-
197
- <div class="lab-menu lab-status-menu" id="lab-status-menu" popover role="menu">
198
- <p class="lab-menu__label">On the site</p>
199
- <p class="lab-status-menu__fact">
200
- {
201
- liveStory
202
- ? liveStory.liveOn.map((m) => `${m.page} · slot ${m.slot} of ${m.of}`).join(", ")
203
- : mod.usedBy.length
204
- ? `Used by ${mod.usedBy.map((f) => f.split("/").pop()).join(", ")}`
205
- : "Not mounted by any page, and nothing live uses it."
206
- }
207
- </p>
208
- {
209
- mod.componentFile && (
210
- <p class="lab-status-menu__fact lab-mono">{mod.componentFile}</p>
211
- )
212
- }
213
- {
214
- mod.responsive && (
215
- <>
216
- <hr />
217
- <p class="lab-menu__label">Responsive</p>
218
- <label class="lab-menu__item lab-menu__check">
219
- <input type="checkbox" data-lab-mark="approved" data-file={mod.moduleFile} checked={mod.responsive.approved} disabled />
220
- Approved for responsive work
221
- </label>
222
- <label class="lab-menu__item lab-menu__check">
223
- <input type="checkbox" data-lab-mark="done" data-file={mod.moduleFile} checked={mod.responsive.done} disabled />
224
- Made responsive
225
- </label>
226
- </>
227
- )
228
- }
229
- {
230
- cullable && (
231
- <>
232
- <hr />
233
- <p class="lab-menu__label">Clean-up</p>
234
- <label class="lab-menu__item lab-menu__check" title={blocked ?? undefined}>
235
- <input type="checkbox" data-lab-mark="cull" data-file={mod.moduleFile} data-blocked={blocked ?? undefined} disabled />
236
- Mark for deletion
237
- </label>
238
- {blocked && <p class="lab-status-menu__fact">{blocked}</p>}
239
- </>
240
- )
241
- }
242
- <p class="lab-status-menu__note" data-lab-marks-note>Marks can be changed on the dev server only.</p>
181
+ <div class="lab-nb__actions">
182
+ <ActionsMenu editorUrl={item.editorUrl} fullScreenUrl={item.storyUrl} stagingUrl={item.stagingUrl} pins={Boolean(tasksBase)} />
243
183
  </div>
244
- </Fragment>
184
+ </div>
245
185
 
246
- <ViewportStage src={item.storyUrl} title={`${mod.moduleName} — ${item.storyName}`} />
186
+ <ViewportStage src={item.storyUrl} title={`${mod.moduleName} — ${item.storyName}`} pins={Boolean(tasksBase)} tasksHref={tasksBase ? `${tasksBase}/all` : null} parameters />
247
187
  </Shell>
248
188
 
249
189
  <script>
250
190
  import "./marks-client";
191
+ import "./navbar-client";
251
192
  </script>
@@ -3,7 +3,14 @@
3
3
  // groups are <details>, rows are links. The shell script adds memory (which groups you closed),
4
4
  // search, the filter menu and j/k on top. Connector lines are CSS on the <li>, so the tree carries
5
5
  // no connector markup at all.
6
+ //
7
+ // Rows carry no icons and read at 12px (decided 2026-09-24, to condense level 2). A node's `icon`
8
+ // is still accepted — the data layer keeps it — and simply not drawn.
9
+ //
10
+ // A tree whose root rows carry a slot is the Sections tree, which draws its own rows and sort
11
+ // control (./SectionsTree.astro).
6
12
  import Icon from "./Icon.astro";
13
+ import SectionsTree from "./SectionsTree.astro";
7
14
  import type { TreeNode } from "./model";
8
15
 
9
16
  interface Props {
@@ -14,6 +21,7 @@ interface Props {
14
21
  }
15
22
 
16
23
  const { nodes, depth = 0, root = true } = Astro.props;
24
+ const sections = root && nodes.some((node) => node.slot !== undefined);
17
25
 
18
26
  /** Whether a node is, or holds, the current page — those groups render open whatever was stored. */
19
27
  const holdsCurrent = (node: TreeNode): boolean =>
@@ -22,62 +30,57 @@ const holdsCurrent = (node: TreeNode): boolean =>
22
30
  const searchText = (node: TreeNode) => `${node.label} ${node.search ?? ""}`.toLowerCase();
23
31
  ---
24
32
 
25
- <ul class={root ? "lab-tree" : "lab-tree__group"} role={root ? "tree" : "group"} data-lab-tree={root ? "" : undefined}>
26
- {
27
- nodes.map((node) => {
28
- const hasChildren = (node.children?.length ?? 0) > 0;
29
- const icon = node.icon ?? (hasChildren ? "folder" : "page");
30
- const trailing = (
31
- <>
32
- {node.count !== undefined && <span class="lab-tree__count">{node.count}</span>}
33
- {node.dot && <span class={`lab-dot lab-dot--${node.dot}`} title={node.dotTitle} />}
34
- </>
35
- );
36
- return (
37
- <li
38
- class="lab-tree__node"
39
- role="none"
40
- style={`--lab-d:${depth}`}
41
- data-lab-search={searchText(node)}
42
- data-lab-facets={node.facets}
43
- >
44
- {hasChildren ? (
45
- <details open data-lab-tree-id={node.id} data-lab-holds-current={holdsCurrent(node) ? "" : undefined}>
46
- <summary
33
+ {
34
+ sections ? (
35
+ <SectionsTree nodes={nodes} />
36
+ ) : (
37
+ <ul class={root ? "lab-tree" : "lab-tree__group"} role={root ? "tree" : "group"} data-lab-tree={root ? "" : undefined}>
38
+ {nodes.map((node) => {
39
+ const hasChildren = (node.children?.length ?? 0) > 0;
40
+ const trailing = (
41
+ <>
42
+ {node.count !== undefined && <span class="lab-tree__count">{node.count}</span>}
43
+ {node.dot && <span class={`lab-dot lab-dot--${node.dot}`} title={node.dotTitle} />}
44
+ </>
45
+ );
46
+ return (
47
+ <li
48
+ class="lab-tree__node"
49
+ role="none"
50
+ style={`--lab-d:${depth}`}
51
+ data-lab-search={searchText(node)}
52
+ data-lab-facets={node.facets}
53
+ >
54
+ {hasChildren ? (
55
+ <details open data-lab-tree-id={node.id} data-lab-holds-current={holdsCurrent(node) ? "" : undefined}>
56
+ <summary class="lab-tree__row" role="treeitem" aria-current={node.current ? "page" : undefined} title={node.title}>
57
+ <span class="lab-tree__label">{node.href ? <a href={node.href}>{node.label}</a> : node.label}</span>
58
+ {trailing}
59
+ <Icon name="chevron-down" size="sm" class="lab-tree__chevron" />
60
+ </summary>
61
+ <Astro.self nodes={node.children!} depth={depth + 1} root={false} />
62
+ </details>
63
+ ) : node.href && !node.disabled ? (
64
+ <a
47
65
  class="lab-tree__row"
48
66
  role="treeitem"
67
+ href={node.href}
49
68
  aria-current={node.current ? "page" : undefined}
50
69
  title={node.title}
70
+ data-lab-leaf
51
71
  >
52
- <Icon name={icon} size="sm" />
53
- <span class="lab-tree__label">{node.href ? <a href={node.href}>{node.label}</a> : node.label}</span>
72
+ <span class="lab-tree__label">{node.label}</span>
54
73
  {trailing}
55
- <Icon name="chevron-down" size="sm" class="lab-tree__chevron" />
56
- </summary>
57
- <Astro.self nodes={node.children!} depth={depth + 1} root={false} />
58
- </details>
59
- ) : node.href && !node.disabled ? (
60
- <a
61
- class="lab-tree__row"
62
- role="treeitem"
63
- href={node.href}
64
- aria-current={node.current ? "page" : undefined}
65
- title={node.title}
66
- data-lab-leaf
67
- >
68
- <Icon name={icon} size="sm" />
69
- <span class="lab-tree__label">{node.label}</span>
70
- {trailing}
71
- </a>
72
- ) : (
73
- <span class="lab-tree__row" role="treeitem" aria-disabled="true" title={node.title}>
74
- <Icon name={icon} size="sm" />
75
- <span class="lab-tree__label">{node.label}</span>
76
- {trailing}
77
- </span>
78
- )}
79
- </li>
80
- );
81
- })
82
- }
83
- </ul>
74
+ </a>
75
+ ) : (
76
+ <span class="lab-tree__row" role="treeitem" aria-disabled="true" title={node.title}>
77
+ <span class="lab-tree__label">{node.label}</span>
78
+ {trailing}
79
+ </span>
80
+ )}
81
+ </li>
82
+ );
83
+ })}
84
+ </ul>
85
+ )
86
+ }