@nuralyui/menu 0.0.19 → 0.0.20
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.js +94 -51
- package/index.js.map +1 -1
- package/menu.component.d.ts +38 -3
- package/menu.component.js +351 -45
- package/menu.component.js.map +1 -1
- package/menu.constants.js.map +1 -1
- package/menu.style.js +113 -64
- package/menu.style.js.map +1 -1
- package/menu.types.d.ts +4 -0
- package/menu.types.js.map +1 -1
- package/package.json +1 -1
- package/react.js.map +1 -1
package/menu.component.js
CHANGED
|
@@ -12,7 +12,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
12
12
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
13
13
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
14
14
|
import { LitElement, html, nothing } from 'lit';
|
|
15
|
-
import { customElement, property } from 'lit/decorators.js';
|
|
15
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
16
16
|
import { styles } from './menu.style.js';
|
|
17
17
|
import { NuralyUIBaseMixin } from '@nuralyui/common/mixins';
|
|
18
18
|
import { StateController, KeyboardController, AccessibilityController } from './controllers/index.js';
|
|
@@ -22,10 +22,12 @@ import { StateController, KeyboardController, AccessibilityController } from './
|
|
|
22
22
|
* @example
|
|
23
23
|
* ```html
|
|
24
24
|
* <nr-menu .items=${menuItems}></nr-menu>
|
|
25
|
+
* <nr-menu .items=${menuItems} arrowPosition="left"></nr-menu>
|
|
25
26
|
* ```
|
|
26
27
|
*
|
|
27
28
|
* @fires change - Menu item selected
|
|
28
29
|
* @fires action-click - Menu action clicked
|
|
30
|
+
* @fires label-edit - Menu item label edited (detail: { path, oldValue, newValue })
|
|
29
31
|
*
|
|
30
32
|
* @slot - Menu items (auto-generated from items property)
|
|
31
33
|
*/
|
|
@@ -37,6 +39,18 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
37
39
|
this.items = [];
|
|
38
40
|
/** Menu size variant (small, medium, large) */
|
|
39
41
|
this.size = "medium" /* MenuSize.Medium */;
|
|
42
|
+
/** Default arrow icon position for submenus (left or right) */
|
|
43
|
+
this.arrowPosition = "right" /* IconPosition.Right */;
|
|
44
|
+
/** Track context menu state */
|
|
45
|
+
this._contextMenuState = null;
|
|
46
|
+
/** Track which menu item is currently being edited (path as string for comparison) */
|
|
47
|
+
this._editingPath = null;
|
|
48
|
+
/** Temporary value while editing */
|
|
49
|
+
this._editingValue = '';
|
|
50
|
+
/** Pending click timeout for double-click detection */
|
|
51
|
+
this._pendingClickTimeout = null;
|
|
52
|
+
/** Path of pending click */
|
|
53
|
+
this._pendingClickPath = null;
|
|
40
54
|
this._linkIndex = 0;
|
|
41
55
|
this.stateController = new StateController(this);
|
|
42
56
|
this.keyboardController = new KeyboardController(this, this.stateController);
|
|
@@ -46,9 +60,32 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
46
60
|
this._initializeSelectedState();
|
|
47
61
|
this.accessibilityController.updateAriaAttributes();
|
|
48
62
|
}
|
|
49
|
-
updated() {
|
|
63
|
+
updated(changedProperties) {
|
|
64
|
+
// Re-initialize selection state when items change
|
|
65
|
+
if (changedProperties.has('items')) {
|
|
66
|
+
this._initializeSelectedState();
|
|
67
|
+
// Also open submenus that have 'opened' set to true
|
|
68
|
+
this._initializeOpenedState();
|
|
69
|
+
}
|
|
50
70
|
this.accessibilityController.updateAriaAttributes();
|
|
51
71
|
}
|
|
72
|
+
_initializeOpenedState() {
|
|
73
|
+
this._openSubMenusFromItems(this.items, []);
|
|
74
|
+
}
|
|
75
|
+
_openSubMenusFromItems(items, parentPath) {
|
|
76
|
+
for (let index = 0; index < items.length; index++) {
|
|
77
|
+
const item = items[index];
|
|
78
|
+
const currentPath = [...parentPath, index];
|
|
79
|
+
if (item.children) {
|
|
80
|
+
// If this item has 'opened' set to true, open it
|
|
81
|
+
if (item.opened) {
|
|
82
|
+
this.stateController.openSubMenu(currentPath);
|
|
83
|
+
}
|
|
84
|
+
// Recursively check children
|
|
85
|
+
this._openSubMenusFromItems(item.children, currentPath);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
52
89
|
_initializeSelectedState() {
|
|
53
90
|
this._linkIndex = 0;
|
|
54
91
|
this._findSelectedPath(this.items);
|
|
@@ -71,7 +108,7 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
71
108
|
}
|
|
72
109
|
return false;
|
|
73
110
|
}
|
|
74
|
-
_handleLinkClick(path, value, event) {
|
|
111
|
+
_handleLinkClick(path, value, editable, event) {
|
|
75
112
|
var _a;
|
|
76
113
|
if ((_a = event === null || event === void 0 ? void 0 : event.target) === null || _a === void 0 ? void 0 : _a.classList.contains('action-icon')) {
|
|
77
114
|
return;
|
|
@@ -82,6 +119,25 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
82
119
|
return;
|
|
83
120
|
}
|
|
84
121
|
}
|
|
122
|
+
const pathKey = path.join('-');
|
|
123
|
+
// If editable, delay the click to allow double-click detection
|
|
124
|
+
if (editable && (event === null || event === void 0 ? void 0 : event.type) === 'mousedown') {
|
|
125
|
+
// Clear any existing pending click
|
|
126
|
+
if (this._pendingClickTimeout) {
|
|
127
|
+
clearTimeout(this._pendingClickTimeout);
|
|
128
|
+
}
|
|
129
|
+
this._pendingClickPath = pathKey;
|
|
130
|
+
this._pendingClickTimeout = setTimeout(() => {
|
|
131
|
+
if (this._pendingClickPath === pathKey) {
|
|
132
|
+
this._executeClick(path, value);
|
|
133
|
+
this._pendingClickPath = null;
|
|
134
|
+
}
|
|
135
|
+
}, 200); // 200ms delay for double-click detection
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
this._executeClick(path, value);
|
|
139
|
+
}
|
|
140
|
+
_executeClick(path, value) {
|
|
85
141
|
this.stateController.setSelectedPath(path);
|
|
86
142
|
this.dispatchEvent(new CustomEvent('change', {
|
|
87
143
|
bubbles: true,
|
|
@@ -90,7 +146,7 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
90
146
|
}));
|
|
91
147
|
this.requestUpdate();
|
|
92
148
|
}
|
|
93
|
-
_handleSubMenuClick(path, value, event) {
|
|
149
|
+
_handleSubMenuClick(path, value, editable, event) {
|
|
94
150
|
const mouseEvent = event;
|
|
95
151
|
const target = event.target;
|
|
96
152
|
// Don't handle if clicking on toggle icon or its parent container
|
|
@@ -109,7 +165,25 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
109
165
|
// Real mouse click - already handled by mousedown, so return
|
|
110
166
|
return;
|
|
111
167
|
}
|
|
112
|
-
|
|
168
|
+
const pathKey = path.join('-');
|
|
169
|
+
// If editable, delay the click to allow double-click detection
|
|
170
|
+
if (editable) {
|
|
171
|
+
// Clear any existing pending click
|
|
172
|
+
if (this._pendingClickTimeout) {
|
|
173
|
+
clearTimeout(this._pendingClickTimeout);
|
|
174
|
+
}
|
|
175
|
+
this._pendingClickPath = pathKey;
|
|
176
|
+
this._pendingClickTimeout = setTimeout(() => {
|
|
177
|
+
if (this._pendingClickPath === pathKey) {
|
|
178
|
+
this._executeSubMenuClick(path, value);
|
|
179
|
+
this._pendingClickPath = null;
|
|
180
|
+
}
|
|
181
|
+
}, 200);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
this._executeSubMenuClick(path, value);
|
|
185
|
+
}
|
|
186
|
+
_executeSubMenuClick(path, value) {
|
|
113
187
|
this.stateController.setSelectedPath([]);
|
|
114
188
|
this.stateController.clearHighlights();
|
|
115
189
|
this.stateController.setHighlighted(path, true);
|
|
@@ -137,12 +211,170 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
137
211
|
}
|
|
138
212
|
_handleActionClick(path, event) {
|
|
139
213
|
const item = event.detail.item;
|
|
214
|
+
const originalEvent = event.detail.originalEvent;
|
|
215
|
+
const dropdown = event.detail.dropdown;
|
|
216
|
+
// Create a close callback for async actions
|
|
217
|
+
const close = () => {
|
|
218
|
+
if (dropdown) {
|
|
219
|
+
if (typeof dropdown.hide === 'function') {
|
|
220
|
+
dropdown.hide();
|
|
221
|
+
}
|
|
222
|
+
else if (typeof dropdown.close === 'function') {
|
|
223
|
+
dropdown.close();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
};
|
|
140
227
|
this.dispatchEvent(new CustomEvent('action-click', {
|
|
141
|
-
detail: { value: item.value, path, item },
|
|
228
|
+
detail: { value: item.value, path, item, originalEvent, close },
|
|
142
229
|
composed: true,
|
|
143
230
|
bubbles: true,
|
|
144
231
|
}));
|
|
145
232
|
}
|
|
233
|
+
_handleContextMenu(path, menu, event) {
|
|
234
|
+
var _a, _b;
|
|
235
|
+
if (!((_b = (_a = menu.menu) === null || _a === void 0 ? void 0 : _a.actions) === null || _b === void 0 ? void 0 : _b.length))
|
|
236
|
+
return;
|
|
237
|
+
event.preventDefault();
|
|
238
|
+
event.stopPropagation();
|
|
239
|
+
this._contextMenuState = {
|
|
240
|
+
path,
|
|
241
|
+
x: event.clientX,
|
|
242
|
+
y: event.clientY,
|
|
243
|
+
actions: menu.menu.actions
|
|
244
|
+
};
|
|
245
|
+
this.requestUpdate();
|
|
246
|
+
// Wait for render then show the dropdown
|
|
247
|
+
this.updateComplete.then(() => {
|
|
248
|
+
var _a;
|
|
249
|
+
const dropdown = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('#context-menu-dropdown');
|
|
250
|
+
if (dropdown && typeof dropdown.show === 'function') {
|
|
251
|
+
dropdown.show();
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
_handleContextMenuAction(path, event) {
|
|
256
|
+
const item = event.detail.item;
|
|
257
|
+
const originalEvent = event.detail.originalEvent;
|
|
258
|
+
const dropdown = event.detail.dropdown;
|
|
259
|
+
// Create a close callback
|
|
260
|
+
const close = () => {
|
|
261
|
+
this._contextMenuState = null;
|
|
262
|
+
if (dropdown) {
|
|
263
|
+
if (typeof dropdown.hide === 'function') {
|
|
264
|
+
dropdown.hide();
|
|
265
|
+
}
|
|
266
|
+
else if (typeof dropdown.close === 'function') {
|
|
267
|
+
dropdown.close();
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
this.requestUpdate();
|
|
271
|
+
};
|
|
272
|
+
this.dispatchEvent(new CustomEvent('action-click', {
|
|
273
|
+
detail: { value: item.value, path, item, originalEvent, close },
|
|
274
|
+
composed: true,
|
|
275
|
+
bubbles: true,
|
|
276
|
+
}));
|
|
277
|
+
close();
|
|
278
|
+
}
|
|
279
|
+
_handleContextMenuClose() {
|
|
280
|
+
this._contextMenuState = null;
|
|
281
|
+
this.requestUpdate();
|
|
282
|
+
}
|
|
283
|
+
_handleDoubleClick(path, menu, event) {
|
|
284
|
+
if (menu.disabled)
|
|
285
|
+
return;
|
|
286
|
+
event.preventDefault();
|
|
287
|
+
event.stopPropagation();
|
|
288
|
+
// Cancel any pending single click
|
|
289
|
+
if (this._pendingClickTimeout) {
|
|
290
|
+
clearTimeout(this._pendingClickTimeout);
|
|
291
|
+
this._pendingClickTimeout = null;
|
|
292
|
+
this._pendingClickPath = null;
|
|
293
|
+
}
|
|
294
|
+
const pathKey = path.join('-');
|
|
295
|
+
this._editingPath = pathKey;
|
|
296
|
+
this._editingValue = menu.text;
|
|
297
|
+
this.requestUpdate();
|
|
298
|
+
// Focus the input after render
|
|
299
|
+
this.updateComplete.then(() => {
|
|
300
|
+
var _a;
|
|
301
|
+
const input = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`input[data-edit-path="${pathKey}"]`);
|
|
302
|
+
if (input) {
|
|
303
|
+
input.focus();
|
|
304
|
+
input.select();
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
_handleEditInput(event) {
|
|
309
|
+
event.stopPropagation();
|
|
310
|
+
const input = event.target;
|
|
311
|
+
this._editingValue = input.value;
|
|
312
|
+
}
|
|
313
|
+
_handleEditKeyDown(path, originalText, event) {
|
|
314
|
+
event.stopPropagation(); // Prevent keyboard navigation from capturing keys
|
|
315
|
+
console.log('Edit keydown:', event.key, 'editingValue:', this._editingValue);
|
|
316
|
+
if (event.key === 'Enter') {
|
|
317
|
+
event.preventDefault();
|
|
318
|
+
// Get the current value directly from the input
|
|
319
|
+
const input = event.target;
|
|
320
|
+
this._editingValue = input.value;
|
|
321
|
+
console.log('Saving with value:', this._editingValue);
|
|
322
|
+
this._saveEdit(path, originalText);
|
|
323
|
+
}
|
|
324
|
+
else if (event.key === 'Escape') {
|
|
325
|
+
event.preventDefault();
|
|
326
|
+
this._cancelEdit();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
_handleEditBlur(path, originalText, event) {
|
|
330
|
+
var _a;
|
|
331
|
+
// Check if focus is moving to another element within this component
|
|
332
|
+
const relatedTarget = event.relatedTarget;
|
|
333
|
+
if (relatedTarget && ((_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.contains(relatedTarget))) {
|
|
334
|
+
return; // Don't save if focus is moving within the component
|
|
335
|
+
}
|
|
336
|
+
// Delay to allow click events to process first
|
|
337
|
+
setTimeout(() => {
|
|
338
|
+
if (this._editingPath === path.join('-')) {
|
|
339
|
+
this._saveEdit(path, originalText);
|
|
340
|
+
}
|
|
341
|
+
}, 200);
|
|
342
|
+
}
|
|
343
|
+
_saveEdit(path, originalText) {
|
|
344
|
+
const newValue = this._editingValue.trim();
|
|
345
|
+
console.log('_saveEdit called:', { newValue, originalText, willEmit: newValue && newValue !== originalText });
|
|
346
|
+
if (newValue && newValue !== originalText) {
|
|
347
|
+
const detail = {
|
|
348
|
+
path,
|
|
349
|
+
oldValue: originalText,
|
|
350
|
+
newValue,
|
|
351
|
+
};
|
|
352
|
+
// Call callback if provided
|
|
353
|
+
if (this.onLabelEdit) {
|
|
354
|
+
console.log('Calling onLabelEdit callback');
|
|
355
|
+
this.onLabelEdit(detail);
|
|
356
|
+
}
|
|
357
|
+
// Also dispatch event
|
|
358
|
+
console.log('Dispatching label-edit event from:', this.tagName);
|
|
359
|
+
const event = new CustomEvent('label-edit', {
|
|
360
|
+
bubbles: true,
|
|
361
|
+
composed: true,
|
|
362
|
+
detail,
|
|
363
|
+
});
|
|
364
|
+
this.dispatchEvent(event);
|
|
365
|
+
}
|
|
366
|
+
this._editingPath = null;
|
|
367
|
+
this._editingValue = '';
|
|
368
|
+
this.requestUpdate();
|
|
369
|
+
}
|
|
370
|
+
_cancelEdit() {
|
|
371
|
+
this._editingPath = null;
|
|
372
|
+
this._editingValue = '';
|
|
373
|
+
this.requestUpdate();
|
|
374
|
+
}
|
|
375
|
+
_isEditing(path) {
|
|
376
|
+
return this._editingPath === path.join('-');
|
|
377
|
+
}
|
|
146
378
|
_isPathSelected(path) {
|
|
147
379
|
return this.stateController.isPathSelected(path);
|
|
148
380
|
}
|
|
@@ -150,45 +382,60 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
150
382
|
return actions.map(action => ({
|
|
151
383
|
id: action.value,
|
|
152
384
|
label: action.label,
|
|
153
|
-
value: action.value
|
|
385
|
+
value: action.value,
|
|
386
|
+
icon: action.icon,
|
|
387
|
+
additionalData: action.additionalData
|
|
154
388
|
}));
|
|
155
389
|
}
|
|
156
390
|
_renderMenuLink(menu, path) {
|
|
157
|
-
var _a, _b;
|
|
391
|
+
var _a, _b, _c;
|
|
158
392
|
const pathKey = path.join('-');
|
|
159
393
|
const isSelected = this._isPathSelected(path);
|
|
394
|
+
const isEditing = this._isEditing(path);
|
|
160
395
|
const linkIndex = this._linkIndex++;
|
|
161
396
|
return html `
|
|
162
|
-
<li
|
|
163
|
-
class="menu-link ${isSelected ? 'selected' : ''} ${menu.disabled ? 'disabled' : ''}"
|
|
397
|
+
<li
|
|
398
|
+
class="menu-link ${isSelected ? 'selected' : ''} ${menu.disabled ? 'disabled' : ''} ${isEditing ? 'editing' : ''}"
|
|
164
399
|
data-path=${pathKey}
|
|
165
400
|
data-index=${linkIndex}
|
|
166
|
-
tabindex
|
|
167
|
-
@mousedown=${!menu.disabled ? (e) => this._handleLinkClick(path, menu.text, e) : nothing}
|
|
168
|
-
@click=${!menu.disabled ? (e) => this._handleLinkClick(path, menu.text, e) : nothing}
|
|
401
|
+
tabindex=${isEditing ? -1 : 0}
|
|
402
|
+
@mousedown=${!menu.disabled && !isEditing ? (e) => this._handleLinkClick(path, menu.text, !!menu.editable, e) : nothing}
|
|
403
|
+
@click=${!menu.disabled && !isEditing ? (e) => this._handleLinkClick(path, menu.text, !!menu.editable, e) : nothing}
|
|
404
|
+
@dblclick=${!menu.disabled ? (e) => this._handleDoubleClick(path, menu, e) : nothing}
|
|
405
|
+
@contextmenu=${!menu.disabled && ((_a = menu.menu) === null || _a === void 0 ? void 0 : _a.actions) ? (e) => this._handleContextMenu(path, menu, e) : nothing}>
|
|
169
406
|
<div class="icon-container">
|
|
170
407
|
${menu.icon ? html `
|
|
171
408
|
${!menu.text
|
|
172
409
|
? html `<div class="icon-only"><nr-icon name="${menu.icon}"></nr-icon></div>`
|
|
173
|
-
: html `<nr-icon name="${menu.icon}"></nr-icon>`}
|
|
410
|
+
: html `<nr-icon name="${menu.icon}" size="small"></nr-icon>`}
|
|
174
411
|
` : nothing}
|
|
175
412
|
</div>
|
|
176
413
|
${menu.text ? html `
|
|
177
414
|
<div class="action-text-container">
|
|
178
|
-
<div class="text-container">
|
|
179
|
-
|
|
415
|
+
<div class="text-container" @dblclick=${!menu.disabled ? (e) => this._handleDoubleClick(path, menu, e) : nothing}>
|
|
416
|
+
${isEditing ? html `
|
|
417
|
+
<input
|
|
418
|
+
type="text"
|
|
419
|
+
class="edit-input"
|
|
420
|
+
data-edit-path=${pathKey}
|
|
421
|
+
.value=${this._editingValue}
|
|
422
|
+
@input=${this._handleEditInput}
|
|
423
|
+
@keydown=${(e) => this._handleEditKeyDown(path, menu.text, e)}
|
|
424
|
+
@blur=${(e) => this._handleEditBlur(path, menu.text, e)}
|
|
425
|
+
/>
|
|
426
|
+
` : html `<span>${menu.text}</span>`}
|
|
180
427
|
</div>
|
|
181
428
|
<div class="icon-container">
|
|
182
|
-
${((
|
|
183
|
-
<nr-icon name=${menu.status.icon} class="status-icon"></nr-icon>
|
|
429
|
+
${((_b = menu.status) === null || _b === void 0 ? void 0 : _b.icon) ? html `
|
|
430
|
+
<nr-icon name=${menu.status.icon} class="status-icon" size="small"></nr-icon>
|
|
184
431
|
` : nothing}
|
|
185
|
-
${((
|
|
186
|
-
<nr-dropdown
|
|
187
|
-
.items=${this._convertActionsToDropdownItems(menu.menu.actions)}
|
|
188
|
-
trigger="click"
|
|
432
|
+
${((_c = menu.menu) === null || _c === void 0 ? void 0 : _c.actions) ? html `
|
|
433
|
+
<nr-dropdown
|
|
434
|
+
.items=${this._convertActionsToDropdownItems(menu.menu.actions)}
|
|
435
|
+
trigger="click"
|
|
189
436
|
placement="bottom-end"
|
|
190
437
|
@nr-dropdown-item-click=${(e) => this._handleActionClick(path, e)}>
|
|
191
|
-
<nr-icon name="${menu.menu.icon}" class="action-icon" slot="trigger"></nr-icon>
|
|
438
|
+
<nr-icon name="${menu.menu.icon}" class="action-icon" slot="trigger" size="small"></nr-icon>
|
|
192
439
|
</nr-dropdown>
|
|
193
440
|
` : nothing}
|
|
194
441
|
</div>
|
|
@@ -198,20 +445,24 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
198
445
|
`;
|
|
199
446
|
}
|
|
200
447
|
_renderSubMenu(menu, path) {
|
|
201
|
-
var _a, _b;
|
|
448
|
+
var _a, _b, _c;
|
|
202
449
|
const pathKey = path.join('-');
|
|
203
450
|
const isOpen = this.stateController.isSubMenuOpen(path) || menu.opened;
|
|
204
451
|
const isHovered = this.stateController.isSubMenuHovered(path);
|
|
205
452
|
const isHighlighted = this.stateController.isSubMenuHighlighted(path);
|
|
206
453
|
const isSelected = menu.selected;
|
|
454
|
+
const isEditing = this._isEditing(path);
|
|
455
|
+
// Determine icon position - use individual menu item setting or fall back to global setting
|
|
456
|
+
const iconPosition = menu.iconPosition || this.arrowPosition;
|
|
457
|
+
const isLeftPosition = iconPosition === "left" /* IconPosition.Left */ || iconPosition === 'left';
|
|
207
458
|
return html `
|
|
208
|
-
<ul
|
|
209
|
-
class="sub-menu ${isHighlighted ? 'highlighted' : ''} ${menu.disabled ? 'disabled' : ''} ${isSelected ? 'selected' : ''}"
|
|
459
|
+
<ul
|
|
460
|
+
class="sub-menu ${isHighlighted ? 'highlighted' : ''} ${menu.disabled ? 'disabled' : ''} ${isSelected ? 'selected' : ''} ${isLeftPosition ? 'arrow-left' : 'arrow-right'} ${isEditing ? 'editing' : ''}"
|
|
210
461
|
data-path=${pathKey}
|
|
211
|
-
tabindex
|
|
462
|
+
tabindex=${isEditing ? -1 : 0}
|
|
212
463
|
@mouseenter=${() => this._handleSubMenuMouseEnter(path)}
|
|
213
464
|
@mouseleave=${() => this._handleSubMenuMouseLeave(path)}
|
|
214
|
-
@click=${!menu.disabled ? (e) => {
|
|
465
|
+
@click=${!menu.disabled && !isEditing ? (e) => {
|
|
215
466
|
// Handle keyboard activation on the ul element
|
|
216
467
|
const target = e.target;
|
|
217
468
|
const mouseEvent = e;
|
|
@@ -222,30 +473,51 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
222
473
|
this.requestUpdate();
|
|
223
474
|
}
|
|
224
475
|
} : nothing}>
|
|
225
|
-
<div
|
|
476
|
+
<div
|
|
226
477
|
class="sub-menu-header"
|
|
227
|
-
@mousedown=${!menu.disabled ? (e) => this._handleSubMenuClick(path, menu.text, e) : nothing}
|
|
228
|
-
@click=${!menu.disabled ? (e) => this._handleSubMenuClick(path, menu.text, e) : nothing}
|
|
229
|
-
|
|
230
|
-
|
|
478
|
+
@mousedown=${!menu.disabled && !isEditing ? (e) => this._handleSubMenuClick(path, menu.text, !!menu.editable, e) : nothing}
|
|
479
|
+
@click=${!menu.disabled && !isEditing ? (e) => this._handleSubMenuClick(path, menu.text, !!menu.editable, e) : nothing}
|
|
480
|
+
@dblclick=${!menu.disabled && menu.editable ? (e) => this._handleDoubleClick(path, menu, e) : nothing}
|
|
481
|
+
@contextmenu=${!menu.disabled && ((_a = menu.menu) === null || _a === void 0 ? void 0 : _a.actions) ? (e) => this._handleContextMenu(path, menu, e) : nothing}>
|
|
482
|
+
${isLeftPosition && menu.children && menu.children.length ? html `
|
|
483
|
+
<nr-icon
|
|
484
|
+
id="toggle-icon"
|
|
485
|
+
name="${isOpen ? 'ChevronDown' : 'ChevronRight'}"
|
|
486
|
+
@mousedown=${!menu.disabled ? (e) => this._toggleSubMenu(path, e) : nothing}
|
|
487
|
+
size="small">
|
|
488
|
+
</nr-icon>
|
|
489
|
+
` : nothing}
|
|
490
|
+
${menu.icon ? html `<nr-icon class="text-icon" name="${menu.icon}" size="small"></nr-icon>` : nothing}
|
|
491
|
+
${isEditing ? html `
|
|
492
|
+
<input
|
|
493
|
+
type="text"
|
|
494
|
+
class="edit-input"
|
|
495
|
+
data-edit-path=${pathKey}
|
|
496
|
+
.value=${this._editingValue}
|
|
497
|
+
@input=${this._handleEditInput}
|
|
498
|
+
@keydown=${(e) => this._handleEditKeyDown(path, menu.text, e)}
|
|
499
|
+
@blur=${(e) => this._handleEditBlur(path, menu.text, e)}
|
|
500
|
+
/>
|
|
501
|
+
` : html `<span class="menu-text" @dblclick=${!menu.disabled ? (e) => this._handleDoubleClick(path, menu, e) : nothing}>${menu.text}</span>`}
|
|
231
502
|
<div class="icons-container">
|
|
232
|
-
${((
|
|
233
|
-
<nr-icon name=${menu.status.icon} class="status-icon"></nr-icon>
|
|
503
|
+
${((_b = menu.status) === null || _b === void 0 ? void 0 : _b.icon) ? html `
|
|
504
|
+
<nr-icon name=${menu.status.icon} class="status-icon" size="small"></nr-icon>
|
|
234
505
|
` : nothing}
|
|
235
|
-
${(isHighlighted || isHovered) && ((
|
|
236
|
-
<nr-dropdown
|
|
237
|
-
.items=${this._convertActionsToDropdownItems(menu.menu.actions)}
|
|
238
|
-
trigger="click"
|
|
506
|
+
${(isHighlighted || isHovered) && ((_c = menu.menu) === null || _c === void 0 ? void 0 : _c.actions) ? html `
|
|
507
|
+
<nr-dropdown
|
|
508
|
+
.items=${this._convertActionsToDropdownItems(menu.menu.actions)}
|
|
509
|
+
trigger="click"
|
|
239
510
|
placement="bottom-end"
|
|
240
511
|
@nr-dropdown-item-click=${(e) => this._handleActionClick(path, e)}>
|
|
241
|
-
<nr-icon name="${menu.menu.icon}" class="action-icon" slot="trigger"></nr-icon>
|
|
512
|
+
<nr-icon name="${menu.menu.icon}" class="action-icon" slot="trigger" size="small"></nr-icon>
|
|
242
513
|
</nr-dropdown>
|
|
243
514
|
` : nothing}
|
|
244
|
-
${menu.children && menu.children.length ? html `
|
|
245
|
-
<nr-icon
|
|
246
|
-
id="toggle-icon"
|
|
247
|
-
name="${isOpen ? '
|
|
248
|
-
@mousedown=${!menu.disabled ? (e) => this._toggleSubMenu(path, e) : nothing}
|
|
515
|
+
${!isLeftPosition && menu.children && menu.children.length ? html `
|
|
516
|
+
<nr-icon
|
|
517
|
+
id="toggle-icon"
|
|
518
|
+
name="${isOpen ? 'ChevronDown' : 'ChevronRight'}"
|
|
519
|
+
@mousedown=${!menu.disabled ? (e) => this._toggleSubMenu(path, e) : nothing}
|
|
520
|
+
size="small">
|
|
249
521
|
</nr-icon>
|
|
250
522
|
` : nothing}
|
|
251
523
|
</div>
|
|
@@ -267,12 +539,31 @@ let NrMenuElement = class NrMenuElement extends NuralyUIBaseMixin(LitElement) {
|
|
|
267
539
|
}
|
|
268
540
|
});
|
|
269
541
|
}
|
|
542
|
+
_renderContextMenu() {
|
|
543
|
+
if (!this._contextMenuState)
|
|
544
|
+
return nothing;
|
|
545
|
+
const { x, y, actions, path } = this._contextMenuState;
|
|
546
|
+
return html `
|
|
547
|
+
<nr-dropdown
|
|
548
|
+
id="context-menu-dropdown"
|
|
549
|
+
.items=${this._convertActionsToDropdownItems(actions)}
|
|
550
|
+
trigger="manual"
|
|
551
|
+
placement="bottom-start"
|
|
552
|
+
style="position: fixed; top: ${y}px; left: ${x}px; z-index: 9999;"
|
|
553
|
+
@nr-dropdown-item-click=${(e) => this._handleContextMenuAction(path, e)}
|
|
554
|
+
@nr-dropdown-close=${() => this._handleContextMenuClose()}
|
|
555
|
+
>
|
|
556
|
+
<span slot="trigger" style="display: block; width: 1px; height: 1px;"></span>
|
|
557
|
+
</nr-dropdown>
|
|
558
|
+
`;
|
|
559
|
+
}
|
|
270
560
|
render() {
|
|
271
561
|
this._linkIndex = 0;
|
|
272
562
|
return html `
|
|
273
563
|
<ul class="menu-root menu--${this.size}">
|
|
274
564
|
${this._renderMenuItems(this.items)}
|
|
275
565
|
</ul>
|
|
566
|
+
${this._renderContextMenu()}
|
|
276
567
|
`;
|
|
277
568
|
}
|
|
278
569
|
};
|
|
@@ -283,6 +574,21 @@ __decorate([
|
|
|
283
574
|
__decorate([
|
|
284
575
|
property({ type: String })
|
|
285
576
|
], NrMenuElement.prototype, "size", void 0);
|
|
577
|
+
__decorate([
|
|
578
|
+
property({ type: String })
|
|
579
|
+
], NrMenuElement.prototype, "arrowPosition", void 0);
|
|
580
|
+
__decorate([
|
|
581
|
+
property({ type: Object, attribute: false })
|
|
582
|
+
], NrMenuElement.prototype, "onLabelEdit", void 0);
|
|
583
|
+
__decorate([
|
|
584
|
+
state()
|
|
585
|
+
], NrMenuElement.prototype, "_contextMenuState", void 0);
|
|
586
|
+
__decorate([
|
|
587
|
+
state()
|
|
588
|
+
], NrMenuElement.prototype, "_editingPath", void 0);
|
|
589
|
+
__decorate([
|
|
590
|
+
state()
|
|
591
|
+
], NrMenuElement.prototype, "_editingValue", void 0);
|
|
286
592
|
NrMenuElement = __decorate([
|
|
287
593
|
customElement('nr-menu')
|
|
288
594
|
], NrMenuElement);
|