master
svelte 111 lines 3.12 KB
Raw
1 <script lang="ts">
2 import { onMount } from "svelte";
3 import { appWindow } from "@tauri-apps/api/window";
4
5 export let items = [];
6 export let target: HTMLElement = null;
7
8 export let menuOpen = false;
9
10 let cursorPos = { x: 0, y: 0 }
11 let menuPos = { h: 0, w: 0 }
12 let windowSize = { h: 0, w: 0 } // keep track of window borders
13
14 onMount(() => {
15 return () => {
16 if (target !== null) {
17 target.removeEventListener("contextmenu", openContextMenu)
18 }
19 }
20 })
21
22 async function openContextMenu(e) {
23 const appWindowSize = await appWindow.innerSize();
24 let windowWidth = appWindowSize.width;
25 let windowHeight = appWindowSize.height;
26
27 menuOpen = true;
28 windowSize = {w: windowWidth, h: windowHeight};
29 cursorPos = {x: e.clientX, y: e.clientY};
30
31 // Adjust context menu position based on where it is on the window
32 // If it overlaps with the window border then move it to the right/left/top/bottom accordingly
33 if (windowSize.h - cursorPos.y < menuPos.h) {
34 cursorPos.y = cursorPos.y - menuPos.h
35 }
36 if (windowSize.w - cursorPos.x < menuPos.w) {
37 cursorPos.x = cursorPos.x - menuPos.w
38 }
39 }
40
41 function getContextMenuDimension(node){
42 // This function will get context menu dimension
43 // when navigation is shown
44 let height = node.offsetHeight;
45 let width = node.offsetWidth;
46 menuPos = {
47 w: width,
48 h: height
49 }
50 }
51
52 $: if (target !== null) {
53 target.addEventListener("contextmenu", openContextMenu)
54 }
55 </script>
56
57 <svelte:window on:contextmenu|preventDefault on:click={(e) => {
58 menuOpen = false;
59 }} on:mouseup={(e) => {
60 if (e.button === 2) {
61 menuOpen = false;
62 }
63 }}></svelte:window>
64
65 {#if menuOpen}
66 <div use:getContextMenuDimension class="context-menu" style="top:{cursorPos.y}px; left:{cursorPos.x}px">
67 {#each items as item}
68 <!-- svelte-ignore a11y-click-events-have-key-events -->
69 <div class="context-menu-option" title="" class:disabled={item.disabled} on:click={() => {
70 if (!item.disabled) {
71 item.action();
72 }
73 }}>
74 <span class="option-name">{item.name}</span>
75 {#if item.shortcut}
76 <span class="option-shortcut">{item.shortcut}</span>
77 {/if}
78 </div>
79 {/each}
80 </div>
81 {/if}
82
83 <style lang="scss">
84 .context-menu {
85 min-width: 10rem;
86 max-width: 18rem;
87 padding: 0.25rem;
88 z-index: 9999;
89 position: absolute;
90 border-radius: 3px;
91 }
92 .context-menu-option {
93 display: flex;
94 align-items: center;
95 justify-content: space-between;
96 height: 17px;
97 padding: 6px 0;
98 width: 100%;
99 font-size: 0.875rem;
100 cursor: pointer;
101 }
102 .option-name, .option-shortcut {
103 padding: 0 10px;
104 }
105 .option-shortcut {
106 margin-left: 10px;
107 }
108 .disabled {
109 cursor: not-allowed;
110 }
111 </style>