-
-
Notifications
You must be signed in to change notification settings - Fork 283
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Context Menu]feat: 1/3, update ver rc.8
* fix contextmenu, support submenus * update version rc.8
- Loading branch information
Showing
21 changed files
with
746 additions
and
370 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
122 changes: 63 additions & 59 deletions
122
packages/radix-vue/src/ContextMenu/ContextMenuContent.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,102 +1,106 @@ | ||
<script lang="ts"> | ||
import { onClickOutside } from "@vueuse/core"; | ||
import { useCollection } from "@/shared"; | ||
export type Boundary = Element | null | Array<Element | null>; | ||
export interface ContextMenuContentProps extends PopperContentProps { | ||
asChild?: boolean; | ||
forceMount?: boolean; | ||
loop?: boolean; //false | ||
//onOpenAutoFocus?: void; | ||
//onCloseAutoFocus?: void; | ||
//onEscapeKeyDown?: void; | ||
//onPointerDownOutside?: void; | ||
//onInteractOutside?: void; | ||
} | ||
</script> | ||
|
||
<script setup lang="ts"> | ||
import { inject, watchEffect } from "vue"; | ||
import { useClickOutside } from "../shared/useClickOutside"; | ||
import { | ||
CONTEXT_MENU_INJECTION_KEY, | ||
type ContextMenuProvideValue, | ||
} from "./ContextMenuRoot.vue"; | ||
import { PopperContent, type PopperContentProps } from "@/Popper"; | ||
import { usePrimitiveElement } from "@/Primitive"; | ||
import { inject, watchEffect, nextTick } from "vue"; | ||
import { PrimitiveDiv, usePrimitiveElement } from "@/Primitive"; | ||
const injectedValue = inject<ContextMenuProvideValue>( | ||
CONTEXT_MENU_INJECTION_KEY | ||
); | ||
import { CONTEXT_MENU_INJECTION_KEY } from "./ContextMenuRoot.vue"; | ||
import { PopperContent, type PopperContentProps } from "@/Popper"; | ||
const props = withDefaults(defineProps<ContextMenuContentProps>(), { | ||
side: "right", | ||
side: "bottom", | ||
align: "start", | ||
alignOffset: 3, | ||
avoidCollisions: true, | ||
}); | ||
const injectedValue = inject(CONTEXT_MENU_INJECTION_KEY); | ||
const { primitiveElement, currentElement: tooltipContentElement } = | ||
usePrimitiveElement(); | ||
const { createCollection, getItems } = useCollection(); | ||
createCollection(tooltipContentElement); | ||
watchEffect(() => { | ||
if (tooltipContentElement.value) { | ||
if (injectedValue?.modelValue.value) { | ||
setTimeout(() => { | ||
focusFirstRadixElement(); | ||
fillItemsArray(); | ||
}, 0); | ||
window.addEventListener("mousedown", closeDialogWhenClickOutside); | ||
injectedValue.itemsArray = getItems(tooltipContentElement.value); | ||
window.addEventListener("keydown", handleKeydown); | ||
window.addEventListener("scroll", closeDialogWhenScrolled); | ||
} else { | ||
cleanupEvents(); | ||
} | ||
} | ||
}); | ||
function closeDialogWhenClickOutside(e: MouseEvent) { | ||
if (tooltipContentElement.value) { | ||
const clickOutside = useClickOutside(e, tooltipContentElement.value); | ||
if (clickOutside) { | ||
injectedValue?.hideTooltip(); | ||
window.removeEventListener("mousedown", closeDialogWhenClickOutside); | ||
} | ||
watchEffect(() => { | ||
if (injectedValue?.selectedElement.value) { | ||
cleanupEvents(); | ||
} | ||
}); | ||
function cleanupEvents() { | ||
window.removeEventListener("keydown", handleKeydown); | ||
window.removeEventListener("scroll", closeDialogWhenScrolled); | ||
} | ||
onClickOutside(tooltipContentElement, (event) => { | ||
const target = event.target as HTMLElement; | ||
if (target.closest('[role="menuitem"]')) return; | ||
injectedValue?.hideTooltip(); | ||
}); | ||
function closeDialogWhenScrolled() { | ||
injectedValue?.hideTooltip(); | ||
window.removeEventListener("scroll", closeDialogWhenScrolled); | ||
} | ||
function focusFirstRadixElement() { | ||
if (!tooltipContentElement.value) { | ||
return console.log("tooltipContentElement not found!"); | ||
} | ||
const allToggleItem = Array.from( | ||
tooltipContentElement.value.querySelectorAll( | ||
"[data-radix-vue-collection-item]" | ||
) | ||
) as HTMLElement[]; | ||
if (allToggleItem.length) { | ||
injectedValue!.selectedElement.value = allToggleItem[0]; | ||
allToggleItem[0].focus(); | ||
async function handleKeydown(e: KeyboardEvent) { | ||
e.preventDefault(); | ||
if (e.key === "ArrowDown" || e.key === "Enter" || e.keyCode === 32) { | ||
injectedValue?.changeSelected(injectedValue.itemsArray?.[0]); | ||
injectedValue?.selectedElement.value?.focus(); | ||
} else if (e.key === "ArrowUp") { | ||
const newSelectedElement = | ||
injectedValue?.itemsArray?.[injectedValue?.itemsArray.length - 1]; | ||
injectedValue?.changeSelected(newSelectedElement!); | ||
newSelectedElement?.focus(); | ||
} | ||
} | ||
function fillItemsArray() { | ||
if (!tooltipContentElement.value) { | ||
return console.log("tooltipContentElement not found!"); | ||
} | ||
const allToggleItem = Array.from( | ||
tooltipContentElement.value.querySelectorAll( | ||
"[data-radix-vue-collection-item]:not([data-disabled])" | ||
) | ||
) as HTMLElement[]; | ||
injectedValue!.itemsArray = allToggleItem; | ||
window.removeEventListener("keydown", handleKeydown); | ||
} | ||
</script> | ||
|
||
<template> | ||
<PopperContent | ||
ref="primitiveElement" | ||
v-if="injectedValue?.modelValue.value" | ||
v-bind="props" | ||
:data-state="injectedValue?.modelValue.value ? 'open' : 'closed'" | ||
:asChild="props.asChild" | ||
data-side="bottom" | ||
role="tooltip" | ||
style="pointer-events: auto" | ||
v-if="injectedValue?.modelValue.value" | ||
prioritize-position | ||
> | ||
<slot /> | ||
<PrimitiveDiv | ||
ref="primitiveElement" | ||
:data-state="injectedValue?.modelValue.value ? 'open' : 'closed'" | ||
:data-side="props.side" | ||
:data-align="props.align" | ||
role="tooltip" | ||
:asChild="props.asChild" | ||
style="pointer-events: auto" | ||
> | ||
<slot /> | ||
</PrimitiveDiv> | ||
</PopperContent> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.