Context menu thing

This commit is contained in:
space-nuko
2023-04-29 14:12:20 -07:00
parent 311ee192b2
commit 2f53758045
10 changed files with 234 additions and 17 deletions

View File

@@ -0,0 +1,47 @@
<script>
import Menu from './Menu.svelte';
import MenuOption from './MenuOption.svelte';
import MenuDivider from './MenuDivider.svelte';
import { tick } from 'svelte'
import Icon from './Icon.svelte'
let pos = { x: 0, y: 0 };
let showMenu = false;
async function onRightClick(e) {
if (showMenu) {
showMenu = false;
await new Promise(res => setTimeout(res, 100));
}
pos = { x: e.clientX, y: e.clientY };
showMenu = true;
}
function closeMenu() {
showMenu = false;
}
</script>
{#if showMenu}
<Menu {...pos} on:click={closeMenu} on:clickoutside={closeMenu}>
<MenuOption
on:click={console.log}
text="Do nothing" />
<MenuOption
on:click={console.log}
text="Do nothing, but twice" />
<MenuDivider />
<MenuOption
isDisabled={true}
on:click={console.log}
text="Whoops, disabled!" />
<MenuOption on:click={console.log}>
<Icon />
<span>Look! An icon!</span>
</MenuOption>
</Menu>
{/if}
<svelte:body on:contextmenu|preventDefault={onRightClick} />

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.5" fill-rule="evenodd" clip-rule="evenodd" d="M11.2584 13.039C10.3201 13.647 9.2013 14 8 14C4.68629 14 2 11.3137 2 8C2 6.66837 2.4338 5.43806 3.1678 4.44268L11.2584 13.039ZM12.7332 11.6878L4.60537 3.05194C5.57075 2.38838 6.74002 2 8 2C11.3137 2 14 4.68629 14 8C14 9.39043 13.527 10.6704 12.7332 11.6878ZM16 8C16 12.4183 12.4183 16 8 16C3.58172 16 0 12.4183 0 8C0 3.58172 3.58172 0 8 0C12.4183 0 16 3.58172 16 8Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 548 B

View File

@@ -0,0 +1,45 @@
<script>
import { setContext, createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import { key } from './menu.ts';
export let x;
export let y;
// whenever x and y is changed, restrict box to be within bounds
$: (() => {
if (!menuEl) return;
const rect = menuEl.getBoundingClientRect();
x = Math.min(window.innerWidth - rect.width, x);
if (y > window.innerHeight - rect.height) y -= rect.height;
})(x, y);
const dispatch = createEventDispatcher();
setContext(key, {
dispatchClick: () => dispatch('click')
});
let menuEl;
function onPageClick(e) {
if (e.target === menuEl || menuEl.contains(e.target)) return;
dispatch('clickoutside');
}
</script>
<svelte:body on:click={onPageClick} />
<div class="menu" bind:this={menuEl} style="top: {y}px; left: {x}px;">
<slot />
</div>
<style>
.menu {
z-index: var(--layer-top);
position: absolute;
display: grid;
border: 1px solid #0003;
box-shadow: 2px 2px 5px 0px #0002;
background: white;
}
</style>

View File

@@ -0,0 +1,9 @@
<hr />
<style>
hr {
border-top: 1px solid #0003;
width: 100%;
margin: 2px 0;
}
</style>

View File

@@ -0,0 +1,50 @@
<script>
import { onMount, getContext } from 'svelte';
import { key } from './menu.ts';
export let isDisabled = false;
export let text = '';
import { createEventDispatcher } from 'svelte';
const dispatch = createEventDispatcher();
const { dispatchClick } = getContext(key);
const handleClick = e => {
if (isDisabled) return;
dispatch('click');
dispatchClick();
}
</script>
<div
class:disabled={isDisabled}
on:click={handleClick}
>
{#if text}
{text}
{:else}
<slot />
{/if}
</div>
<style>
div {
padding: 4px 15px;
cursor: default;
font-size: 14px;
display: flex;
align-items: center;
grid-gap: 5px;
}
div:hover {
background: #0002;
}
div.disabled {
color: #0006;
}
div.disabled:hover {
background: white;
}
</style>

View File

@@ -0,0 +1,3 @@
const key = {};
export { key };