@huyooo/file-explorer-frontend-vue 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/components/Breadcrumb.vue.d.ts +11 -0
  2. package/dist/components/Breadcrumb.vue.d.ts.map +1 -0
  3. package/dist/components/CompressDialog.vue.d.ts +16 -0
  4. package/dist/components/CompressDialog.vue.d.ts.map +1 -0
  5. package/dist/components/ContextMenu.vue.d.ts +18 -0
  6. package/dist/components/ContextMenu.vue.d.ts.map +1 -0
  7. package/dist/components/FileGrid.vue.d.ts +40 -0
  8. package/dist/components/FileGrid.vue.d.ts.map +1 -0
  9. package/dist/components/FileIcon.vue.d.ts +13 -0
  10. package/dist/components/FileIcon.vue.d.ts.map +1 -0
  11. package/dist/components/FileInfoDialog.vue.d.ts +14 -0
  12. package/dist/components/FileInfoDialog.vue.d.ts.map +1 -0
  13. package/dist/components/FileList.vue.d.ts +37 -0
  14. package/dist/components/FileList.vue.d.ts.map +1 -0
  15. package/dist/components/FileListView.vue.d.ts +43 -0
  16. package/dist/components/FileListView.vue.d.ts.map +1 -0
  17. package/dist/components/FileSidebar.vue.d.ts +17 -0
  18. package/dist/components/FileSidebar.vue.d.ts.map +1 -0
  19. package/dist/components/ProgressDialog.vue.d.ts +28 -0
  20. package/dist/components/ProgressDialog.vue.d.ts.map +1 -0
  21. package/dist/components/SortIndicator.vue.d.ts +6 -0
  22. package/dist/components/SortIndicator.vue.d.ts.map +1 -0
  23. package/dist/components/StatusBar.vue.d.ts +27 -0
  24. package/dist/components/StatusBar.vue.d.ts.map +1 -0
  25. package/dist/components/Toolbar.vue.d.ts +60 -0
  26. package/dist/components/Toolbar.vue.d.ts.map +1 -0
  27. package/dist/components/Window.vue.d.ts +65 -0
  28. package/dist/components/Window.vue.d.ts.map +1 -0
  29. package/dist/composables/useApplicationIcon.d.ts +16 -0
  30. package/dist/composables/useApplicationIcon.d.ts.map +1 -0
  31. package/dist/composables/useDragAndDrop.d.ts +14 -0
  32. package/dist/composables/useDragAndDrop.d.ts.map +1 -0
  33. package/dist/composables/useMediaPlayer.d.ts +24 -0
  34. package/dist/composables/useMediaPlayer.d.ts.map +1 -0
  35. package/dist/composables/useSelection.d.ts +15 -0
  36. package/dist/composables/useSelection.d.ts.map +1 -0
  37. package/dist/composables/useWindowDrag.d.ts +18 -0
  38. package/dist/composables/useWindowDrag.d.ts.map +1 -0
  39. package/dist/composables/useWindowResize.d.ts +12 -0
  40. package/dist/composables/useWindowResize.d.ts.map +1 -0
  41. package/dist/index.css +1 -0
  42. package/dist/index.d.ts +22 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +4051 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/types/index.d.ts +268 -0
  47. package/dist/types/index.d.ts.map +1 -0
  48. package/dist/utils/fileTypeIcon.d.ts +6 -0
  49. package/dist/utils/fileTypeIcon.d.ts.map +1 -0
  50. package/dist/utils/folderTypeIcon.d.ts +14 -0
  51. package/dist/utils/folderTypeIcon.d.ts.map +1 -0
  52. package/package.json +55 -0
  53. package/src/components/Breadcrumb.vue +111 -0
  54. package/src/components/CompressDialog.vue +478 -0
  55. package/src/components/ContextMenu.vue +550 -0
  56. package/src/components/FileGrid.vue +504 -0
  57. package/src/components/FileIcon.vue +132 -0
  58. package/src/components/FileInfoDialog.vue +465 -0
  59. package/src/components/FileList.vue +421 -0
  60. package/src/components/FileListView.vue +321 -0
  61. package/src/components/FileSidebar.vue +158 -0
  62. package/src/components/ProgressDialog.vue +368 -0
  63. package/src/components/SortIndicator.vue +22 -0
  64. package/src/components/StatusBar.vue +43 -0
  65. package/src/components/Toolbar.vue +271 -0
  66. package/src/components/Window.vue +561 -0
  67. package/src/composables/useApplicationIcon.ts +79 -0
  68. package/src/composables/useDragAndDrop.ts +103 -0
  69. package/src/composables/useMediaPlayer.ts +174 -0
  70. package/src/composables/useSelection.ts +107 -0
  71. package/src/composables/useWindowDrag.ts +66 -0
  72. package/src/composables/useWindowResize.ts +134 -0
  73. package/src/index.ts +32 -0
  74. package/src/types/index.ts +273 -0
  75. package/src/utils/fileTypeIcon.ts +309 -0
  76. package/src/utils/folderTypeIcon.ts +132 -0
@@ -0,0 +1,550 @@
1
+ <template>
2
+ <Teleport to="body">
3
+ <div v-if="visible" class="context-menu-container">
4
+ <!-- 主菜单 - 不使用 overlay,通过 document mousedown 事件来关闭菜单 -->
5
+ <div
6
+ ref="menuRef"
7
+ class="context-menu"
8
+ :style="menuStyle"
9
+ >
10
+ <template v-for="(option, index) in options" :key="option.id || index">
11
+ <div v-if="option.separator" class="context-menu-separator" />
12
+ <div
13
+ v-else
14
+ :ref="(el) => setItemRef(option.id, el)"
15
+ :class="[
16
+ 'context-menu-item',
17
+ option.disabled ? 'context-menu-item--disabled' : '',
18
+ option.danger ? 'context-menu-item--danger' : '',
19
+ hasChildren(option) ? 'context-menu-item--has-children' : '',
20
+ hoveredItemId === option.id ? 'context-menu-item--active' : ''
21
+ ]"
22
+ @click="handleOptionClick(option)"
23
+ @mouseenter="handleItemMouseEnter(option)"
24
+ >
25
+ <Icon
26
+ v-if="option.icon"
27
+ :icon="option.icon"
28
+ width="16"
29
+ height="16"
30
+ class="context-menu-item-icon"
31
+ />
32
+ <span class="context-menu-item-label">{{ option.label }}</span>
33
+ <Icon
34
+ v-if="option.checked"
35
+ icon="lucide:check"
36
+ width="14"
37
+ height="14"
38
+ class="context-menu-item-check"
39
+ />
40
+ <span v-if="option.shortcut && !hasChildren(option)" class="context-menu-item-shortcut">
41
+ {{ option.shortcut }}
42
+ </span>
43
+ <Icon
44
+ v-if="hasChildren(option)"
45
+ icon="lucide:chevron-right"
46
+ width="14"
47
+ height="14"
48
+ class="context-menu-item-arrow"
49
+ />
50
+ </div>
51
+ </template>
52
+ </div>
53
+
54
+ <!-- 二级菜单(独立渲染,避免定位问题) -->
55
+ <div
56
+ v-if="hoveredItem && submenuPosition"
57
+ class="context-menu context-menu-submenu"
58
+ :style="submenuStyle"
59
+ @mouseenter="keepSubmenuOpen"
60
+ @mouseleave="closeSubmenu"
61
+ >
62
+ <template v-for="(child, childIndex) in hoveredItem.children" :key="child.id || childIndex">
63
+ <div v-if="child.separator" class="context-menu-separator" />
64
+ <div
65
+ v-else
66
+ :class="[
67
+ 'context-menu-item',
68
+ child.disabled ? 'context-menu-item--disabled' : '',
69
+ child.danger ? 'context-menu-item--danger' : ''
70
+ ]"
71
+ @click="handleOptionClick(child)"
72
+ >
73
+ <Icon
74
+ v-if="child.icon"
75
+ :icon="child.icon"
76
+ width="16"
77
+ height="16"
78
+ class="context-menu-item-icon"
79
+ />
80
+ <span class="context-menu-item-label">{{ child.label }}</span>
81
+ <Icon
82
+ v-if="child.checked"
83
+ icon="lucide:check"
84
+ width="14"
85
+ height="14"
86
+ class="context-menu-item-check"
87
+ />
88
+ <span v-if="child.shortcut" class="context-menu-item-shortcut">
89
+ {{ child.shortcut }}
90
+ </span>
91
+ </div>
92
+ </template>
93
+ </div>
94
+ </div>
95
+ </Teleport>
96
+ </template>
97
+
98
+ <script setup lang="ts">
99
+ import { computed, ref, watch, nextTick, onMounted, onUnmounted } from 'vue';
100
+ import { Icon } from '@iconify/vue';
101
+ import type { ContextMenuItem } from '../types';
102
+
103
+ interface Props {
104
+ visible: boolean;
105
+ x: number;
106
+ y: number;
107
+ options: ContextMenuItem[];
108
+ }
109
+
110
+ const props = defineProps<Props>();
111
+
112
+ const emit = defineEmits<{
113
+ close: [];
114
+ select: [option: ContextMenuItem];
115
+ }>();
116
+
117
+ /** 边距常量 */
118
+ const MARGIN = 8;
119
+ /** 菜单固定宽度 */
120
+ const MENU_WIDTH = 220;
121
+ /** 菜单项高度 */
122
+ const MENU_ITEM_HEIGHT = 32;
123
+ /** 分隔线高度 */
124
+ const SEPARATOR_HEIGHT = 9;
125
+ /** 菜单内边距 */
126
+ const MENU_PADDING = 8;
127
+ /** 子菜单与父菜单的间距 */
128
+ const SUBMENU_GAP = 0;
129
+
130
+ /**
131
+ * 估算菜单高度(根据菜单项数量)
132
+ */
133
+ function estimateMenuHeight(items: ContextMenuItem[]): number {
134
+ let height = MENU_PADDING;
135
+ for (const item of items) {
136
+ height += item.separator ? SEPARATOR_HEIGHT : MENU_ITEM_HEIGHT;
137
+ }
138
+ return height;
139
+ }
140
+
141
+ /**
142
+ * 计算自适应菜单位置
143
+ * 使用固定宽度,只需测量高度,简化计算逻辑
144
+ */
145
+ function calculateMenuPosition(
146
+ clickX: number,
147
+ clickY: number,
148
+ menuHeight: number
149
+ ): { x: number; y: number } {
150
+ const viewportWidth = window.innerWidth;
151
+ const viewportHeight = window.innerHeight;
152
+
153
+ let adjustedX = clickX;
154
+ let adjustedY = clickY;
155
+
156
+ // 水平方向调整(使用固定宽度)
157
+ const spaceRight = viewportWidth - clickX;
158
+ const spaceLeft = clickX;
159
+
160
+ // 如果右边空间不足,尝试在左边显示
161
+ if (spaceRight < MENU_WIDTH + MARGIN) {
162
+ // 左边空间足够,在左边显示
163
+ if (spaceLeft >= MENU_WIDTH + MARGIN) {
164
+ adjustedX = clickX - MENU_WIDTH;
165
+ } else {
166
+ // 左右空间都不足,选择空间更大的一边,并留出边距
167
+ if (spaceRight > spaceLeft) {
168
+ adjustedX = viewportWidth - MENU_WIDTH - MARGIN;
169
+ } else {
170
+ adjustedX = MARGIN;
171
+ }
172
+ }
173
+ }
174
+
175
+ // 垂直方向调整
176
+ const spaceBottom = viewportHeight - clickY;
177
+ const spaceTop = clickY;
178
+
179
+ // 如果下边空间不足,尝试在上边显示
180
+ if (spaceBottom < menuHeight + MARGIN) {
181
+ // 上边空间足够,在上边显示
182
+ if (spaceTop >= menuHeight + MARGIN) {
183
+ adjustedY = clickY - menuHeight;
184
+ } else {
185
+ // 上下空间都不足,选择空间更大的一边,并留出边距
186
+ if (spaceBottom > spaceTop) {
187
+ adjustedY = viewportHeight - menuHeight - MARGIN;
188
+ } else {
189
+ adjustedY = MARGIN;
190
+ }
191
+ }
192
+ }
193
+
194
+ // 最终边界检查,确保不会超出视口
195
+ adjustedX = Math.max(MARGIN, Math.min(adjustedX, viewportWidth - MENU_WIDTH - MARGIN));
196
+ adjustedY = Math.max(MARGIN, Math.min(adjustedY, viewportHeight - menuHeight - MARGIN));
197
+
198
+ return { x: adjustedX, y: adjustedY };
199
+ }
200
+
201
+ /**
202
+ * 计算二级菜单位置(Windows/Mac 标准行为:优先右侧,空间不足时左侧)
203
+ */
204
+ function calculateSubmenuPosition(
205
+ parentRect: DOMRect,
206
+ submenuHeight: number
207
+ ): { x: number; y: number } {
208
+ const viewportWidth = window.innerWidth;
209
+ const viewportHeight = window.innerHeight;
210
+
211
+ // 计算可用空间
212
+ const spaceRight = viewportWidth - parentRect.right;
213
+ const spaceLeft = parentRect.left;
214
+
215
+ let submenuX: number;
216
+ let submenuY: number;
217
+
218
+ // 水平方向:优先右侧,空间不足时左侧(Windows/Mac 标准行为)
219
+ if (spaceRight >= MENU_WIDTH + SUBMENU_GAP + MARGIN) {
220
+ // 右侧有足够空间,在右侧显示(紧贴父菜单右边缘)
221
+ submenuX = parentRect.right + SUBMENU_GAP;
222
+ } else if (spaceLeft >= MENU_WIDTH + SUBMENU_GAP + MARGIN) {
223
+ // 右侧空间不足,左侧有足够空间,在左侧显示(紧贴父菜单左边缘)
224
+ submenuX = parentRect.left - MENU_WIDTH - SUBMENU_GAP;
225
+ } else {
226
+ // 左右空间都不足,选择空间更大的一边
227
+ if (spaceRight > spaceLeft) {
228
+ submenuX = viewportWidth - MENU_WIDTH - MARGIN;
229
+ } else {
230
+ submenuX = MARGIN;
231
+ }
232
+ }
233
+
234
+ // 垂直方向:与父菜单项顶部对齐(Windows/Mac 标准行为)
235
+ submenuY = parentRect.top;
236
+
237
+ // 如果子菜单底部超出视口,向上调整
238
+ if (submenuY + submenuHeight > viewportHeight - MARGIN) {
239
+ submenuY = viewportHeight - submenuHeight - MARGIN;
240
+ }
241
+
242
+ // 如果子菜单顶部超出视口,向下调整
243
+ if (submenuY < MARGIN) {
244
+ submenuY = MARGIN;
245
+ }
246
+
247
+ return { x: submenuX, y: submenuY };
248
+ }
249
+
250
+ const menuRef = ref<HTMLDivElement | null>(null);
251
+ const hoveredItemId = ref<string | null>(null);
252
+ const submenuPosition = ref<{ x: number; y: number } | null>(null);
253
+ const itemRefs = ref<Map<string, HTMLElement>>(new Map());
254
+
255
+ // 使用 computed 在渲染前计算位置(使用估算高度,避免位置调整动画)
256
+ const position = computed(() => {
257
+ const estimatedHeight = estimateMenuHeight(props.options);
258
+ return calculateMenuPosition(props.x, props.y, estimatedHeight);
259
+ });
260
+
261
+ const menuStyle = computed(() => ({
262
+ left: `${position.value.x}px`,
263
+ top: `${position.value.y}px`
264
+ }));
265
+
266
+ const submenuStyle = computed(() => {
267
+ if (!submenuPosition.value) return {};
268
+ return {
269
+ left: `${submenuPosition.value.x}px`,
270
+ top: `${submenuPosition.value.y}px`
271
+ };
272
+ });
273
+
274
+ // 获取当前悬停项的子菜单
275
+ const hoveredItem = computed(() => {
276
+ if (!hoveredItemId.value) return null;
277
+ return props.options.find(opt => opt.id === hoveredItemId.value && opt.children && opt.children.length > 0);
278
+ });
279
+
280
+ // 设置菜单项 ref
281
+ const setItemRef = (id: string, el: unknown) => {
282
+ if (el && el instanceof HTMLElement) {
283
+ itemRefs.value.set(id, el);
284
+ } else {
285
+ itemRefs.value.delete(id);
286
+ }
287
+ };
288
+
289
+ // 检查是否有子菜单
290
+ const hasChildren = (option: ContextMenuItem) => {
291
+ return option.children && option.children.length > 0;
292
+ };
293
+
294
+ // 计算子菜单位置
295
+ watch(hoveredItemId, (newId) => {
296
+ if (!newId || !hoveredItem.value) {
297
+ submenuPosition.value = null;
298
+ return;
299
+ }
300
+
301
+ nextTick(() => {
302
+ const itemEl = itemRefs.value.get(newId);
303
+ if (!itemEl || !hoveredItem.value?.children) {
304
+ submenuPosition.value = null;
305
+ return;
306
+ }
307
+
308
+ const rect = itemEl.getBoundingClientRect();
309
+ const submenuHeight = estimateMenuHeight(hoveredItem.value.children);
310
+ submenuPosition.value = calculateSubmenuPosition(rect, submenuHeight);
311
+ });
312
+ });
313
+
314
+ // 重置状态当菜单关闭时
315
+ watch(() => props.visible, (visible) => {
316
+ if (!visible) {
317
+ hoveredItemId.value = null;
318
+ submenuPosition.value = null;
319
+ }
320
+ });
321
+
322
+ // 点击外部或按 ESC 键时关闭(事件处理函数定义在外部,确保引用一致)
323
+ const handleClickOutside = (e: MouseEvent) => {
324
+ const target = e.target as Node;
325
+
326
+ // 检查是否点击在菜单内(包括主菜单和子菜单)
327
+ const menuContainer = document.querySelector('.context-menu-container');
328
+ if (menuContainer && menuContainer.contains(target)) {
329
+ return;
330
+ }
331
+
332
+ emit('close');
333
+ };
334
+
335
+ const handleEscape = (e: KeyboardEvent) => {
336
+ if (e.key === 'Escape') {
337
+ emit('close');
338
+ }
339
+ };
340
+
341
+ // 监听 visible 变化,添加/移除事件监听器
342
+ watch(() => props.visible, (visible) => {
343
+ if (visible) {
344
+ // 使用 mousedown 来更快响应(Windows/Mac 标准行为)
345
+ document.addEventListener('mousedown', handleClickOutside);
346
+ document.addEventListener('keydown', handleEscape);
347
+ } else {
348
+ document.removeEventListener('mousedown', handleClickOutside);
349
+ document.removeEventListener('keydown', handleEscape);
350
+ }
351
+ }, { immediate: true });
352
+
353
+ // 组件卸载时移除事件监听器
354
+ onUnmounted(() => {
355
+ document.removeEventListener('mousedown', handleClickOutside);
356
+ document.removeEventListener('keydown', handleEscape);
357
+ });
358
+
359
+
360
+ const handleOptionClick = (option: ContextMenuItem) => {
361
+ if (option.disabled) return;
362
+
363
+ // 如果有子菜单,不执行 action,只显示子菜单
364
+ if (option.children && option.children.length > 0) {
365
+ return;
366
+ }
367
+
368
+ if (option.action) {
369
+ option.action();
370
+ }
371
+ emit('select', option);
372
+ emit('close');
373
+ };
374
+
375
+ // 处理菜单项鼠标进入
376
+ const handleItemMouseEnter = (option: ContextMenuItem) => {
377
+ if (option.children && option.children.length > 0) {
378
+ hoveredItemId.value = option.id;
379
+ } else {
380
+ hoveredItemId.value = null;
381
+ }
382
+ };
383
+
384
+ // 保持子菜单打开
385
+ const keepSubmenuOpen = () => {
386
+ // 当鼠标在子菜单上时,保持 hoveredItemId 不变
387
+ };
388
+
389
+ // 关闭子菜单
390
+ const closeSubmenu = () => {
391
+ hoveredItemId.value = null;
392
+ };
393
+ </script>
394
+
395
+ <style scoped>
396
+ /* container 不拦截事件,让右键事件可以穿透到下层文件列表 */
397
+ .context-menu-container {
398
+ position: fixed;
399
+ inset: 0;
400
+ z-index: 9999;
401
+ pointer-events: none;
402
+ }
403
+
404
+ .context-menu {
405
+ position: fixed;
406
+ z-index: 10000;
407
+ width: 220px; /* 固定宽度,与 calculateMenuPosition 中的 MENU_WIDTH 一致 */
408
+ background: rgba(255, 255, 255, 0.95);
409
+ backdrop-filter: blur(24px);
410
+ border: 1px solid rgba(229, 231, 233, 0.5);
411
+ border-radius: 0.5rem;
412
+ box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.15), 0 4px 6px -2px rgba(0, 0, 0, 0.1);
413
+ padding: 0.25rem 0;
414
+ font-size: 0.875rem;
415
+ user-select: none;
416
+ animation: fade-in 100ms ease-out, zoom-in 100ms ease-out;
417
+ pointer-events: auto;
418
+ }
419
+
420
+ @keyframes fade-in {
421
+ from { opacity: 0; }
422
+ to { opacity: 1; }
423
+ }
424
+
425
+ @keyframes zoom-in {
426
+ from { transform: scale(0.95); }
427
+ to { transform: scale(1); }
428
+ }
429
+
430
+ .context-menu-item {
431
+ width: 100%;
432
+ text-align: left;
433
+ display: flex;
434
+ align-items: center;
435
+ gap: 0.5rem;
436
+ padding: 0.375rem 1rem;
437
+ color: rgb(55, 65, 81);
438
+ transition: all 200ms;
439
+ border: none;
440
+ background: transparent;
441
+ cursor: pointer;
442
+ }
443
+
444
+ .context-menu-item:hover {
445
+ background: rgb(59, 130, 246);
446
+ color: white;
447
+ }
448
+
449
+ .context-menu-item:hover .context-menu-item-icon {
450
+ color: white;
451
+ }
452
+
453
+ .context-menu-item:hover .context-menu-item-shortcut {
454
+ color: rgba(255, 255, 255, 0.7);
455
+ }
456
+
457
+ .context-menu-item--disabled {
458
+ opacity: 0.5;
459
+ cursor: not-allowed;
460
+ }
461
+
462
+ .context-menu-item--disabled:hover {
463
+ background: transparent;
464
+ color: rgb(55, 65, 81);
465
+ }
466
+
467
+ .context-menu-item--disabled:hover .context-menu-item-icon {
468
+ color: rgb(107, 114, 128);
469
+ }
470
+
471
+ .context-menu-item--danger {
472
+ color: rgb(220, 38, 38);
473
+ }
474
+
475
+ .context-menu-item--danger:hover {
476
+ background: rgb(220, 38, 38);
477
+ color: white;
478
+ }
479
+
480
+ .context-menu-item--danger .context-menu-item-icon {
481
+ color: rgb(220, 38, 38);
482
+ }
483
+
484
+ .context-menu-item--danger:hover .context-menu-item-icon {
485
+ color: white;
486
+ }
487
+
488
+ .context-menu-item--has-children {
489
+ position: relative;
490
+ }
491
+
492
+ .context-menu-item--active {
493
+ background: rgb(59, 130, 246);
494
+ color: white;
495
+ }
496
+
497
+ .context-menu-item--active .context-menu-item-icon {
498
+ color: white;
499
+ }
500
+
501
+ .context-menu-item--active .context-menu-item-shortcut {
502
+ color: rgba(255, 255, 255, 0.7);
503
+ }
504
+
505
+ .context-menu-item-icon {
506
+ color: rgb(107, 114, 128);
507
+ flex-shrink: 0;
508
+ }
509
+
510
+ .context-menu-item-label {
511
+ flex: 1;
512
+ }
513
+
514
+ .context-menu-item-shortcut {
515
+ color: rgb(156, 163, 175);
516
+ font-size: 0.6875rem;
517
+ }
518
+
519
+ .context-menu-separator {
520
+ height: 1px;
521
+ background: rgb(229, 231, 233);
522
+ margin: 0.25rem 0.5rem;
523
+ }
524
+
525
+ .context-menu-item-check {
526
+ color: rgb(107, 114, 128);
527
+ flex-shrink: 0;
528
+ margin-left: auto;
529
+ }
530
+
531
+ .context-menu-item:hover .context-menu-item-check,
532
+ .context-menu-item--active .context-menu-item-check {
533
+ color: white;
534
+ }
535
+
536
+ .context-menu-item-arrow {
537
+ color: rgb(156, 163, 175);
538
+ flex-shrink: 0;
539
+ margin-left: auto;
540
+ }
541
+
542
+ .context-menu-item:hover .context-menu-item-arrow,
543
+ .context-menu-item--active .context-menu-item-arrow {
544
+ color: white;
545
+ }
546
+
547
+ .context-menu-submenu {
548
+ z-index: 10001;
549
+ }
550
+ </style>