schlechtenburg/packages/core/lib/components/ContextMenu.tsx

85 lines
2 KiB
TypeScript

import {
watch,
defineComponent,
ref,
Ref,
} from 'vue';
import { SbButton } from './Button';
import './ContextMenu.scss';
export interface IContextMenuSlotContext {
opened: Ref<boolean>;
open: () => void;
close: () => void;
toggle: () => void;
}
export const SbContextMenu = defineComponent({
name: 'sb-context-menu',
props: {
onClose: { type: Function, default: () => {} },
onOpen: { type: Function, default: () => {} },
},
setup(props, context) {
const opened = ref(false);
const open = () => { opened.value = true; };
const close = () => { opened.value = false; };
const closeOnEscape = ($event: KeyboardEvent) => {
if ($event.key === 'Escape') {
close();
}
};
const toggle = () => { opened.value ? close() : open() };
watch(opened, (curr, prev) => {
if (curr === prev) {
return;
}
if (!curr) {
document.body.removeEventListener('click', close);
document.body.removeEventListener('keypress', closeOnEscape);
props.onClose();
} else {
setTimeout(() => {
document.body.addEventListener('click', close);
document.body.addEventListener('keypress', closeOnEscape);
props.onOpen();
});
}
});
return () => (
<div class="sb-context">
{
context.slots.context?.({
opened,
toggle,
close,
open,
}) || <SbButton {...{ onClick: toggle }}>Menu</SbButton>
}
<dialog
class="sb-context-menu"
open={opened.value ? true : undefined}
onClick={($event: Event) => {
// Make sure clicks inside do not autoclose this
$event.stopPropagation();
}}
{...{ onClose: close /* TODO: DialogHTMLAttributes needs an onClose handler type */ }}
>
{context.slots.default?.({
opened,
toggle,
close,
open,
}) || null}
</dialog>
</div>
);
},
});