Skip to content

Commit

Permalink
feat(dropdown-menu): new component
Browse files Browse the repository at this point in the history
  • Loading branch information
Cr0zy07 committed Nov 11, 2023
1 parent 800e472 commit 74ed10a
Show file tree
Hide file tree
Showing 30 changed files with 1,498 additions and 6 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ Enter the component you want most in the components, leave the emojis and follow
| [Collapsible](https://oku-ui.com/primitives/components/collapsible) | <span><a href="https://www.npmjs.com/package/@oku-ui/collapsible "><img src="https://img.shields.io/npm/v/@oku-ui/collapsible?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/collapsible"> <img src="https://img.shields.io/npm/dm/@oku-ui/collapsible?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/collapsible"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> |
| [Context Menu](https://github.com/oku-ui/primitives/issues/8) | A menu that appears when a user interacts with an element's trigger | Not Started | - |
| [Dialog](https://oku-ui.com/primitives/components/dialog) | <span><a href="https://www.npmjs.com/package/@oku-ui/dialog "><img src="https://img.shields.io/npm/v/@oku-ui/dialog?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/dialog"> <img src="https://img.shields.io/npm/dm/@oku-ui/dialog?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/dialog"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> |
| [Dropdown Menu](https://github.com/oku-ui/primitives/issues/10) | A menu that appears when a user interacts with an element's trigger | Not Started | - |
| [Form](https://github.com/oku-ui/primitives/issues/11) | A group of form controls | Not Started | - |
| [Dropdown Menu](https://oku-ui.com/primitives/components/dropdown-menu) | <span><a href="https://www.npmjs.com/package/@oku-ui/dropdown-menu "><img src="https://img.shields.io/npm/v/@oku-ui/dropdown-menu?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/dropdown-menu"> <img src="https://img.shields.io/npm/dm/@oku-ui/dropdown-menu?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/dropdown-menu"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> |
| [Hover Card](https://oku-ui.com/primitives/components/hover-card) | <span><a href="https://www.npmjs.com/package/@oku-ui/hover-card "><img src="https://img.shields.io/npm/v/@oku-ui/hover-card?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/hover-card"> <img src="https://img.shields.io/npm/dm/@oku-ui/hover-card?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/hover-card"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> |
| [Label](https://oku-ui.com/primitives/components/label) | <span><a href="https://www.npmjs.com/package/@oku-ui/label "><img src="https://img.shields.io/npm/v/@oku-ui/label?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/label"> <img src="https://img.shields.io/npm/dm/@oku-ui/label?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/label"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> |
| [Menubar](https://github.com/oku-ui/primitives/issues/13) | A menu that appears when a user interacts with an element's trigger | 🚧 In Progress | - |
Expand All @@ -54,15 +53,19 @@ Enter the component you want most in the components, leave the emojis and follow
| [Toggle Group](https://oku-ui.com/primitives/components/toggle-group) | <span><a href="https://www.npmjs.com/package/@oku-ui/toggle-group "><img src="https://img.shields.io/npm/v/@oku-ui/toggle-group?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/toggle-group"> <img src="https://img.shields.io/npm/dm/@oku-ui/toggle-group?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/slider"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> |
| [Toolbar](https://oku-ui.com/primitives/components/toolbar) | <span><a href="https://www.npmjs.com/package/@oku-ui/toolbar "><img src="https://img.shields.io/npm/v/@oku-ui/toolbar?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/toolbar"> <img src="https://img.shields.io/npm/dm/@oku-ui/switch?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/toolbar"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> |
| [Tooltip](https://oku-ui.com/primitives/components/tooltip) | <span><a href="https://www.npmjs.com/package/@oku-ui/tooltip "><img src="https://img.shields.io/npm/v/@oku-ui/tooltip?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/tooltip"> <img src="https://img.shields.io/npm/dm/@oku-ui/tooltip?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/tooltip"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> |
[Primitives](https://oku-ui.com/primitives) | <span><a href="https://www.npmjs.com/package/@oku-ui/primitives "><img src="https://img.shields.io/npm/v/@oku-ui/primitives?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/primitives"> <img src="https://img.shields.io/npm/dm/@oku-ui/primitives?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/primitives"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> |
| [Primitives](https://oku-ui.com/primitives) | <span><a href="https://www.npmjs.com/package/@oku-ui/primitives "><img src="https://img.shields.io/npm/v/@oku-ui/primitives?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/primitives"> <img src="https://img.shields.io/npm/dm/@oku-ui/primitives?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/primitives"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> |


## Utils

[Nuxt Module](https://oku-ui.com/primitives/introduction/nuxt) | <span><a href="https://www.npmjs.com/package/@oku-ui/primitives-nuxt"><img src="https://img.shields.io/npm/v/@oku-ui/primitives-nuxt?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/primitives-nuxt"> <img src="https://img.shields.io/npm/dm/@oku-ui/primitives-nuxt?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="ttps://oku-ui.com/primitives/introduction/nuxt"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> |
| Util | Description | Status | Docs |
| --- | --- | --- | --- |
| [Nuxt Module](https://oku-ui.com/primitives/introduction/nuxt) | <span><a href="https://www.npmjs.com/package/@oku-ui/primitives-nuxt"><img src="https://img.shields.io/npm/v/@oku-ui/primitives-nuxt?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/primitives-nuxt"> <img src="https://img.shields.io/npm/dm/@oku-ui/primitives-nuxt?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="ttps://oku-ui.com/primitives/introduction/nuxt"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span> |

## Core

| Core | Description | Status | Docs |
| --- | --- | --- | --- |
| [Menu](https://oku-ui.com/primitives/core/menu) | <span><a href="https://www.npmjs.com/package/@oku-ui/menu "><img src="https://img.shields.io/npm/v/@oku-ui/menu?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/menu"> <img src="https://img.shields.io/npm/dm/@oku-ui/menu?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/core/menu"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> |

## Community
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@oku-ui/dialog": "workspace:^",
"@oku-ui/direction": "workspace:^",
"@oku-ui/dismissable-layer": "workspace:^",
"@oku-ui/dropdown-menu": "workspace:^",
"@oku-ui/focus-guards": "workspace:^",
"@oku-ui/focus-scope": "workspace:^",
"@oku-ui/hover-card": "workspace:^",
Expand Down Expand Up @@ -137,6 +138,7 @@
"@oku-ui/dialog": "workspace:^",
"@oku-ui/direction": "workspace:^",
"@oku-ui/dismissable-layer": "workspace:^",
"@oku-ui/dropdown-menu": "workspace:^",
"@oku-ui/focus-guards": "workspace:^",
"@oku-ui/focus-scope": "workspace:^",
"@oku-ui/hover-card": "workspace:^",
Expand Down
16 changes: 16 additions & 0 deletions packages/components/dropdown-menu/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Dropdown Menu
Displays a menu to the user—such as a set of actions or functions—triggered by a button.

![@oku-ui/dropdown-menu](./../../../.github/assets/og/oku-dropdown-menu.jpg)

| Description | Status | Docs |
| --- | --- | --- |
| <span><a href="https://www.npmjs.com/package/@oku-ui/dropdown-menu "><img src="https://img.shields.io/npm/v/@oku-ui/dropdown-menu?style=flat&colorA=18181B&colorB=28CF8D" alt="Version"></a> </span> | <span> <a href="https://www.npmjs.com/package/@oku-ui/dropdown-menu"> <img src="https://img.shields.io/npm/dm/@oku-ui/dropdown-menu?style=flat&colorA=18181B&colorB=28CF8D" alt="Downloads"> </a> </span> | <span> <a href="https://oku-ui.com/primitives/components/dropdown-menu"><img src="https://img.shields.io/badge/Open%20Documentation-18181B" alt="Website"></a> </span>

## Installation

```sh
$ pnpm add @oku-ui/dropdown-menu
```

[Documentation](https://oku-ui.com/primitives/components/dropdown-menu)
7 changes: 7 additions & 0 deletions packages/components/dropdown-menu/build.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineBuildConfig } from 'unbuild'

const isClean = (process.env.CLEAN || 'false') === 'true'
export default defineBuildConfig({
declaration: true,
clean: isClean,
})
56 changes: 56 additions & 0 deletions packages/components/dropdown-menu/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"name": "@oku-ui/dropdown-menu",
"type": "module",
"version": "0.5.0",
"license": "MIT",
"source": "src/index.ts",
"funding": "https://github.com/sponsors/productdevbook",
"homepage": "https://oku-ui.com/primitives",
"repository": {
"type": "git",
"url": "git+https://github.com/oku-ui/primitives.git",
"directory": "packages/components/dropdown-menu"
},
"bugs": {
"url": "https://github.com/oku-ui/primitives/issues"
},
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs"
}
},
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"engines": {
"node": ">=18"
},
"scripts": {
"build": "unbuild",
"dev": "unbuild --stub",
"clean": "rimraf ./dist && rimraf ./node_modules"
},
"peerDependencies": {
"vue": "^3.3.0"
},
"dependencies": {
"@floating-ui/vue": "^1.0.2",
"@oku-ui/direction": "latest",
"@oku-ui/dismissable-layer": "latest",
"@oku-ui/focus-scope": "latest",
"@oku-ui/menu": "latest",
"@oku-ui/primitive": "latest",
"@oku-ui/provide": "latest",
"@oku-ui/use-composable": "latest",
"@oku-ui/utils": "latest"
},
"devDependencies": {
"tsconfig": "workspace:^"
},
"publishConfig": {
"access": "public"
}
}
41 changes: 41 additions & 0 deletions packages/components/dropdown-menu/src/dropdown-menu-arrow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { defineComponent, h, mergeProps, reactive, toRefs } from 'vue'
import { reactiveOmit, useForwardRef } from '@oku-ui/use-composable'
import { OkuMenuArrow } from '@oku-ui/menu'
import { DROPDOWN_MENU_ARROW_NAME, dropdownMenuArrowProps, scopedDropdownMenuProps, useMenuScope } from './props'
import type { DropdownMenuArrowNativeElement } from './props'

const dropdownMenuArrow = defineComponent({
name: DROPDOWN_MENU_ARROW_NAME,
components: {
OkuMenuArrow,
},
inheritAttrs: false,
props: {
...dropdownMenuArrowProps.props,
...scopedDropdownMenuProps,
},
emits: dropdownMenuArrowProps.emits,
setup(props, { attrs, slots }) {
const {
scopeOkuDropdownMenu,
...arrowProps
} = toRefs(props)

const _other = reactive(arrowProps)
const otherProps = reactiveOmit(_other, (key, _value) => key === undefined)

const forwardedRef = useForwardRef()

const menuScope = useMenuScope(scopeOkuDropdownMenu.value)

return () => h(OkuMenuArrow, {
...menuScope,
...mergeProps(attrs, otherProps),
ref: forwardedRef,
}, slots)
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuDropdownMenuArrow = dropdownMenuArrow as typeof dropdownMenuArrow &
(new () => { $props: DropdownMenuArrowNativeElement })
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { defineComponent, h, mergeProps, reactive, toRefs } from 'vue'
import { reactiveOmit, useForwardRef, useListeners } from '@oku-ui/use-composable'
import { OkuMenuCheckboxItem } from '@oku-ui/menu'
import { DROPDOWN_MENU_CHECKBOX_ITEM_NAME, dropdownMenuCheckboxItemProps, scopedDropdownMenuProps, useMenuScope } from './props'
import type { DropdownMenuCheckboxItemNativeElement } from './props'

const dropdownMenuCheckboxItem = defineComponent({
name: DROPDOWN_MENU_CHECKBOX_ITEM_NAME,
components: {
OkuMenuCheckboxItem,
},
inheritAttrs: false,
props: {
...dropdownMenuCheckboxItemProps.props,
...scopedDropdownMenuProps,
},
emits: dropdownMenuCheckboxItemProps.emits,
setup(props, { attrs, slots }) {
const {
scopeOkuDropdownMenu,
...checkboxItemProps
} = toRefs(props)

const _other = reactive(checkboxItemProps)
const otherProps = reactiveOmit(_other, (key, _value) => key === undefined)

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

const menuScope = useMenuScope(scopeOkuDropdownMenu.value)

return () => h(OkuMenuCheckboxItem, {
...menuScope,
...mergeProps(attrs, otherProps, emits),
ref: forwardedRef,
}, slots)
},
})

// TODO: https://github.com/vuejs/core/pull/7444 after delete
export const OkuDropdownMenuCheckboxItem = dropdownMenuCheckboxItem as typeof dropdownMenuCheckboxItem &
(new () => { $props: DropdownMenuCheckboxItemNativeElement })
74 changes: 74 additions & 0 deletions packages/components/dropdown-menu/src/dropdown-menu-content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { defineComponent, h, mergeProps, reactive, ref, toRefs } from 'vue'
import { reactiveOmit, useForwardRef, useListeners } from '@oku-ui/use-composable'
import { composeEventHandlers } from '@oku-ui/utils'
import { OkuMenuContent } from '@oku-ui/menu'
import { DROPDOWN_MENU_CONTENT_NAME, dropdownMenuContentProps, scopedDropdownMenuProps, useDropdownMenuInject, useMenuScope } from './props'
import type { DropdownMenuContentEmits } from './props'

const dropdownMenuContent = defineComponent({
name: DROPDOWN_MENU_CONTENT_NAME,
components: {
OkuMenuContent,
},
inheritAttrs: false,
props: {
...dropdownMenuContentProps.props,
...scopedDropdownMenuProps,
},
emits: dropdownMenuContentProps.emits,
setup(props, { attrs, emit, slots }) {
const {
scopeOkuDropdownMenu,
...contentProps
} = toRefs(props)

const _other = reactive(contentProps)
const otherProps = reactiveOmit(_other, (key, _value) => key === undefined)

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

const inject = useDropdownMenuInject(DROPDOWN_MENU_CONTENT_NAME, scopeOkuDropdownMenu.value)
const menuScope = useMenuScope(scopeOkuDropdownMenu.value)
const hasInteractedOutsideRef = ref(false)

return () => h(OkuMenuContent, {
'id': inject.contentId.value,
'aria-labelledby': inject.triggerId.value,
...menuScope,
...mergeProps(attrs, otherProps, emits),
'ref': forwardedRef,
'onCloseAutoFocus': composeEventHandlers<DropdownMenuContentEmits['closeAutoFocus'][0]>((event) => {
emit('closeAutoFocus', event)
}, (event) => {
if (!hasInteractedOutsideRef.value)
inject.triggerRef.value?.focus()
hasInteractedOutsideRef.value = false
// Always prevent auto focus because we either focus manually or want user agent focus
event.preventDefault()
}),
'onInteractOutside': composeEventHandlers<DropdownMenuContentEmits['interactOutside'][0]>((event) => {
emit('interactOutside', event)
}, (event) => {
const originalEvent = event.detail.originalEvent as PointerEvent
const ctrlLeftClick = originalEvent.button === 0 && originalEvent.ctrlKey === true
const isRightClick = originalEvent.button === 2 || ctrlLeftClick
if (!inject.modal.value || isRightClick)
hasInteractedOutsideRef.value = true
}),
'style': {
...attrs.style as any,
// re-namespace exposed content custom properties
...{
'--oku-dropdown-menu-content-transform-origin': 'var(--oku-popper-transform-origin)',
'--oku-dropdown-menu-content-available-width': 'var(--oku-popper-available-width)',
'--oku-dropdown-menu-content-available-height': 'var(--oku-popper-available-height)',
'--oku-dropdown-menu-trigger-width': 'var(--oku-popper-anchor-width)',
'--oku-dropdown-menu-trigger-height': 'var(--oku-popper-anchor-height)',
},
},
}, slots)
},
})

export const OkuDropdownMenuContent = dropdownMenuContent
41 changes: 41 additions & 0 deletions packages/components/dropdown-menu/src/dropdown-menu-group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { defineComponent, h, mergeProps, reactive, toRefs } from 'vue'
import { reactiveOmit, useForwardRef } from '@oku-ui/use-composable'
import { OkuMenuGroup } from '@oku-ui/menu'
import { DROPDOWN_MENU_GROUP_NAME, dropdownMenuGroupProps, scopedDropdownMenuProps, useMenuScope } from './props'
import type { DropdownMenuGroupNativeElement } from './props'

const dropdownMenuGroup = defineComponent({
name: DROPDOWN_MENU_GROUP_NAME,
components: {
OkuMenuGroup,
},
inheritAttrs: false,
props: {
...dropdownMenuGroupProps.props,
...scopedDropdownMenuProps,
},
emits: dropdownMenuGroupProps.emits,
setup(props, { attrs, slots }) {
const {
scopeOkuDropdownMenu,
...groupProps
} = toRefs(props)

const _other = reactive(groupProps)
const otherProps = reactiveOmit(_other, (key, _value) => key === undefined)

const forwardedRef = useForwardRef()

const menuScope = useMenuScope(scopeOkuDropdownMenu.value)

return () => h(OkuMenuGroup, {
...menuScope,
...mergeProps(attrs, otherProps),
ref: forwardedRef,
}, slots)
},
})

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

0 comments on commit 74ed10a

Please sign in to comment.