@aquera/nile-elements 1.7.8 → 1.8.0

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 (111) hide show
  1. package/README.md +6 -0
  2. package/dist/index.cjs.js +1 -1
  3. package/dist/index.esm.js +1 -1
  4. package/dist/index.js +862 -520
  5. package/dist/nile-context-menu/index.cjs.js +2 -0
  6. package/dist/nile-context-menu/index.cjs.js.map +1 -0
  7. package/dist/nile-context-menu/index.esm.js +1 -0
  8. package/dist/nile-context-menu/nile-context-menu.cjs.js +2 -0
  9. package/dist/nile-context-menu/nile-context-menu.cjs.js.map +1 -0
  10. package/dist/nile-context-menu/nile-context-menu.css.cjs.js +2 -0
  11. package/dist/nile-context-menu/nile-context-menu.css.cjs.js.map +1 -0
  12. package/dist/nile-context-menu/nile-context-menu.css.esm.js +51 -0
  13. package/dist/nile-context-menu/nile-context-menu.esm.js +25 -0
  14. package/dist/nile-context-menu-group/index.cjs.js +2 -0
  15. package/dist/nile-context-menu-group/index.cjs.js.map +1 -0
  16. package/dist/nile-context-menu-group/index.esm.js +1 -0
  17. package/dist/nile-context-menu-group/nile-context-menu-group.cjs.js +2 -0
  18. package/dist/nile-context-menu-group/nile-context-menu-group.cjs.js.map +1 -0
  19. package/dist/nile-context-menu-group/nile-context-menu-group.css.cjs.js +2 -0
  20. package/dist/nile-context-menu-group/nile-context-menu-group.css.cjs.js.map +1 -0
  21. package/dist/nile-context-menu-group/nile-context-menu-group.css.esm.js +20 -0
  22. package/dist/nile-context-menu-group/nile-context-menu-group.esm.js +11 -0
  23. package/dist/nile-context-menu-item/index.cjs.js +2 -0
  24. package/dist/nile-context-menu-item/index.cjs.js.map +1 -0
  25. package/dist/nile-context-menu-item/index.esm.js +1 -0
  26. package/dist/nile-context-menu-item/nile-context-menu-item.cjs.js +2 -0
  27. package/dist/nile-context-menu-item/nile-context-menu-item.cjs.js.map +1 -0
  28. package/dist/nile-context-menu-item/nile-context-menu-item.css.cjs.js +2 -0
  29. package/dist/nile-context-menu-item/nile-context-menu-item.css.cjs.js.map +1 -0
  30. package/dist/nile-context-menu-item/nile-context-menu-item.css.esm.js +72 -0
  31. package/dist/nile-context-menu-item/nile-context-menu-item.esm.js +20 -0
  32. package/dist/nile-context-submenu/index.cjs.js +2 -0
  33. package/dist/nile-context-submenu/index.cjs.js.map +1 -0
  34. package/dist/nile-context-submenu/index.esm.js +1 -0
  35. package/dist/nile-context-submenu/nile-context-submenu.cjs.js +2 -0
  36. package/dist/nile-context-submenu/nile-context-submenu.cjs.js.map +1 -0
  37. package/dist/nile-context-submenu/nile-context-submenu.esm.js +3 -0
  38. package/dist/nile-status-light/index.cjs.js +2 -0
  39. package/dist/nile-status-light/index.cjs.js.map +1 -0
  40. package/dist/nile-status-light/index.esm.js +1 -0
  41. package/dist/nile-status-light/nile-status-light.cjs.js +2 -0
  42. package/dist/nile-status-light/nile-status-light.cjs.js.map +1 -0
  43. package/dist/nile-status-light/nile-status-light.css.cjs.js +2 -0
  44. package/dist/nile-status-light/nile-status-light.css.cjs.js.map +1 -0
  45. package/dist/nile-status-light/nile-status-light.css.esm.js +136 -0
  46. package/dist/nile-status-light/nile-status-light.esm.js +13 -0
  47. package/dist/src/index.d.ts +4 -0
  48. package/dist/src/index.js +4 -0
  49. package/dist/src/index.js.map +1 -1
  50. package/dist/src/nile-context-menu/index.d.ts +3 -0
  51. package/dist/src/nile-context-menu/index.js +4 -0
  52. package/dist/src/nile-context-menu/index.js.map +1 -0
  53. package/dist/src/nile-context-menu/nile-context-menu.css.d.ts +10 -0
  54. package/dist/src/nile-context-menu/nile-context-menu.css.js +127 -0
  55. package/dist/src/nile-context-menu/nile-context-menu.css.js.map +1 -0
  56. package/dist/src/nile-context-menu/nile-context-menu.d.ts +123 -0
  57. package/dist/src/nile-context-menu/nile-context-menu.js +625 -0
  58. package/dist/src/nile-context-menu/nile-context-menu.js.map +1 -0
  59. package/dist/src/nile-context-menu-group/index.d.ts +1 -0
  60. package/dist/src/nile-context-menu-group/index.js +2 -0
  61. package/dist/src/nile-context-menu-group/index.js.map +1 -0
  62. package/dist/src/nile-context-menu-group/nile-context-menu-group.css.d.ts +9 -0
  63. package/dist/src/nile-context-menu-group/nile-context-menu-group.css.js +29 -0
  64. package/dist/src/nile-context-menu-group/nile-context-menu-group.css.js.map +1 -0
  65. package/dist/src/nile-context-menu-group/nile-context-menu-group.d.ts +28 -0
  66. package/dist/src/nile-context-menu-group/nile-context-menu-group.js +55 -0
  67. package/dist/src/nile-context-menu-group/nile-context-menu-group.js.map +1 -0
  68. package/dist/src/nile-context-menu-item/index.d.ts +1 -0
  69. package/dist/src/nile-context-menu-item/index.js +2 -0
  70. package/dist/src/nile-context-menu-item/index.js.map +1 -0
  71. package/dist/src/nile-context-menu-item/nile-context-menu-item.css.d.ts +9 -0
  72. package/dist/src/nile-context-menu-item/nile-context-menu-item.css.js +81 -0
  73. package/dist/src/nile-context-menu-item/nile-context-menu-item.css.js.map +1 -0
  74. package/dist/src/nile-context-menu-item/nile-context-menu-item.d.ts +45 -0
  75. package/dist/src/nile-context-menu-item/nile-context-menu-item.js +96 -0
  76. package/dist/src/nile-context-menu-item/nile-context-menu-item.js.map +1 -0
  77. package/dist/src/nile-context-submenu/index.d.ts +1 -0
  78. package/dist/src/nile-context-submenu/index.js +2 -0
  79. package/dist/src/nile-context-submenu/index.js.map +1 -0
  80. package/dist/src/nile-context-submenu/nile-context-submenu.d.ts +60 -0
  81. package/dist/src/nile-context-submenu/nile-context-submenu.js +324 -0
  82. package/dist/src/nile-context-submenu/nile-context-submenu.js.map +1 -0
  83. package/dist/src/nile-status-light/index.d.ts +1 -0
  84. package/dist/src/nile-status-light/index.js +2 -0
  85. package/dist/src/nile-status-light/index.js.map +1 -0
  86. package/dist/src/nile-status-light/nile-status-light.css.d.ts +9 -0
  87. package/dist/src/nile-status-light/nile-status-light.css.js +145 -0
  88. package/dist/src/nile-status-light/nile-status-light.css.js.map +1 -0
  89. package/dist/src/nile-status-light/nile-status-light.d.ts +53 -0
  90. package/dist/src/nile-status-light/nile-status-light.js +108 -0
  91. package/dist/src/nile-status-light/nile-status-light.js.map +1 -0
  92. package/dist/src/version.js +1 -1
  93. package/dist/src/version.js.map +1 -1
  94. package/dist/tsconfig.tsbuildinfo +1 -1
  95. package/package.json +2 -1
  96. package/src/index.ts +4 -0
  97. package/src/nile-context-menu/index.ts +12 -0
  98. package/src/nile-context-menu/nile-context-menu.css.ts +130 -0
  99. package/src/nile-context-menu/nile-context-menu.ts +698 -0
  100. package/src/nile-context-menu-group/index.ts +1 -0
  101. package/src/nile-context-menu-group/nile-context-menu-group.css.ts +31 -0
  102. package/src/nile-context-menu-group/nile-context-menu-group.ts +55 -0
  103. package/src/nile-context-menu-item/index.ts +5 -0
  104. package/src/nile-context-menu-item/nile-context-menu-item.css.ts +83 -0
  105. package/src/nile-context-menu-item/nile-context-menu-item.ts +112 -0
  106. package/src/nile-context-submenu/index.ts +1 -0
  107. package/src/nile-context-submenu/nile-context-submenu.ts +322 -0
  108. package/src/nile-status-light/index.ts +1 -0
  109. package/src/nile-status-light/nile-status-light.css.ts +146 -0
  110. package/src/nile-status-light/nile-status-light.ts +111 -0
  111. package/vscode-html-custom-data.json +140 -4
@@ -0,0 +1,625 @@
1
+ /**
2
+ * Copyright Aquera Inc 2026
3
+ *
4
+ * This source code is licensed under the BSD-3-Clause license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ var NileContextMenu_1;
8
+ import { __decorate } from "tslib";
9
+ import { html } from 'lit';
10
+ import { customElement, property, query, state } from 'lit/decorators.js';
11
+ import { styles } from './nile-context-menu.css';
12
+ import NileElement from '../internal/nile-element';
13
+ import '../nile-floating-panel';
14
+ import '../nile-context-menu-group';
15
+ import '../nile-context-menu-item';
16
+ import '../nile-context-submenu';
17
+ const ITEM_TAG = 'nile-context-menu-item';
18
+ const SUBMENU_TAG = 'nile-context-submenu';
19
+ const MENU_CONTAINER_CLASS = 'nile-context-menu__menu';
20
+ const OBSERVED_ITEM_ATTRS = ['value', 'disabled'];
21
+ let proxyIdSeq = 0;
22
+ /**
23
+ * Nile context-menu component.
24
+ *
25
+ * @tag nile-context-menu
26
+ *
27
+ * @slot Default menu content.
28
+ *
29
+ * @event nile-change Fired on menu lifecycle and selection. `detail.type` is one of
30
+ * `'open'`, `'click'`, or `'close'`; remaining fields depend on the type.
31
+ */
32
+ let NileContextMenu = NileContextMenu_1 = class NileContextMenu extends NileElement {
33
+ constructor() {
34
+ super(...arguments);
35
+ this.for = '';
36
+ this.trigger = 'right';
37
+ this.skipOn = '';
38
+ this.zIndex = 9999;
39
+ this.items = [];
40
+ this._items = [];
41
+ this._open = false;
42
+ this._openContext = null;
43
+ this._targetEl = null;
44
+ this._previouslyFocused = null;
45
+ this._proxyId = `nile-context-menu-anchor-${++proxyIdSeq}`;
46
+ this._wasDataMode = false;
47
+ this._menuContainerRef = null;
48
+ this._onKeydown = (e) => {
49
+ if (!this._open)
50
+ return;
51
+ switch (e.key) {
52
+ case 'Escape':
53
+ e.preventDefault();
54
+ this.close('escape');
55
+ return;
56
+ case 'Tab':
57
+ this.close('programmatic');
58
+ return;
59
+ case 'ArrowDown':
60
+ e.preventDefault();
61
+ this._moveFocus(1);
62
+ return;
63
+ case 'ArrowUp':
64
+ e.preventDefault();
65
+ this._moveFocus(-1);
66
+ return;
67
+ case 'ArrowRight': {
68
+ const { item } = this._getFocusedItemAnyLevel();
69
+ const sub = item?.querySelector(`:scope > ${SUBMENU_TAG}`);
70
+ if (!sub)
71
+ return;
72
+ e.preventDefault();
73
+ sub.openSubmenu?.();
74
+ requestAnimationFrame(() => {
75
+ sub.focusFirstItem?.();
76
+ });
77
+ return;
78
+ }
79
+ case 'ArrowLeft': {
80
+ const { item, container } = this._getFocusedItemAnyLevel();
81
+ if (!item || !container)
82
+ return;
83
+ const sub = this._findSubmenuOwning(container);
84
+ if (!sub)
85
+ return;
86
+ e.preventDefault();
87
+ sub.closeSubmenu?.();
88
+ sub.parentItem?.focus();
89
+ return;
90
+ }
91
+ case 'Enter':
92
+ case ' ': {
93
+ e.preventDefault();
94
+ const { item } = this._getFocusedItemAnyLevel();
95
+ if (!item || item.disabled)
96
+ return;
97
+ const sub = item.querySelector(`:scope > ${SUBMENU_TAG}`);
98
+ if (sub) {
99
+ sub.openSubmenu?.();
100
+ requestAnimationFrame(() => {
101
+ const panel = sub.shadowRoot?.querySelector(`.${MENU_CONTAINER_CLASS}`);
102
+ const first = panel
103
+ ? this._enabledItemsIn(panel)[0]
104
+ : undefined;
105
+ first?.focus();
106
+ });
107
+ return;
108
+ }
109
+ this._selectItem(item);
110
+ return;
111
+ }
112
+ }
113
+ };
114
+ this._onScroll = (e) => {
115
+ if (!this._open)
116
+ return;
117
+ const path = e.composedPath();
118
+ // Scroll inside the root menu container?
119
+ if (this._menuContainerRef && path.includes(this._menuContainerRef))
120
+ return;
121
+ // Scroll inside any descendant submenu's portaled panel?
122
+ for (const node of path) {
123
+ if (node instanceof HTMLElement && node.classList?.contains(MENU_CONTAINER_CLASS)) {
124
+ return;
125
+ }
126
+ }
127
+ this.close('programmatic');
128
+ };
129
+ this._onOutsidePointer = (e) => {
130
+ if (!this._open)
131
+ return;
132
+ const path = e.composedPath();
133
+ if (this._menuContainerRef && path.includes(this._menuContainerRef))
134
+ return;
135
+ for (const node of path) {
136
+ if (node instanceof HTMLElement &&
137
+ node.classList?.contains(MENU_CONTAINER_CLASS)) {
138
+ return;
139
+ }
140
+ }
141
+ this.close('outside-click');
142
+ };
143
+ this._onDescendantSelect = (e) => {
144
+ if (e.detail?.type !== 'click')
145
+ return;
146
+ this.close('select');
147
+ };
148
+ this._onPanelShown = (e) => {
149
+ e.stopPropagation();
150
+ };
151
+ this._onMenuMouseOver = (e) => {
152
+ const item = e
153
+ .composedPath()
154
+ .find(node => node instanceof HTMLElement &&
155
+ node.tagName.toLowerCase() === ITEM_TAG);
156
+ if (!item || item.disabled)
157
+ return;
158
+ item.focus();
159
+ };
160
+ this._onMenuClick = (e) => {
161
+ const item = e
162
+ .composedPath()
163
+ .find(node => node instanceof HTMLElement &&
164
+ node.tagName.toLowerCase() === ITEM_TAG);
165
+ if (!item || item.disabled)
166
+ return;
167
+ if (item.querySelector(`:scope > ${SUBMENU_TAG}`))
168
+ return;
169
+ this._selectItem(item);
170
+ };
171
+ }
172
+ static get styles() {
173
+ return [styles];
174
+ }
175
+ get _isDataMode() {
176
+ return Array.isArray(this.items) && this.items.length > 0;
177
+ }
178
+ connectedCallback() {
179
+ super.connectedCallback();
180
+ this._ensureProxy();
181
+ this._resolveTarget();
182
+ this._attachTriggers();
183
+ this._lightObserver = new MutationObserver(() => {
184
+ if (this._isDataMode)
185
+ return;
186
+ this._relocateLightChildren();
187
+ });
188
+ this._lightObserver.observe(this, { childList: true });
189
+ }
190
+ updated(changed) {
191
+ super.updated(changed);
192
+ if ((changed.has('for') || changed.has('trigger') || changed.has('skipOn')) && this._open) {
193
+ this.close('programmatic');
194
+ }
195
+ if (changed.has('for')) {
196
+ this._resolveTarget();
197
+ this._attachTriggers();
198
+ }
199
+ else if (changed.has('trigger') || changed.has('skipOn')) {
200
+ this._attachTriggers();
201
+ }
202
+ if (changed.has('items') && this._menuContainerRef) {
203
+ if (this._isDataMode) {
204
+ this._renderDataItems();
205
+ this._wasDataMode = true;
206
+ }
207
+ else if (this._wasDataMode) {
208
+ this._renderDataItems();
209
+ this._wasDataMode = false;
210
+ }
211
+ }
212
+ }
213
+ _resolveTarget() {
214
+ const value = this.for?.trim();
215
+ if (!value) {
216
+ this._targetEl = null;
217
+ return;
218
+ }
219
+ const root = this.getRootNode() ?? document;
220
+ if (/^[#.\[:]/.test(value)) {
221
+ this._targetEl = root.querySelector(value);
222
+ }
223
+ else if ('getElementById' in root) {
224
+ this._targetEl = root.getElementById(value);
225
+ }
226
+ else {
227
+ this._targetEl = document.getElementById(value);
228
+ }
229
+ }
230
+ get targetElement() {
231
+ return this._targetEl;
232
+ }
233
+ _attachTriggers() {
234
+ this._detachTriggers?.();
235
+ this._detachTriggers = undefined;
236
+ const trigger = this.trigger;
237
+ if (trigger === 'manual')
238
+ return;
239
+ const cleanups = [];
240
+ if (trigger === 'global') {
241
+ const skipSelector = this.skipOn?.trim() ?? '';
242
+ const isInteractive = (el) => {
243
+ if (!el || !skipSelector)
244
+ return false;
245
+ return !!el.closest(skipSelector);
246
+ };
247
+ const handler = (e) => {
248
+ const me = e;
249
+ if (isInteractive(me.target))
250
+ return;
251
+ me.preventDefault();
252
+ if (this._open)
253
+ return;
254
+ this.open({
255
+ x: me.clientX,
256
+ y: me.clientY,
257
+ target: me.target ?? undefined,
258
+ originalEvent: me,
259
+ });
260
+ };
261
+ window.addEventListener('contextmenu', handler, true);
262
+ cleanups.push(() => window.removeEventListener('contextmenu', handler, true));
263
+ this._detachTriggers = () => cleanups.forEach(fn => fn());
264
+ return;
265
+ }
266
+ const target = this._targetEl;
267
+ if (!target)
268
+ return;
269
+ const wantsContext = trigger === 'right' || trigger === 'both';
270
+ const wantsClick = trigger === 'left' || trigger === 'both';
271
+ if (wantsContext) {
272
+ const handler = (e) => {
273
+ const me = e;
274
+ me.preventDefault();
275
+ this.open({
276
+ x: me.clientX,
277
+ y: me.clientY,
278
+ target,
279
+ originalEvent: me,
280
+ });
281
+ };
282
+ target.addEventListener('contextmenu', handler);
283
+ cleanups.push(() => target.removeEventListener('contextmenu', handler));
284
+ }
285
+ if (wantsClick) {
286
+ const handler = (e) => {
287
+ const me = e;
288
+ this.open({
289
+ x: me.clientX,
290
+ y: me.clientY,
291
+ target,
292
+ originalEvent: me,
293
+ });
294
+ };
295
+ target.addEventListener('click', handler);
296
+ cleanups.push(() => target.removeEventListener('click', handler));
297
+ }
298
+ if (cleanups.length === 0)
299
+ return;
300
+ this._detachTriggers = () => cleanups.forEach(fn => fn());
301
+ }
302
+ disconnectedCallback() {
303
+ super.disconnectedCallback();
304
+ NileContextMenu_1._openInstances.delete(this);
305
+ this._open = false;
306
+ this._detachTriggers?.();
307
+ this._detachTriggers = undefined;
308
+ this._lightObserver?.disconnect();
309
+ this._lightObserver = undefined;
310
+ this._menuObserver?.disconnect();
311
+ this._menuObserver = undefined;
312
+ this._menuContainerRef?.removeEventListener('click', this._onMenuClick);
313
+ this._menuContainerRef?.removeEventListener('mouseover', this._onMenuMouseOver);
314
+ this._menuContainerRef?.removeEventListener('nile-change', this._onDescendantSelect);
315
+ document.removeEventListener('pointerdown', this._onOutsidePointer, true);
316
+ document.removeEventListener('keydown', this._onKeydown, true);
317
+ window.removeEventListener('scroll', this._onScroll, true);
318
+ this._proxyEl?.remove();
319
+ this._proxyEl = undefined;
320
+ }
321
+ firstUpdated() {
322
+ this._menuContainerRef = this.renderRoot.querySelector('.nile-context-menu__menu');
323
+ if (this._isDataMode) {
324
+ this._renderDataItems();
325
+ this._wasDataMode = true;
326
+ }
327
+ else {
328
+ this._relocateLightChildren();
329
+ }
330
+ if (this._menuContainerRef) {
331
+ this._menuObserver = new MutationObserver(() => this._parseChildren());
332
+ this._menuObserver.observe(this._menuContainerRef, {
333
+ childList: true,
334
+ subtree: true,
335
+ attributes: true,
336
+ attributeFilter: OBSERVED_ITEM_ATTRS,
337
+ });
338
+ this._menuContainerRef.addEventListener('click', this._onMenuClick);
339
+ this._menuContainerRef.addEventListener('mouseover', this._onMenuMouseOver);
340
+ this._menuContainerRef.addEventListener('nile-change', this._onDescendantSelect);
341
+ }
342
+ this._parseChildren();
343
+ }
344
+ _ensureProxy() {
345
+ if (this._proxyEl)
346
+ return;
347
+ const el = document.createElement('div');
348
+ el.id = this._proxyId;
349
+ el.setAttribute('aria-hidden', 'true');
350
+ el.style.cssText =
351
+ 'position: fixed; top: 0; left: 0; width: 0; height: 0; pointer-events: none;';
352
+ document.body.appendChild(el);
353
+ this._proxyEl = el;
354
+ }
355
+ _positionProxyAt(x, y) {
356
+ if (!this._proxyEl)
357
+ return;
358
+ this._proxyEl.style.left = `${x}px`;
359
+ this._proxyEl.style.top = `${y}px`;
360
+ }
361
+ _relocateLightChildren() {
362
+ if (!this._menuContainerRef)
363
+ return;
364
+ const kids = Array.from(this.children);
365
+ if (kids.length === 0)
366
+ return;
367
+ for (const kid of kids) {
368
+ this._menuContainerRef.appendChild(kid);
369
+ }
370
+ }
371
+ _renderDataItems() {
372
+ const container = this._menuContainerRef;
373
+ if (!container)
374
+ return;
375
+ while (container.firstChild)
376
+ container.removeChild(container.firstChild);
377
+ for (const entry of this.items) {
378
+ const node = this._createDataNode(entry);
379
+ if (node)
380
+ container.appendChild(node);
381
+ }
382
+ }
383
+ _createDataNode(entry) {
384
+ if (entry.type === 'group') {
385
+ const group = document.createElement('nile-context-menu-group');
386
+ if (entry.name)
387
+ group.label = entry.name;
388
+ for (const item of entry.data ?? []) {
389
+ group.appendChild(this._createItemNode(item));
390
+ }
391
+ return group;
392
+ }
393
+ return null;
394
+ }
395
+ _createItemNode(data) {
396
+ const item = document.createElement('nile-context-menu-item');
397
+ if (data.id)
398
+ item.id = data.id;
399
+ item.value = data.value ?? data.id ?? '';
400
+ if (data.disabled)
401
+ item.disabled = true;
402
+ if (data.icon) {
403
+ const glyph = document.createElement('nile-glyph');
404
+ glyph.setAttribute('slot', 'icon');
405
+ glyph.setAttribute('name', data.icon);
406
+ if (data.iconSet)
407
+ glyph.setAttribute('set', data.iconSet);
408
+ if (data.iconSize)
409
+ glyph.setAttribute('size', data.iconSize);
410
+ if (data.iconMethod)
411
+ glyph.setAttribute('method', data.iconMethod);
412
+ if (data.iconColor)
413
+ glyph.setAttribute('color', data.iconColor);
414
+ item.appendChild(glyph);
415
+ }
416
+ item.appendChild(document.createTextNode(data.label));
417
+ if (data.submenu && data.submenu.length > 0) {
418
+ const submenu = document.createElement('nile-context-submenu');
419
+ for (const entry of data.submenu) {
420
+ const node = this._createDataNode(entry);
421
+ if (node)
422
+ submenu.appendChild(node);
423
+ }
424
+ item.appendChild(submenu);
425
+ }
426
+ return item;
427
+ }
428
+ _parseChildren() {
429
+ const root = this._menuContainerRef ?? this;
430
+ const all = Array.from(root.querySelectorAll(ITEM_TAG));
431
+ this._items = all.filter(item => !this._isInsideDescendantSubmenu(item, root));
432
+ }
433
+ _isInsideDescendantSubmenu(item, container) {
434
+ let cur = item.parentElement;
435
+ while (cur && cur !== container) {
436
+ if (cur.tagName.toLowerCase() === SUBMENU_TAG)
437
+ return true;
438
+ cur = cur.parentElement;
439
+ }
440
+ return false;
441
+ }
442
+ get menuItems() {
443
+ return this._items;
444
+ }
445
+ getItemByValue(value) {
446
+ return this._items.find(item => item.value === value);
447
+ }
448
+ get isOpen() {
449
+ return this._open;
450
+ }
451
+ open(options) {
452
+ if (this._open)
453
+ return;
454
+ for (const other of NileContextMenu_1._openInstances) {
455
+ if (other !== this)
456
+ other.close('programmatic');
457
+ }
458
+ this._previouslyFocused = document.activeElement;
459
+ this._openContext = {
460
+ x: options.x,
461
+ y: options.y,
462
+ target: options.target ?? null,
463
+ originalEvent: options.originalEvent ?? null,
464
+ };
465
+ this._ensureProxy();
466
+ this._positionProxyAt(options.x, options.y);
467
+ this._open = true;
468
+ NileContextMenu_1._openInstances.add(this);
469
+ document.addEventListener('pointerdown', this._onOutsidePointer, true);
470
+ document.addEventListener('keydown', this._onKeydown, true);
471
+ requestAnimationFrame(() => {
472
+ if (this._open)
473
+ window.addEventListener('scroll', this._onScroll, true);
474
+ });
475
+ this.emit('nile-change', { type: 'open', ...this._openContext });
476
+ }
477
+ close(reason = 'programmatic') {
478
+ if (!this._open)
479
+ return;
480
+ this._open = false;
481
+ this._openContext = null;
482
+ NileContextMenu_1._openInstances.delete(this);
483
+ const subs = this._menuContainerRef?.querySelectorAll(SUBMENU_TAG);
484
+ subs?.forEach(s => s.closeSubmenu?.());
485
+ document.removeEventListener('pointerdown', this._onOutsidePointer, true);
486
+ document.removeEventListener('keydown', this._onKeydown, true);
487
+ window.removeEventListener('scroll', this._onScroll, true);
488
+ const active = document.activeElement;
489
+ const focusInMenu = active === this ||
490
+ this._items.some(item => item === active || item.shadowRoot?.contains(active));
491
+ if (focusInMenu) {
492
+ this._previouslyFocused?.focus?.();
493
+ }
494
+ this._previouslyFocused = null;
495
+ this.emit('nile-change', { type: 'close', reason });
496
+ }
497
+ _enabledItems() {
498
+ return this._items.filter(item => !item.disabled);
499
+ }
500
+ _getFocusedItem() {
501
+ const active = document.activeElement;
502
+ for (const item of this._items) {
503
+ if (item === active || item.shadowRoot?.contains(active))
504
+ return item;
505
+ }
506
+ return null;
507
+ }
508
+ _getFocusedItemAnyLevel() {
509
+ let node = document.activeElement;
510
+ while (node && node.tagName?.toLowerCase() !== ITEM_TAG) {
511
+ const root = node.getRootNode();
512
+ node = root instanceof ShadowRoot ? root.host : node.parentElement;
513
+ }
514
+ if (!node)
515
+ return { item: null, container: null };
516
+ const container = node.closest(`.${MENU_CONTAINER_CLASS}`);
517
+ return { item: node, container };
518
+ }
519
+ _enabledItemsIn(container) {
520
+ const all = Array.from(container.querySelectorAll(ITEM_TAG));
521
+ return all.filter(item => {
522
+ if (item.disabled)
523
+ return false;
524
+ return !this._isInsideDescendantSubmenu(item, container);
525
+ });
526
+ }
527
+ _focusFirstEnabled() {
528
+ const enabled = this._enabledItems();
529
+ enabled[0]?.focus();
530
+ }
531
+ _moveFocus(delta) {
532
+ const { item: focused, container } = this._getFocusedItemAnyLevel();
533
+ const root = container ?? this._menuContainerRef ?? this;
534
+ const enabled = this._enabledItemsIn(root);
535
+ if (enabled.length === 0)
536
+ return;
537
+ const current = focused ? enabled.indexOf(focused) : -1;
538
+ const next = current + delta;
539
+ const wrapped = ((next % enabled.length) + enabled.length) % enabled.length;
540
+ enabled[wrapped].focus();
541
+ }
542
+ _findSubmenuOwning(container) {
543
+ const customElements = window.customElements;
544
+ const ctor = customElements.get(SUBMENU_TAG);
545
+ return ctor?.findByContainer?.(container) ?? null;
546
+ }
547
+ _stop(e) {
548
+ e.stopPropagation();
549
+ }
550
+ _selectItem(item) {
551
+ const detail = {
552
+ id: item.id,
553
+ value: item.value,
554
+ name: (item.textContent ?? '').trim(),
555
+ target: this._openContext?.target ?? null,
556
+ originalEvent: this._openContext?.originalEvent ?? null,
557
+ };
558
+ try {
559
+ item.onSelect?.(detail);
560
+ }
561
+ catch (err) {
562
+ console.error('[nile-context-menu] onSelect callback threw:', err);
563
+ }
564
+ this.emit('nile-change', { type: 'click', ...detail });
565
+ this.close('select');
566
+ }
567
+ render() {
568
+ return html `
569
+ <nile-floating-panel
570
+ for=${this._proxyId}
571
+ trigger="manual"
572
+ placement="bottom-start"
573
+ panelClass="nile-context-menu-panel"
574
+ .zIndex=${this.zIndex}
575
+ ?open=${this._open}
576
+ .interactive=${true}
577
+ .hideOnClick=${false}
578
+ .closeOnEscape=${false}
579
+ .arrow=${'none'}
580
+ .distance=${0}
581
+ @nile-init=${this._stop}
582
+ @nile-destroy=${this._stop}
583
+ @nile-show=${this._stop}
584
+ @nile-hide=${this._stop}
585
+ @nile-after-show=${this._onPanelShown}
586
+ @nile-after-hide=${this._stop}
587
+ @nile-toggle=${this._stop}
588
+ @nile-visibility-change=${this._stop}
589
+ >
590
+ <div class="nile-context-menu__menu" role="menu"></div>
591
+ </nile-floating-panel>
592
+ `;
593
+ }
594
+ };
595
+ NileContextMenu._openInstances = new Set();
596
+ __decorate([
597
+ property({ attribute: true, type: String, reflect: true })
598
+ ], NileContextMenu.prototype, "for", void 0);
599
+ __decorate([
600
+ property({ attribute: true, type: String, reflect: true })
601
+ ], NileContextMenu.prototype, "trigger", void 0);
602
+ __decorate([
603
+ property({ attribute: true, type: String, reflect: true })
604
+ ], NileContextMenu.prototype, "skipOn", void 0);
605
+ __decorate([
606
+ property({ attribute: true, type: Number, reflect: true })
607
+ ], NileContextMenu.prototype, "zIndex", void 0);
608
+ __decorate([
609
+ property({ attribute: false })
610
+ ], NileContextMenu.prototype, "items", void 0);
611
+ __decorate([
612
+ query('nile-floating-panel')
613
+ ], NileContextMenu.prototype, "_floatingPanel", void 0);
614
+ __decorate([
615
+ state()
616
+ ], NileContextMenu.prototype, "_items", void 0);
617
+ __decorate([
618
+ state()
619
+ ], NileContextMenu.prototype, "_open", void 0);
620
+ NileContextMenu = NileContextMenu_1 = __decorate([
621
+ customElement('nile-context-menu')
622
+ ], NileContextMenu);
623
+ export { NileContextMenu };
624
+ export default NileContextMenu;
625
+ //# sourceMappingURL=nile-context-menu.js.map