le-kit 0.1.19 → 0.2.1

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 (143) hide show
  1. package/dist/cjs/index-pT2cVC5w.js.map +1 -1
  2. package/dist/cjs/le-button_13.cjs.entry.js +17 -21
  3. package/dist/cjs/le-icon.cjs.entry.js +108 -0
  4. package/dist/cjs/le-kit.cjs.js +1 -1
  5. package/dist/cjs/le-multiselect.cjs.entry.js +3 -3
  6. package/dist/cjs/le-navigation.cjs.entry.js +505 -0
  7. package/dist/cjs/le-number-input.cjs.entry.js +1 -1
  8. package/dist/cjs/le-round-progress.cjs.entry.js +1 -1
  9. package/dist/cjs/le-segmented-control.cjs.entry.js +1 -1
  10. package/dist/cjs/le-stack.cjs.entry.js +1 -1
  11. package/dist/cjs/le-tab-bar.cjs.entry.js +1 -1
  12. package/dist/cjs/le-tab-panel.cjs.entry.js +2 -2
  13. package/dist/cjs/le-tab.cjs.entry.js +1 -1
  14. package/dist/cjs/le-tabs.cjs.entry.js +2 -2
  15. package/dist/cjs/le-tag.cjs.entry.js +1 -1
  16. package/dist/cjs/le-turntable.cjs.entry.js +1 -1
  17. package/dist/cjs/loader.cjs.js +1 -1
  18. package/dist/collection/assets/icons/chevron-down.svg +3 -0
  19. package/dist/collection/collection-manifest.json +2 -0
  20. package/dist/collection/components/le-collapse/le-collapse.css +3 -3
  21. package/dist/collection/components/le-collapse/le-collapse.js +11 -15
  22. package/dist/collection/components/le-collapse/le-collapse.js.map +1 -1
  23. package/dist/collection/components/le-icon/le-icon.css +13 -0
  24. package/dist/collection/components/le-icon/le-icon.js +168 -0
  25. package/dist/collection/components/le-icon/le-icon.js.map +1 -0
  26. package/dist/collection/components/le-multiselect/le-multiselect.js +3 -3
  27. package/dist/collection/components/le-navigation/le-navigation.css +323 -0
  28. package/dist/collection/components/le-navigation/le-navigation.js +768 -0
  29. package/dist/collection/components/le-navigation/le-navigation.js.map +1 -0
  30. package/dist/collection/components/le-number-input/le-number-input.js +1 -1
  31. package/dist/collection/components/le-popover/le-popover.js +3 -3
  32. package/dist/collection/components/le-popup/le-popup.js +7 -7
  33. package/dist/collection/components/le-round-progress/le-round-progress.js +1 -1
  34. package/dist/collection/components/le-scroll-progress/le-scroll-progress.js +1 -1
  35. package/dist/collection/components/le-segmented-control/le-segmented-control.js +1 -1
  36. package/dist/collection/components/le-select/le-select.js +2 -2
  37. package/dist/collection/components/le-slot/le-slot.js +1 -1
  38. package/dist/collection/components/le-stack/le-stack.js +1 -1
  39. package/dist/collection/components/le-string-input/le-string-input.js +2 -2
  40. package/dist/collection/components/le-tab/le-tab.js +1 -1
  41. package/dist/collection/components/le-tab-bar/le-tab-bar.js +1 -1
  42. package/dist/collection/components/le-tab-panel/le-tab-panel.js +2 -2
  43. package/dist/collection/components/le-tabs/le-tabs.js +2 -2
  44. package/dist/collection/components/le-tag/le-tag.js +1 -1
  45. package/dist/collection/components/le-turntable/le-turntable.js +1 -1
  46. package/dist/collection/dist/components/assets/custom-elements.json +1278 -533
  47. package/dist/collection/dist/components/assets/icons/chevron-down.json +13 -0
  48. package/dist/collection/dist/components/assets/icons/chevron-down.svg +3 -0
  49. package/dist/collection/types/options.js.map +1 -1
  50. package/dist/components/assets/custom-elements.json +1278 -533
  51. package/dist/components/assets/icons/chevron-down.json +13 -0
  52. package/dist/components/assets/icons/chevron-down.svg +3 -0
  53. package/dist/components/index.js.map +1 -1
  54. package/dist/components/le-button2.js +5 -5
  55. package/dist/components/le-collapse.js +1 -137
  56. package/dist/components/le-collapse.js.map +1 -1
  57. package/dist/components/le-collapse2.js +138 -0
  58. package/dist/components/le-collapse2.js.map +1 -0
  59. package/dist/components/le-icon.d.ts +11 -0
  60. package/dist/components/le-icon.js +9 -0
  61. package/dist/components/le-icon.js.map +1 -0
  62. package/dist/components/le-icon2.js +133 -0
  63. package/dist/components/le-icon2.js.map +1 -0
  64. package/dist/components/le-multiselect.js +3 -3
  65. package/dist/components/le-navigation.d.ts +11 -0
  66. package/dist/components/le-navigation.js +605 -0
  67. package/dist/components/le-navigation.js.map +1 -0
  68. package/dist/components/le-number-input.js +1 -1
  69. package/dist/components/le-popover2.js +3 -3
  70. package/dist/components/le-round-progress.js +1 -1
  71. package/dist/components/le-scroll-progress.js +1 -1
  72. package/dist/components/le-segmented-control.js +1 -1
  73. package/dist/components/le-stack.js +1 -1
  74. package/dist/components/le-tab-bar.js +1 -1
  75. package/dist/components/le-tab-panel.js +2 -2
  76. package/dist/components/le-tab2.js +1 -1
  77. package/dist/components/le-tabs.js +2 -2
  78. package/dist/components/le-tag2.js +1 -1
  79. package/dist/components/le-turntable.js +1 -1
  80. package/dist/docs.json +544 -27
  81. package/dist/esm/index-CNv6tzAt.js.map +1 -1
  82. package/dist/esm/le-button_13.entry.js +17 -21
  83. package/dist/esm/le-icon.entry.js +106 -0
  84. package/dist/esm/le-icon.entry.js.map +1 -0
  85. package/dist/esm/le-kit.js +1 -1
  86. package/dist/esm/le-multiselect.entry.js +3 -3
  87. package/dist/esm/le-navigation.entry.js +503 -0
  88. package/dist/esm/le-navigation.entry.js.map +1 -0
  89. package/dist/esm/le-number-input.entry.js +1 -1
  90. package/dist/esm/le-round-progress.entry.js +1 -1
  91. package/dist/esm/le-segmented-control.entry.js +1 -1
  92. package/dist/esm/le-stack.entry.js +1 -1
  93. package/dist/esm/le-tab-bar.entry.js +1 -1
  94. package/dist/esm/le-tab-panel.entry.js +2 -2
  95. package/dist/esm/le-tab.entry.js +1 -1
  96. package/dist/esm/le-tabs.entry.js +2 -2
  97. package/dist/esm/le-tag.entry.js +1 -1
  98. package/dist/esm/le-turntable.entry.js +1 -1
  99. package/dist/esm/loader.js +1 -1
  100. package/dist/le-kit/dist/components/assets/custom-elements.json +1278 -533
  101. package/dist/le-kit/dist/components/assets/icons/chevron-down.json +13 -0
  102. package/dist/le-kit/dist/components/assets/icons/chevron-down.svg +3 -0
  103. package/dist/le-kit/le-kit.esm.js +1 -1
  104. package/dist/le-kit/{p-3327b089.entry.js → p-0ac4397c.entry.js} +2 -2
  105. package/dist/le-kit/{p-dfd20890.entry.js → p-25a29e69.entry.js} +2 -2
  106. package/dist/le-kit/{p-64c2cee1.entry.js → p-511fbb63.entry.js} +2 -2
  107. package/dist/le-kit/{p-4bdb03cb.entry.js → p-58120921.entry.js} +2 -2
  108. package/dist/le-kit/{p-b7c008de.entry.js → p-5ceb06d8.entry.js} +2 -2
  109. package/dist/le-kit/{p-e3f3cc8f.entry.js → p-9a3bdbe1.entry.js} +2 -2
  110. package/dist/le-kit/p-CNv6tzAt.js.map +1 -1
  111. package/dist/le-kit/{p-f88c804a.entry.js → p-a0d2c580.entry.js} +2 -2
  112. package/dist/le-kit/p-b1dc7e06.entry.js +2 -0
  113. package/dist/le-kit/p-b1dc7e06.entry.js.map +1 -0
  114. package/dist/le-kit/p-bb160082.entry.js +2 -0
  115. package/dist/le-kit/p-bb160082.entry.js.map +1 -0
  116. package/dist/le-kit/{p-da0265c5.entry.js → p-de5638c9.entry.js} +2 -2
  117. package/dist/le-kit/{p-1733d907.entry.js → p-e24d3e33.entry.js} +2 -2
  118. package/dist/le-kit/{p-6acdf000.entry.js → p-ec20e438.entry.js} +2 -2
  119. package/dist/le-kit/p-f2fa3bf5.entry.js +2 -0
  120. package/dist/le-kit/p-f2fa3bf5.entry.js.map +1 -0
  121. package/dist/le-kit/{p-02532919.entry.js → p-f4f2c3e7.entry.js} +2 -2
  122. package/dist/types/components/le-collapse/le-collapse.d.ts +5 -3
  123. package/dist/types/components/le-icon/le-icon.d.ts +28 -0
  124. package/dist/types/components/le-navigation/le-navigation.d.ts +116 -0
  125. package/dist/types/components.d.ts +204 -10
  126. package/dist/types/types/options.d.ts +5 -0
  127. package/package.json +1 -1
  128. package/dist/collection/dist/components/assets/.gitkeep +0 -1
  129. package/dist/components/assets/.gitkeep +0 -1
  130. package/dist/le-kit/dist/components/assets/.gitkeep +0 -1
  131. package/dist/le-kit/p-83007215.entry.js +0 -2
  132. package/dist/le-kit/p-83007215.entry.js.map +0 -1
  133. /package/dist/le-kit/{p-3327b089.entry.js.map → p-0ac4397c.entry.js.map} +0 -0
  134. /package/dist/le-kit/{p-dfd20890.entry.js.map → p-25a29e69.entry.js.map} +0 -0
  135. /package/dist/le-kit/{p-64c2cee1.entry.js.map → p-511fbb63.entry.js.map} +0 -0
  136. /package/dist/le-kit/{p-4bdb03cb.entry.js.map → p-58120921.entry.js.map} +0 -0
  137. /package/dist/le-kit/{p-b7c008de.entry.js.map → p-5ceb06d8.entry.js.map} +0 -0
  138. /package/dist/le-kit/{p-e3f3cc8f.entry.js.map → p-9a3bdbe1.entry.js.map} +0 -0
  139. /package/dist/le-kit/{p-f88c804a.entry.js.map → p-a0d2c580.entry.js.map} +0 -0
  140. /package/dist/le-kit/{p-da0265c5.entry.js.map → p-de5638c9.entry.js.map} +0 -0
  141. /package/dist/le-kit/{p-1733d907.entry.js.map → p-e24d3e33.entry.js.map} +0 -0
  142. /package/dist/le-kit/{p-6acdf000.entry.js.map → p-ec20e438.entry.js.map} +0 -0
  143. /package/dist/le-kit/{p-02532919.entry.js.map → p-f4f2c3e7.entry.js.map} +0 -0
@@ -0,0 +1,768 @@
1
+ import { h, Host, } from "@stencil/core";
2
+ import { classnames, generateId } from "../../utils/utils";
3
+ /**
4
+ * Navigation component with vertical (tree) and horizontal (menu) layouts.
5
+ *
6
+ * - Accepts items as `LeOption[]` or a JSON string.
7
+ * - Supports hierarchical items via `children`.
8
+ * - Supports persisted expansion via `open` on items.
9
+ *
10
+ * @cmsEditable true
11
+ * @cmsCategory Navigation
12
+ */
13
+ export class LeNavigation {
14
+ el;
15
+ /**
16
+ * Navigation items.
17
+ * Can be passed as an array or JSON string (same pattern as le-select).
18
+ */
19
+ items = [];
20
+ /**
21
+ * Layout orientation.
22
+ */
23
+ orientation = 'horizontal';
24
+ /**
25
+ * Horizontal wrapping behavior.
26
+ * If false, overflow behavior depends on `overflowMode`.
27
+ */
28
+ wrap = true;
29
+ /**
30
+ * Overflow behavior for horizontal, non-wrapping menus.
31
+ * - more: moves overflow items into a "More" popover
32
+ * - hamburger: turns the whole nav into a hamburger popover
33
+ */
34
+ overflowMode = 'more';
35
+ /**
36
+ * Active url for automatic selection.
37
+ */
38
+ activeUrl = '';
39
+ /**
40
+ * Enables a search input for the vertical navigation.
41
+ */
42
+ searchable = false;
43
+ /**
44
+ * Placeholder text for the search input.
45
+ */
46
+ searchPlaceholder = 'Search...';
47
+ /**
48
+ * Text shown when no items match the filter.
49
+ */
50
+ emptyText = 'No results found';
51
+ /**
52
+ * Whether submenu popovers should include a filter input.
53
+ */
54
+ submenuSearchable = false;
55
+ /**
56
+ * Fired when a navigation item is activated.
57
+ *
58
+ * This event is cancelable. Call `event.preventDefault()` to prevent
59
+ * default browser navigation and implement custom routing.
60
+ */
61
+ leNavItemSelect;
62
+ /**
63
+ * Fired when a tree branch is toggled.
64
+ */
65
+ leNavItemToggle;
66
+ searchQuery = '';
67
+ openState = {};
68
+ overflowIds = [];
69
+ hamburgerActive = false;
70
+ submenuQueries = {};
71
+ navContainerEl;
72
+ measureEl;
73
+ measureMoreEl;
74
+ topItemEls = new Map();
75
+ popoverRefs = new Map();
76
+ resizeObserver;
77
+ instanceId = generateId('le-nav');
78
+ handleLayoutInputsChange() {
79
+ this.scheduleOverflowRecalc();
80
+ }
81
+ componentDidLoad() {
82
+ this.setupResizeObserver();
83
+ this.scheduleOverflowRecalc();
84
+ }
85
+ disconnectedCallback() {
86
+ this.resizeObserver?.disconnect();
87
+ this.resizeObserver = undefined;
88
+ }
89
+ componentDidRender() {
90
+ // In case refs change after render.
91
+ this.scheduleOverflowRecalc();
92
+ }
93
+ setupResizeObserver() {
94
+ this.resizeObserver?.disconnect();
95
+ if (typeof ResizeObserver === 'undefined')
96
+ return;
97
+ this.resizeObserver = new ResizeObserver(() => this.computeOverflow());
98
+ this.observeContainer(this.navContainerEl);
99
+ }
100
+ observeContainer(el) {
101
+ if (!this.resizeObserver)
102
+ return;
103
+ this.resizeObserver.disconnect();
104
+ if (el)
105
+ this.resizeObserver.observe(el);
106
+ }
107
+ scheduleOverflowRecalc() {
108
+ // Avoid work for vertical layout.
109
+ if (this.orientation !== 'horizontal')
110
+ return;
111
+ // Ensure it runs after layout.
112
+ requestAnimationFrame(() => this.computeOverflow());
113
+ }
114
+ get parsedItems() {
115
+ if (typeof this.items === 'string') {
116
+ try {
117
+ return JSON.parse(this.items);
118
+ }
119
+ catch {
120
+ return [];
121
+ }
122
+ }
123
+ return this.items;
124
+ }
125
+ getItemId(item, path) {
126
+ return item.id ?? `${this.instanceId}:${path}`;
127
+ }
128
+ getChildItems(item) {
129
+ return Array.isArray(item.children) ? item.children : [];
130
+ }
131
+ isOpen(item, id) {
132
+ const fromState = this.openState[id];
133
+ if (typeof fromState === 'boolean')
134
+ return fromState;
135
+ return !!item.open;
136
+ }
137
+ setOpen(id, open) {
138
+ if (this.openState[id] === open)
139
+ return;
140
+ this.openState = {
141
+ ...this.openState,
142
+ [id]: open,
143
+ };
144
+ }
145
+ matchesQuery(option, query) {
146
+ if (!query)
147
+ return true;
148
+ const q = query.toLowerCase();
149
+ return (option.label.toLowerCase().includes(q) ||
150
+ (option.description?.toLowerCase().includes(q) ?? false));
151
+ }
152
+ filterTree(items, query, pathPrefix, autoOpen) {
153
+ if (!query)
154
+ return items;
155
+ const result = [];
156
+ items.forEach((item, index) => {
157
+ const path = pathPrefix ? `${pathPrefix}.${index}` : String(index);
158
+ const id = this.getItemId(item, path);
159
+ const children = this.getChildItems(item);
160
+ const filteredChildren = this.filterTree(children, query, path, autoOpen);
161
+ const selfMatch = this.matchesQuery(item, query);
162
+ const childMatch = filteredChildren.length > 0;
163
+ if (selfMatch || childMatch) {
164
+ if (childMatch) {
165
+ autoOpen.add(id);
166
+ }
167
+ if (childMatch && filteredChildren !== children) {
168
+ result.push({
169
+ ...item,
170
+ children: filteredChildren,
171
+ });
172
+ }
173
+ else {
174
+ result.push(item);
175
+ }
176
+ }
177
+ });
178
+ return result;
179
+ }
180
+ handleItemSelect = (e, item, id) => {
181
+ if (item.disabled) {
182
+ e.preventDefault();
183
+ e.stopPropagation();
184
+ return;
185
+ }
186
+ const emitted = this.leNavItemSelect.emit({
187
+ item,
188
+ id,
189
+ href: item.href,
190
+ originalEvent: e,
191
+ });
192
+ if (emitted.defaultPrevented) {
193
+ e.preventDefault();
194
+ }
195
+ };
196
+ handleToggle = (e, item, id) => {
197
+ e.preventDefault();
198
+ e.stopPropagation();
199
+ if (item.disabled)
200
+ return;
201
+ const next = !this.isOpen(item, id);
202
+ this.setOpen(id, next);
203
+ this.leNavItemToggle.emit({
204
+ item,
205
+ id,
206
+ open: next,
207
+ originalEvent: e,
208
+ });
209
+ };
210
+ handleSearchInput = (e) => {
211
+ const target = e.target;
212
+ this.searchQuery = target.value;
213
+ };
214
+ handleSubmenuSearchInput = (submenuId, e) => {
215
+ const target = e.target;
216
+ const value = target.value;
217
+ if (this.submenuQueries[submenuId] === value)
218
+ return;
219
+ this.submenuQueries = {
220
+ ...this.submenuQueries,
221
+ [submenuId]: value,
222
+ };
223
+ // Position may change as items filter.
224
+ requestAnimationFrame(() => this.popoverRefs.get(submenuId)?.updatePosition());
225
+ };
226
+ getTopLevelIds(items) {
227
+ return items.map((item, index) => this.getItemId(item, String(index)));
228
+ }
229
+ computeOverflow() {
230
+ // Only applies to horizontal, non-wrapping navs.
231
+ if (this.orientation !== 'horizontal' || this.wrap) {
232
+ if (this.overflowIds.length)
233
+ this.overflowIds = [];
234
+ if (this.hamburgerActive)
235
+ this.hamburgerActive = false;
236
+ return;
237
+ }
238
+ const container = this.navContainerEl;
239
+ if (!container)
240
+ return;
241
+ const topIds = this.getTopLevelIds(this.parsedItems);
242
+ const widths = topIds.map(id => this.topItemEls.get(id)?.getBoundingClientRect().width ?? 0);
243
+ const totalWidth = widths.reduce((a, b) => a + b, 0);
244
+ const availableWidth = container.getBoundingClientRect().width;
245
+ if (this.overflowMode === 'hamburger') {
246
+ const shouldHamburger = totalWidth > availableWidth;
247
+ if (shouldHamburger !== this.hamburgerActive) {
248
+ this.hamburgerActive = shouldHamburger;
249
+ }
250
+ if (this.overflowIds.length)
251
+ this.overflowIds = [];
252
+ return;
253
+ }
254
+ // overflowMode === 'more'
255
+ this.computeOverflowMoreByWrap(availableWidth);
256
+ }
257
+ computeOverflowMoreByWrap(availableWidth) {
258
+ const container = this.navContainerEl;
259
+ const measure = this.measureEl;
260
+ const measureMore = this.measureMoreEl;
261
+ const items = this.parsedItems;
262
+ if (!container || !measure)
263
+ return;
264
+ // Ensure measurement container matches visible container width.
265
+ measure.style.width = `${availableWidth}px`;
266
+ const allIds = this.getTopLevelIds(items);
267
+ const itemEls = allIds
268
+ .map(id => measure.querySelector(`[data-nav-id="${CSS.escape(id)}"]`))
269
+ .filter((el) => !!el);
270
+ // Reset measurement visibility.
271
+ itemEls.forEach(el => {
272
+ el.style.display = '';
273
+ });
274
+ if (measureMore) {
275
+ measureMore.style.display = 'none';
276
+ }
277
+ if (itemEls.length === 0) {
278
+ if (!this.overflowIds || this.overflowIds.length) {
279
+ this.overflowIds = [];
280
+ }
281
+ return;
282
+ }
283
+ const firstRowTop = Math.min(...itemEls.map(el => el.offsetTop));
284
+ const overflowSet = new Set();
285
+ // Pass 1: detect which items fall onto rows > 1 (without "More" in flow).
286
+ itemEls.forEach(el => {
287
+ const id = el.getAttribute('data-nav-id');
288
+ if (!id)
289
+ return;
290
+ if (el.offsetTop > firstRowTop)
291
+ overflowSet.add(id);
292
+ });
293
+ if (overflowSet.size === 0) {
294
+ if (!this.overflowIds || this.overflowIds.length) {
295
+ this.overflowIds = [];
296
+ }
297
+ return;
298
+ }
299
+ // Pass 2: show "More" and iteratively move items into overflow until "More" fits on row 1.
300
+ if (measureMore) {
301
+ measureMore.style.display = '';
302
+ }
303
+ // Hide currently overflowing items.
304
+ itemEls.forEach(el => {
305
+ const id = el.getAttribute('data-nav-id');
306
+ if (!id)
307
+ return;
308
+ if (overflowSet.has(id))
309
+ el.style.display = 'none';
310
+ });
311
+ const getVisibleItemEls = () => itemEls.filter(el => el.style.display !== 'none');
312
+ while (measureMore) {
313
+ const visible = getVisibleItemEls();
314
+ const rowTop = visible.length ? Math.min(...visible.map(el => el.offsetTop)) : 0;
315
+ if (measureMore.offsetTop <= rowTop)
316
+ break;
317
+ if (visible.length === 0)
318
+ break;
319
+ // Remove one last visible item and retry.
320
+ const last = visible[visible.length - 1];
321
+ const lastId = last.getAttribute('data-nav-id');
322
+ if (!lastId)
323
+ break;
324
+ last.style.display = 'none';
325
+ overflowSet.add(lastId);
326
+ }
327
+ const overflowIds = allIds.filter(id => overflowSet.has(id));
328
+ const same = overflowIds.length === this.overflowIds.length &&
329
+ overflowIds.every((v, i) => v === this.overflowIds[i]);
330
+ if (!same) {
331
+ this.overflowIds = overflowIds;
332
+ }
333
+ }
334
+ renderHorizontalMeasureItem(item, index) {
335
+ const id = this.getItemId(item, String(index));
336
+ const children = this.getChildItems(item);
337
+ const hasChildren = children.length > 0;
338
+ const setRef = (el) => {
339
+ if (el)
340
+ this.topItemEls.set(id, el);
341
+ };
342
+ if (!hasChildren) {
343
+ return (h("div", { class: "h-item", ref: setRef, "data-nav-id": id }, h("span", { class: "h-link" }, item.iconStart && (h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), h("span", { class: "h-label" }, item.label), item.iconEnd && (h("span", { class: "nav-icon nav-icon-end", "aria-hidden": "true" }, item.iconEnd)))));
344
+ }
345
+ return (h("div", { class: "h-item", ref: setRef, "data-nav-id": id }, h("span", { class: "h-trigger" }, h("span", { class: "h-link" }, item.iconStart && (h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), h("span", { class: "h-label" }, item.label)), h("span", { class: "h-submenu-toggle", "aria-hidden": "true" }, h("span", { class: "nav-chevron" }, "\u25BC")))));
346
+ }
347
+ renderVerticalList(items, { depth, pathPrefix, autoOpenIds, searchable, searchQuery, searchPlaceholder, emptyText, submenuId, }) {
348
+ const query = searchQuery ?? '';
349
+ const openFromSearch = autoOpenIds ?? new Set();
350
+ const filtered = query ? this.filterTree(items, query, pathPrefix, openFromSearch) : items;
351
+ return (h("div", { class: classnames('nav-vertical', { 'is-submenu': !!submenuId }) }, searchable && (h("div", { class: "nav-search" }, h("le-string-input", { mode: "default", class: "nav-search-input", placeholder: searchPlaceholder ?? 'Search...', value: query, onInput: (e) => submenuId ? this.handleSubmenuSearchInput(submenuId, e) : this.handleSearchInput(e) }))), filtered.length === 0 ? (h("div", { class: "nav-empty" }, emptyText ?? this.emptyText)) : (h("ul", { class: "nav-list", role: "tree" }, filtered.map((item, index) => {
352
+ const path = pathPrefix ? `${pathPrefix}.${index}` : String(index);
353
+ const id = this.getItemId(item, path);
354
+ const children = this.getChildItems(item);
355
+ const hasChildren = children.length > 0;
356
+ const open = hasChildren && (this.isOpen(item, id) || openFromSearch.has(id));
357
+ const paddingLeft = `calc(var(--le-nav-item-padding-x) + ${depth} * var(--le-spacing-4))`;
358
+ const TagType = item.href && !item.disabled ? 'a' : 'button';
359
+ const attrs = TagType === 'a'
360
+ ? { href: item.href, role: 'treeitem' }
361
+ : { type: 'button', role: 'treeitem' };
362
+ return (h("li", { class: classnames('nav-node', {
363
+ 'disabled': item.disabled,
364
+ 'selected': item.selected || (this.activeUrl && item.href === this.activeUrl),
365
+ open,
366
+ 'has-children': hasChildren,
367
+ }), key: id, role: "none" }, h("div", { class: "nav-row", style: { paddingLeft } }, hasChildren ? (h("button", { type: "button", class: "nav-toggle", "aria-label": open ? 'Collapse' : 'Expand', "aria-expanded": open ? 'true' : 'false', onClick: (e) => this.handleToggle(e, item, id), disabled: item.disabled }, h("le-icon", { name: "chevron-down", class: "nav-chevron", "aria-hidden": "true" }))) : (h("span", { class: "nav-toggle-spacer", "aria-hidden": "true" })), h(TagType, { class: "nav-item", ...attrs, "aria-disabled": item.disabled ? 'true' : undefined, onClick: (e) => {
368
+ // For buttons, also toggle if this is a purely structural node.
369
+ this.handleItemSelect(e, item, id);
370
+ if (!item.href && hasChildren && !item.disabled) {
371
+ this.handleToggle(e, item, id);
372
+ }
373
+ } }, item.iconStart && (h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), h("span", { class: "nav-text" }, h("span", { class: "nav-label" }, item.label), item.description && (h("span", { class: "nav-description" }, item.description))), item.iconEnd && (h("span", { class: "nav-icon nav-icon-end", "aria-hidden": "true" }, item.iconEnd)))), hasChildren && (h("le-collapse", { class: "nav-children", closed: !open, noFading: true, role: "group" }, this.renderVerticalList(children, {
374
+ depth: depth + 1,
375
+ pathPrefix: path,
376
+ autoOpenIds: openFromSearch,
377
+ })))));
378
+ })))));
379
+ }
380
+ renderHorizontalItem(item, index) {
381
+ const id = this.getItemId(item, String(index));
382
+ const children = this.getChildItems(item);
383
+ const hasChildren = children.length > 0;
384
+ if (!hasChildren) {
385
+ const TagType = item.href && !item.disabled ? 'a' : 'button';
386
+ const attrs = TagType === 'a'
387
+ ? { href: item.href, role: 'menuitem' }
388
+ : { type: 'button', role: 'menuitem' };
389
+ return (h("div", { class: "h-item" }, h(TagType, { class: classnames('h-link', {
390
+ disabled: item.disabled,
391
+ selected: item.selected || (this.activeUrl && item.href === this.activeUrl),
392
+ }), ...attrs, "aria-disabled": item.disabled ? 'true' : undefined, onClick: (e) => this.handleItemSelect(e, item, id) }, item.iconStart && (h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), h("span", { class: "h-label" }, item.label), item.iconEnd && (h("span", { class: "nav-icon nav-icon-end", "aria-hidden": "true" }, item.iconEnd)))));
393
+ }
394
+ const submenuId = id;
395
+ return (h("div", { class: "h-item" }, h("le-popover", { ref: el => {
396
+ if (el)
397
+ this.popoverRefs.set(submenuId, el);
398
+ }, mode: "default", showClose: false, closeOnClickOutside: true, closeOnEscape: true, position: "bottom", align: "start", minWidth: "240px" }, h("div", { slot: "trigger", class: classnames('h-trigger', {
399
+ disabled: item.disabled,
400
+ selected: item.selected || (this.activeUrl && item.href === this.activeUrl),
401
+ }), role: "menuitem", "aria-disabled": item.disabled ? 'true' : undefined, onClick: (e) => {
402
+ // Don’t let le-popover auto-toggle from its internal wrapper.
403
+ e.stopPropagation();
404
+ if (item.disabled)
405
+ return;
406
+ if (item.href) {
407
+ this.handleItemSelect(e, item, id);
408
+ }
409
+ else {
410
+ this.popoverRefs.get(submenuId)?.toggle();
411
+ }
412
+ } }, item.href ? (h("a", { class: "h-link", href: item.href, onClick: (e) => {
413
+ e.stopPropagation();
414
+ this.handleItemSelect(e, item, id);
415
+ } }, item.iconStart && (h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), h("span", { class: "h-label" }, item.label))) : (h("button", { type: "button", class: "h-link", onClick: (e) => {
416
+ e.stopPropagation();
417
+ if (item.disabled)
418
+ return;
419
+ this.popoverRefs.get(submenuId)?.toggle();
420
+ } }, item.iconStart && (h("span", { class: "nav-icon", "aria-hidden": "true" }, item.iconStart)), h("span", { class: "h-label" }, item.label))), h("button", { type: "button", class: "h-submenu-toggle", "aria-label": "Open submenu", onClick: (e) => {
421
+ e.preventDefault();
422
+ e.stopPropagation();
423
+ if (item.disabled)
424
+ return;
425
+ this.popoverRefs.get(submenuId)?.toggle();
426
+ } }, h("span", { class: "nav-chevron", "aria-hidden": "true" }, h("le-icon", { name: "chevron-down" })))), h("div", { class: "popover-menu" }, this.renderVerticalList(children, {
427
+ depth: 0,
428
+ pathPrefix: String(index),
429
+ searchable: this.submenuSearchable,
430
+ searchQuery: this.submenuQueries[submenuId] ?? '',
431
+ searchPlaceholder: this.searchPlaceholder,
432
+ emptyText: this.emptyText,
433
+ submenuId,
434
+ })))));
435
+ }
436
+ renderHorizontal() {
437
+ const items = this.parsedItems;
438
+ const overflowSet = new Set(this.overflowIds);
439
+ const overflowItems = [];
440
+ items.forEach((item, index) => {
441
+ const id = this.getItemId(item, String(index));
442
+ if (!this.wrap && this.overflowMode === 'more' && overflowSet.has(id)) {
443
+ overflowItems.push(item);
444
+ }
445
+ });
446
+ // Hamburger mode: show a single trigger if anything overflows.
447
+ if (!this.wrap && this.overflowMode === 'hamburger' && this.hamburgerActive) {
448
+ return (h("div", { class: "nav-horizontal-shell" }, h("div", { class: "nav-horizontal-measure", "aria-hidden": "true", ref: el => {
449
+ this.measureEl = el;
450
+ } }, items.map((item, index) => this.renderHorizontalMeasureItem(item, index)), h("div", { class: "h-item", ref: el => {
451
+ this.measureMoreEl = el;
452
+ } }, h("button", { type: "button", class: "overflow-trigger" }, "More"))), h("div", { class: "nav-horizontal", ref: el => {
453
+ this.navContainerEl = el;
454
+ this.setupResizeObserver();
455
+ this.observeContainer(this.navContainerEl);
456
+ } }, h("le-popover", { mode: "default", showClose: false, closeOnClickOutside: true, closeOnEscape: true, position: "bottom", align: "end", minWidth: "260px" }, h("button", { slot: "trigger", type: "button", class: "overflow-trigger", "aria-label": "Open menu" }, "\u2630"), h("div", { class: "popover-menu" }, this.renderVerticalList(items, { depth: 0, pathPrefix: '' }))))));
457
+ }
458
+ const showMore = !this.wrap && this.overflowMode === 'more' && overflowItems.length > 0;
459
+ return (h("div", { class: "nav-horizontal-shell", role: "menubar" }, h("div", { class: "nav-horizontal-measure", "aria-hidden": "true", ref: el => {
460
+ this.measureEl = el;
461
+ } }, items.map((item, index) => this.renderHorizontalMeasureItem(item, index)), h("div", { class: "h-item", ref: el => {
462
+ this.measureMoreEl = el;
463
+ } }, h("button", { type: "button", class: "overflow-trigger" }, "More"))), h("div", { class: classnames('nav-horizontal', {
464
+ wrap: this.wrap,
465
+ nowrap: !this.wrap,
466
+ }), ref: el => {
467
+ this.navContainerEl = el;
468
+ this.setupResizeObserver();
469
+ this.observeContainer(this.navContainerEl);
470
+ } }, items.map((item, index) => {
471
+ const id = this.getItemId(item, String(index));
472
+ const isOverflow = !this.wrap && this.overflowMode === 'more' && overflowSet.has(id);
473
+ if (isOverflow)
474
+ return null;
475
+ return this.renderHorizontalItem(item, index);
476
+ }), h("div", { class: classnames('more-trigger-wrap', {
477
+ 'is-visible': showMore,
478
+ 'is-measure': !showMore,
479
+ }) }, h("le-popover", { mode: "default", position: "bottom", align: "end", minWidth: "260px", showClose: false }, h("button", { slot: "trigger", type: "button", class: "overflow-trigger", "aria-label": "More" }, "More"), h("div", { class: "popover-menu" }, this.renderVerticalList(overflowItems, { depth: 0, pathPrefix: '' })))))));
480
+ }
481
+ render() {
482
+ const items = this.parsedItems;
483
+ if (this.orientation === 'horizontal') {
484
+ return (h(Host, null, h("le-component", { component: "le-navigation" }, this.renderHorizontal())));
485
+ }
486
+ return (h(Host, null, h("le-component", { component: "le-navigation" }, this.renderVerticalList(items, {
487
+ depth: 0,
488
+ pathPrefix: '',
489
+ searchable: this.searchable,
490
+ searchQuery: this.searchQuery,
491
+ searchPlaceholder: this.searchPlaceholder,
492
+ emptyText: this.emptyText,
493
+ }))));
494
+ }
495
+ static get is() { return "le-navigation"; }
496
+ static get encapsulation() { return "shadow"; }
497
+ static get originalStyleUrls() {
498
+ return {
499
+ "$": ["le-navigation.css"]
500
+ };
501
+ }
502
+ static get styleUrls() {
503
+ return {
504
+ "$": ["le-navigation.css"]
505
+ };
506
+ }
507
+ static get properties() {
508
+ return {
509
+ "items": {
510
+ "type": "string",
511
+ "mutable": false,
512
+ "complexType": {
513
+ "original": "LeOption[] | string",
514
+ "resolved": "LeOption[] | string",
515
+ "references": {
516
+ "LeOption": {
517
+ "location": "import",
518
+ "path": "../../types/options",
519
+ "id": "src/types/options.ts::LeOption"
520
+ }
521
+ }
522
+ },
523
+ "required": false,
524
+ "optional": false,
525
+ "docs": {
526
+ "tags": [],
527
+ "text": "Navigation items.\nCan be passed as an array or JSON string (same pattern as le-select)."
528
+ },
529
+ "getter": false,
530
+ "setter": false,
531
+ "reflect": false,
532
+ "attribute": "items",
533
+ "defaultValue": "[]"
534
+ },
535
+ "orientation": {
536
+ "type": "string",
537
+ "mutable": false,
538
+ "complexType": {
539
+ "original": "'vertical' | 'horizontal'",
540
+ "resolved": "\"horizontal\" | \"vertical\"",
541
+ "references": {}
542
+ },
543
+ "required": false,
544
+ "optional": false,
545
+ "docs": {
546
+ "tags": [],
547
+ "text": "Layout orientation."
548
+ },
549
+ "getter": false,
550
+ "setter": false,
551
+ "reflect": true,
552
+ "attribute": "orientation",
553
+ "defaultValue": "'horizontal'"
554
+ },
555
+ "wrap": {
556
+ "type": "boolean",
557
+ "mutable": false,
558
+ "complexType": {
559
+ "original": "boolean",
560
+ "resolved": "boolean",
561
+ "references": {}
562
+ },
563
+ "required": false,
564
+ "optional": false,
565
+ "docs": {
566
+ "tags": [],
567
+ "text": "Horizontal wrapping behavior.\nIf false, overflow behavior depends on `overflowMode`."
568
+ },
569
+ "getter": false,
570
+ "setter": false,
571
+ "reflect": true,
572
+ "attribute": "wrap",
573
+ "defaultValue": "true"
574
+ },
575
+ "overflowMode": {
576
+ "type": "string",
577
+ "mutable": false,
578
+ "complexType": {
579
+ "original": "'more' | 'hamburger'",
580
+ "resolved": "\"hamburger\" | \"more\"",
581
+ "references": {}
582
+ },
583
+ "required": false,
584
+ "optional": false,
585
+ "docs": {
586
+ "tags": [],
587
+ "text": "Overflow behavior for horizontal, non-wrapping menus.\n- more: moves overflow items into a \"More\" popover\n- hamburger: turns the whole nav into a hamburger popover"
588
+ },
589
+ "getter": false,
590
+ "setter": false,
591
+ "reflect": true,
592
+ "attribute": "overflow-mode",
593
+ "defaultValue": "'more'"
594
+ },
595
+ "activeUrl": {
596
+ "type": "string",
597
+ "mutable": false,
598
+ "complexType": {
599
+ "original": "string",
600
+ "resolved": "string",
601
+ "references": {}
602
+ },
603
+ "required": false,
604
+ "optional": false,
605
+ "docs": {
606
+ "tags": [],
607
+ "text": "Active url for automatic selection."
608
+ },
609
+ "getter": false,
610
+ "setter": false,
611
+ "reflect": false,
612
+ "attribute": "active-url",
613
+ "defaultValue": "''"
614
+ },
615
+ "searchable": {
616
+ "type": "boolean",
617
+ "mutable": false,
618
+ "complexType": {
619
+ "original": "boolean",
620
+ "resolved": "boolean",
621
+ "references": {}
622
+ },
623
+ "required": false,
624
+ "optional": false,
625
+ "docs": {
626
+ "tags": [],
627
+ "text": "Enables a search input for the vertical navigation."
628
+ },
629
+ "getter": false,
630
+ "setter": false,
631
+ "reflect": false,
632
+ "attribute": "searchable",
633
+ "defaultValue": "false"
634
+ },
635
+ "searchPlaceholder": {
636
+ "type": "string",
637
+ "mutable": false,
638
+ "complexType": {
639
+ "original": "string",
640
+ "resolved": "string",
641
+ "references": {}
642
+ },
643
+ "required": false,
644
+ "optional": false,
645
+ "docs": {
646
+ "tags": [],
647
+ "text": "Placeholder text for the search input."
648
+ },
649
+ "getter": false,
650
+ "setter": false,
651
+ "reflect": false,
652
+ "attribute": "search-placeholder",
653
+ "defaultValue": "'Search...'"
654
+ },
655
+ "emptyText": {
656
+ "type": "string",
657
+ "mutable": false,
658
+ "complexType": {
659
+ "original": "string",
660
+ "resolved": "string",
661
+ "references": {}
662
+ },
663
+ "required": false,
664
+ "optional": false,
665
+ "docs": {
666
+ "tags": [],
667
+ "text": "Text shown when no items match the filter."
668
+ },
669
+ "getter": false,
670
+ "setter": false,
671
+ "reflect": false,
672
+ "attribute": "empty-text",
673
+ "defaultValue": "'No results found'"
674
+ },
675
+ "submenuSearchable": {
676
+ "type": "boolean",
677
+ "mutable": false,
678
+ "complexType": {
679
+ "original": "boolean",
680
+ "resolved": "boolean",
681
+ "references": {}
682
+ },
683
+ "required": false,
684
+ "optional": false,
685
+ "docs": {
686
+ "tags": [],
687
+ "text": "Whether submenu popovers should include a filter input."
688
+ },
689
+ "getter": false,
690
+ "setter": false,
691
+ "reflect": false,
692
+ "attribute": "submenu-searchable",
693
+ "defaultValue": "false"
694
+ }
695
+ };
696
+ }
697
+ static get states() {
698
+ return {
699
+ "searchQuery": {},
700
+ "openState": {},
701
+ "overflowIds": {},
702
+ "hamburgerActive": {},
703
+ "submenuQueries": {}
704
+ };
705
+ }
706
+ static get events() {
707
+ return [{
708
+ "method": "leNavItemSelect",
709
+ "name": "leNavItemSelect",
710
+ "bubbles": true,
711
+ "cancelable": true,
712
+ "composed": true,
713
+ "docs": {
714
+ "tags": [],
715
+ "text": "Fired when a navigation item is activated.\n\nThis event is cancelable. Call `event.preventDefault()` to prevent\ndefault browser navigation and implement custom routing."
716
+ },
717
+ "complexType": {
718
+ "original": "LeNavigationItemSelectDetail",
719
+ "resolved": "LeNavigationItemSelectDetail",
720
+ "references": {
721
+ "LeNavigationItemSelectDetail": {
722
+ "location": "local",
723
+ "path": "/home/runner/work/le-kit/le-kit/src/components/le-navigation/le-navigation.tsx",
724
+ "id": "src/components/le-navigation/le-navigation.tsx::LeNavigationItemSelectDetail"
725
+ }
726
+ }
727
+ }
728
+ }, {
729
+ "method": "leNavItemToggle",
730
+ "name": "leNavItemToggle",
731
+ "bubbles": true,
732
+ "cancelable": true,
733
+ "composed": true,
734
+ "docs": {
735
+ "tags": [],
736
+ "text": "Fired when a tree branch is toggled."
737
+ },
738
+ "complexType": {
739
+ "original": "LeNavigationItemToggleDetail",
740
+ "resolved": "LeNavigationItemToggleDetail",
741
+ "references": {
742
+ "LeNavigationItemToggleDetail": {
743
+ "location": "local",
744
+ "path": "/home/runner/work/le-kit/le-kit/src/components/le-navigation/le-navigation.tsx",
745
+ "id": "src/components/le-navigation/le-navigation.tsx::LeNavigationItemToggleDetail"
746
+ }
747
+ }
748
+ }
749
+ }];
750
+ }
751
+ static get elementRef() { return "el"; }
752
+ static get watchers() {
753
+ return [{
754
+ "propName": "items",
755
+ "methodName": "handleLayoutInputsChange"
756
+ }, {
757
+ "propName": "orientation",
758
+ "methodName": "handleLayoutInputsChange"
759
+ }, {
760
+ "propName": "wrap",
761
+ "methodName": "handleLayoutInputsChange"
762
+ }, {
763
+ "propName": "overflowMode",
764
+ "methodName": "handleLayoutInputsChange"
765
+ }];
766
+ }
767
+ }
768
+ //# sourceMappingURL=le-navigation.js.map