Skip to content

Commit

Permalink
refactor: menu use slot function
Browse files Browse the repository at this point in the history
  • Loading branch information
Cr0zy07 committed Nov 28, 2023
1 parent ff367fa commit 34eed36
Show file tree
Hide file tree
Showing 18 changed files with 325 additions and 371 deletions.
7 changes: 3 additions & 4 deletions packages/core/menu/src/menu-anchor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,17 @@ const menuAnchor = defineComponent({
const otherProps = reactiveOmit(_other, (key, _value) => key === undefined)

const forwardedRef = useForwardRef()
// const emits = useLis
// const emits = useListeners()

const popperScope = usePopperScope(scopeOkuMenu.value)

return () => h(OkuPopperAnchor, {
...mergeProps(attrs, otherProps),
...popperScope,
ref: forwardedRef,
}, slots)
}, () => slots.default?.())
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuMenuAnchor = menuAnchor as typeof menuAnchor &
(new () => { $props: MenuAnchorNativeElement })
export const OkuMenuAnchor = menuAnchor as typeof menuAnchor & (new () => { $props: MenuAnchorNativeElement })
7 changes: 3 additions & 4 deletions packages/core/menu/src/menu-arrow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,17 @@ const menuArrow = defineComponent({
const otherProps = reactiveOmit(_other, (key, _value) => key === undefined)

const forwardedRef = useForwardRef()
// const listeners = useListeners()
// const emits = useListeners()

const popperScope = usePopperScope(scopeOkuMenu.value)

return () => h(OkuPopperArrow, {
...popperScope,
...mergeProps(attrs, otherProps),
ref: forwardedRef,
}, slots)
}, () => slots.default?.())
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuMenuArrow = menuArrow as typeof menuArrow &
(new () => { $props: MenuArrowNativeElement })
export const OkuMenuArrow = menuArrow as typeof menuArrow & (new () => { $props: MenuArrowNativeElement })
5 changes: 2 additions & 3 deletions packages/core/menu/src/menu-checkbox-item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,9 @@ const menuCheckboxItem = defineComponent({
}, () => {
emit('checkedChange', isIndeterminate(checked.value) ? true : !checked.value)
}, { checkForDefaultPrevented: false }),
}, slots)
}, () => slots.default?.())
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuMenuCheckboxItem = menuCheckboxItem as typeof menuCheckboxItem &
(new () => { $props: MenuCheckboxItemNativeElement })
export const OkuMenuCheckboxItem = menuCheckboxItem as typeof menuCheckboxItem & (new () => { $props: MenuCheckboxItemNativeElement })
183 changes: 88 additions & 95 deletions packages/core/menu/src/menu-content-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,103 +143,96 @@ const menuContentImpl = defineComponent({
contentRef.value?.focus()
}),
onUnmountAutoFocus: event => emit('closeAutoFocus', event),
}, {
default: () => h(OkuDismissableLayer, {
asChild: true,
disableOutsidePointerEvents: disableOutsidePointerEvents.value,
onEscapeKeydown: event => emit('escapeKeydown', event),
onPointerdownOutside: event => emit('pointerdownOutside', event),
onFocusOutside: event => emit('focusOutside', event),
onInteractOutside: event => emit('interactOutside', event),
onDismiss: () => emit('dismiss'),
}, {
default: () => h(OkuRovingFocusGroup, {
asChild: true,
...rovingFocusGroupScope,
dir: rootInject.dir.value,
orientation: 'vertical',
loop: loop.value,
currentTabStopId: currentItemId.value,
onCurrentTabStopIdChange: (tabStopId) => {
currentItemId.value = tabStopId
},
onEntryFocus: composeEventHandlers<MenuContentImplEmits['entryFocus'][0]>((event) => {
emit('entryFocus', event)
}, (event) => {
// only focus first item when using keyboard
if (!rootInject.isUsingKeyboardRef.value)
event.preventDefault()
}),
}, {
default: () => h(OkuPopperContent, {
'role': 'menu',
'aria-orientation': 'vertical',
'data-state': getOpenState(inject.open.value!),
'data-oku-menu-content': '',
'dir': rootInject.dir.value,
...popperScope,
...mergeProps(attrs, otherProps),
'ref': composedRefs,
'style': { outline: 'none', ...attrs.style as any },
'onKeydown': composeEventHandlers<MenuContentImplEmits['keydown'][0]>((event) => {
emit('keydown', event)
}, (event) => {
// submenu key events bubble through portals. We only care about keys in this menu.
const target = event.target as HTMLElement
const isKeyDownInside = target.closest('[data-oku-menu-content]') === event.currentTarget
const isModifierKey = event.ctrlKey || event.altKey || event.metaKey
const isCharacterKey = event.key.length === 1
if (isKeyDownInside) {
// menus should not be navigated using tab key so we prevent it
if (event.key === 'Tab')
event.preventDefault()
if (!isModifierKey && isCharacterKey)
handleTypeaheadSearch(event.key)
}
// focus first/last item based on key pressed
const content = contentRef.value
if (event.target !== content)
return
if (!FIRST_LAST_KEYS.includes(event.key))
return
event.preventDefault()
const items = getItems().filter(item => !item.disabled)
const candidateNodes = items.map(item => item.ref.value!)
if (LAST_KEYS.includes(event.key))
candidateNodes.reverse()
focusFirst(candidateNodes)
}),
'onBlur': composeEventHandlers<MenuContentImplEmits['blur'][0]>((event) => {
emit('blur', event)
}, async (event) => {
// clear search buffer when leaving the menu
if (!(event.currentTarget as HTMLElement).contains(event.target as HTMLElement)) {
await nextTick()

window.clearTimeout(timerRef.value)
searchRef.value = ''
}
}),
'onPointermove': composeEventHandlers<MenuContentImplEmits['pointermove'][0]>((event) => {
emit('pointermove', event)
}, whenMouse((event) => {
const target = event.target as HTMLDivElement
const pointerXHasChanged = lastPointerXRef.value !== event.clientX
// We don't use `event.movementX` for this check because Safari will
// always return `0` on a pointer event.
if ((event.currentTarget as HTMLElement).contains(target) && pointerXHasChanged) {
const newDir = event.clientX > lastPointerXRef.value ? 'right' : 'left'
pointerDirRef.value = newDir
lastPointerXRef.value = event.clientX
}
})),
}, slots),
}),
}, () => h(OkuDismissableLayer, {
asChild: true,
disableOutsidePointerEvents: disableOutsidePointerEvents.value,
onEscapeKeydown: event => emit('escapeKeydown', event),
onPointerdownOutside: event => emit('pointerdownOutside', event),
onFocusOutside: event => emit('focusOutside', event),
onInteractOutside: event => emit('interactOutside', event),
onDismiss: () => emit('dismiss'),
}, () => h(OkuRovingFocusGroup, {
asChild: true,
...rovingFocusGroupScope,
dir: rootInject.dir.value,
orientation: 'vertical',
loop: loop.value,
currentTabStopId: currentItemId.value,
onCurrentTabStopIdChange: (tabStopId) => {
currentItemId.value = tabStopId
},
onEntryFocus: composeEventHandlers<MenuContentImplEmits['entryFocus'][0]>((event) => {
emit('entryFocus', event)
}, (event) => {
// only focus first item when using keyboard
if (!rootInject.isUsingKeyboardRef.value)
event.preventDefault()
}),
})
}, () => h(OkuPopperContent, {
'role': 'menu',
'aria-orientation': 'vertical',
'data-state': getOpenState(inject.open.value!),
'data-oku-menu-content': '',
'dir': rootInject.dir.value,
...popperScope,
...mergeProps(attrs, otherProps),
'ref': composedRefs,
'style': { outline: 'none', ...attrs.style as any },
'onKeydown': composeEventHandlers<MenuContentImplEmits['keydown'][0]>((event) => {
emit('keydown', event)
}, (event) => {
// submenu key events bubble through portals. We only care about keys in this menu.
const target = event.target as HTMLElement
const isKeyDownInside = target.closest('[data-oku-menu-content]') === event.currentTarget
const isModifierKey = event.ctrlKey || event.altKey || event.metaKey
const isCharacterKey = event.key.length === 1
if (isKeyDownInside) {
// menus should not be navigated using tab key so we prevent it
if (event.key === 'Tab')
event.preventDefault()
if (!isModifierKey && isCharacterKey)
handleTypeaheadSearch(event.key)
}
// focus first/last item based on key pressed
const content = contentRef.value
if (event.target !== content)
return
if (!FIRST_LAST_KEYS.includes(event.key))
return
event.preventDefault()
const items = getItems().filter(item => !item.disabled)
const candidateNodes = items.map(item => item.ref.value!)
if (LAST_KEYS.includes(event.key))
candidateNodes.reverse()
focusFirst(candidateNodes)
}),
'onBlur': composeEventHandlers<MenuContentImplEmits['blur'][0]>((event) => {
emit('blur', event)
}, async (event) => {
// clear search buffer when leaving the menu
if (!(event.currentTarget as HTMLElement).contains(event.target as HTMLElement)) {
await nextTick()

window.clearTimeout(timerRef.value)
searchRef.value = ''
}
}),
'onPointermove': composeEventHandlers<MenuContentImplEmits['pointermove'][0]>((event) => {
emit('pointermove', event)
}, whenMouse((event) => {
const target = event.target as HTMLDivElement
const pointerXHasChanged = lastPointerXRef.value !== event.clientX
// We don't use `event.movementX` for this check because Safari will
// always return `0` on a pointer event.
if ((event.currentTarget as HTMLElement).contains(target) && pointerXHasChanged) {
const newDir = event.clientX > lastPointerXRef.value ? 'right' : 'left'
pointerDirRef.value = newDir
lastPointerXRef.value = event.clientX
}
})),
}, () => slots.default?.()))))
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuMenuContentImpl = menuContentImpl as typeof menuContentImpl &
(new () => { $props: MenuContentImplNativeElement })
export const OkuMenuContentImpl = menuContentImpl as typeof menuContentImpl & (new () => { $props: MenuContentImplNativeElement })
29 changes: 11 additions & 18 deletions packages/core/menu/src/menu-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,18 @@ const menuContent = defineComponent({
const inject = useMenuInject(MENU_CONTENT_NAME, scopeOkuMenu.value)
const rootInject = useMenuRootInject(MENU_CONTENT_NAME, scopeOkuMenu.value)

return () => h(CollectionProvider, { scope: scopeOkuMenu.value }, {
default: () => h(OkuPresence, { present: forceMount.value || inject.open.value }, {
default: () => h(CollectionSlot, { scope: scopeOkuMenu.value }, {
default: () => rootInject.modal.value
? h(OkuMenuRootContentModal, {
...mergeProps(attrs, otherProps),
ref: forwardedRef,
}, slots)
: h(OkuMenuRootContentNonModal, {
...mergeProps(attrs, otherProps),
ref: forwardedRef,
}, slots),
}),
}),
})
return () => [h(CollectionProvider, { scope: scopeOkuMenu.value }, () => h(OkuPresence, { present: forceMount.value || inject.open.value }, () => h(CollectionSlot, { scope: scopeOkuMenu.value }, () => rootInject.modal.value
? h(OkuMenuRootContentModal, {
...mergeProps(attrs, otherProps),
ref: forwardedRef,
}, slots)
: h(OkuMenuRootContentNonModal, {
...mergeProps(attrs, otherProps),
ref: forwardedRef,
}, () => slots.default?.())))),
]
},

})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuMenuContent = menuContent as typeof menuContent &
(new () => { $props: MenuContentNativeElement })
export const OkuMenuContent = menuContent as typeof menuContent & (new () => { $props: MenuContentNativeElement })
5 changes: 2 additions & 3 deletions packages/core/menu/src/menu-group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,9 @@ const menuGroup = defineComponent({
...mergeProps(attrs, otherProps),
role: 'group',
ref: forwardedRef,
}, slots.default?.())
}, () => slots.default?.())
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuMenuGroup = menuGroup as typeof menuGroup &
(new () => { $props: MenuGroupNativeElement })
export const OkuMenuGroup = menuGroup as typeof menuGroup & (new () => { $props: MenuGroupNativeElement })
Loading

0 comments on commit 34eed36

Please sign in to comment.