wip(menu): use dropdown!!!

This commit is contained in:
07akioni 2021-01-06 01:35:25 +08:00
parent caa3f5df12
commit 7b2f77a573
14 changed files with 315 additions and 320 deletions

View File

@ -203,7 +203,9 @@ export default defineComponent({
[createKey('optionIconPrefixWidth', size)]: optionIconPrefixWidth,
[createKey('optionPrefixWidth', size)]: optionPrefixWidth,
[createKey('fontSize', size)]: fontSize,
[createKey('optionHeight', size)]: optionHeight
[createKey('optionHeight', size)]: optionHeight,
[createKey('optionIconSize', size)]: optionIconSize,
[createKey('groupHeaderFontSize', size)]: groupHeaderFontSize
}
} = themeRef.value
return {
@ -223,7 +225,9 @@ export default defineComponent({
'--option-text-color': optionTextColor,
'--prefix-color': prefixColor,
'--suffix-color': suffixColor,
'--group-header-text-color': groupHeaderTextColor
'--group-header-text-color': groupHeaderTextColor,
'--group-header-font-size': groupHeaderFontSize,
'--option-icon-size': optionIconSize
}
})
}

View File

@ -44,7 +44,8 @@ export default defineComponent({
class: 'n-dropdown-option-body__label',
'n-dropdown-option': true
},
[h(render, { render: rawNode.label })]
// TODO: Workaround, menu campatible
[h(render, { render: rawNode.label ?? rawNode.title })]
),
h('div', {
class: [

View File

@ -122,6 +122,7 @@ export default defineComponent({
render () {
const {
NDropdown: { animated },
rawNode,
mergedShowSubmenu
} = this
const submenuVNode = mergedShowSubmenu
@ -163,7 +164,7 @@ export default defineComponent({
],
'n-dropdown-option': true
},
[h(render, { render: this.rawNode.icon })]
[h(render, { render: rawNode.icon })]
),
h(
'div',
@ -171,7 +172,8 @@ export default defineComponent({
class: 'n-dropdown-option-body__label',
'n-dropdown-option': true
},
[h(render, { render: this.rawNode.label })]
// TODO: Workaround, menu campatible
[h(render, { render: rawNode.label ?? rawNode.title })]
),
h(
'div',

View File

@ -18,6 +18,7 @@ import fadeInScaleUpTransition from '../../../_styles/transitions/fade-in-scale-
// --option-text-color
// --prefix-color
// --suffix-color
// --option-icon-size
export default cB('dropdown-menu', {
transformOrigin: 'inherit',
padding: 'var(--padding)',
@ -46,6 +47,7 @@ export default cB('dropdown-menu', {
backgroundColor: 'var(--option-color-hover)'
}),
cM('group', {
fontSize: 'calc(var(--font-size) - 1px)',
color: 'var(--group-header-text-color)'
}, [
cE('prefix', {
@ -68,7 +70,7 @@ export default cB('dropdown-menu', {
cB('icon', {
transition: 'color .3s var(--bezier)',
color: 'var(--prefix-color)',
fontSize: '16px'
fontSize: 'var(--option-icon-size)'
})
]),
cE('label', {
@ -91,7 +93,7 @@ export default cB('dropdown-menu', {
cB('icon', {
transition: 'color .3s var(--bezier)',
color: 'var(--suffix-color)',
fontSize: '16px'
fontSize: 'var(--option-icon-size)'
})
]),
cB('dropdown-menu', {

View File

@ -1,5 +1,9 @@
export default {
padding: '4px 0',
optionIconSizeSmall: '14px',
optionIconSizeMedium: '16px',
optionIconSizeLarge: '16px',
optionIconSizeHuge: '18px',
optionSuffixWidthSmall: '28px',
optionSuffixWidthMedium: '28px',
optionSuffixWidthLarge: '32px',

View File

@ -1,4 +1,12 @@
import { h, nextTick, ref, toRef, computed, onMounted } from 'vue'
import {
h,
nextTick,
ref,
toRef,
computed,
onMounted,
defineComponent
} from 'vue'
import { createTreeMate } from 'treemate'
import { useCompitable, useMergedState } from 'vooks'
import { useTheme } from '../../_mixins'
@ -6,7 +14,7 @@ import { itemRenderer } from './utils'
import { menuLight } from '../styles'
import style from './styles/index.cssr.js'
export default {
export default defineComponent({
name: 'Menu',
provide () {
return {
@ -244,4 +252,4 @@ export default {
this.tmNodes.map((tmNode) => itemRenderer(tmNode))
)
}
}
})

View File

@ -8,8 +8,8 @@
>
<n-tooltip
trigger="hover"
:placement="popoverPlacement"
:disabled="!popoverEnabled"
:placement="dropdownPlacement"
:disabled="!dropdownEnabled"
>
<template #trigger>
<n-menu-item-content
@ -29,14 +29,14 @@
</template>
<script>
import { computed } from 'vue'
import { computed, defineComponent } from 'vue'
import NMenuItemContent from './MenuItemContent.vue'
import { NTooltip } from '../../tooltip'
import menuChildMixin from './menu-child-mixin'
import { useMemo } from 'vooks'
import { useInjectionRef } from '../../_utils/composable'
export default {
export default defineComponent({
name: 'MenuItem',
components: {
NMenuItemContent,
@ -84,11 +84,11 @@ export default {
}
},
computed: {
popoverEnabled () {
dropdownEnabled () {
return (
!this.horizontal &&
this.root &&
this.menuCollapsed &&
!this.horizontal &&
!this.mergedDisabled
)
}
@ -105,5 +105,5 @@ export default {
}
}
}
}
})
</script>

View File

@ -4,10 +4,9 @@
:style="style"
:class="{
'n-menu-item-content--collapsed': collapsed,
'n-menu-item-content--child-selected': childSelected,
'n-menu-item-content--child-active': childActive,
'n-menu-item-content--disabled': disabled,
'n-menu-item-content--hover': hover,
'n-menu-item-content--uncollapsable': uncollapsable
'n-menu-item-content--hover': hover
}"
@click="handleClick"
>
@ -25,9 +24,10 @@
</template>
<script>
import { defineComponent } from 'vue'
import { render } from '../../_utils'
export default {
export default defineComponent({
name: 'MenuItemContent',
components: {
render
@ -69,7 +69,7 @@ export default {
type: Boolean,
default: false
},
childSelected: {
childActive: {
type: Boolean,
default: false
},
@ -77,10 +77,6 @@ export default {
type: Boolean,
default: false
},
uncollapsable: {
type: Boolean,
default: false
},
onClick: {
type: Function,
default: () => {}
@ -105,5 +101,5 @@ export default {
this.onClick()
}
}
}
})
</script>

View File

@ -1,9 +1,9 @@
import { h } from 'vue'
import { h, defineComponent } from 'vue'
import { render } from '../../_utils'
import { itemRenderer } from './utils'
import menuChildMixin from './menu-child-mixin'
export default {
export default defineComponent({
name: 'MenuItemGroup',
mixins: [menuChildMixin],
provide () {
@ -44,4 +44,4 @@ export default {
]
)
}
}
})

View File

@ -1,13 +1,13 @@
import { h, withDirectives, vShow, ref } from 'vue'
import { h, withDirectives, vShow, ref, defineComponent } from 'vue'
import { NFadeInExpandTransition } from '../../_base'
import { NPopover } from '../../popover'
import { NDropdown } from '../../dropdown'
import NMenuItemContent from './MenuItemContent.vue'
import menuChildMixin from './menu-child-mixin'
import { itemRenderer } from './utils'
import { useMemo } from 'vooks'
import { useInjectionRef } from '../../_utils/composable'
export default {
export default defineComponent({
name: 'Submenu',
mixins: [menuChildMixin],
provide () {
@ -17,6 +17,14 @@ export default {
}
},
props: {
rawNodes: {
type: Array,
required: true
},
tmNodes: {
type: Array,
required: true
},
extra: {
type: [String, Function],
default: undefined
@ -25,10 +33,6 @@ export default {
type: Boolean,
default: false
},
tmNodes: {
type: Array,
required: true
},
icon: {
type: Function,
default: undefined
@ -41,10 +45,10 @@ export default {
setup (props) {
const activePathRef = useInjectionRef('NMenu', 'activePath')
return {
selectedInside: useMemo(() => {
childActive: useMemo(() => {
return activePathRef.value.includes(props.internalKey)
}),
popoverShow: ref(false)
dropdownShow: ref(false)
}
},
computed: {
@ -56,13 +60,12 @@ export default {
},
collapsed () {
if (this.horizontal) return false
if (this.insidePopover) return false
if (this.menuCollapsed) {
return true
}
return !this.NMenu.mergedExpandedKeys.includes(this.internalKey)
},
popoverEnabled () {
dropdownEnabled () {
return !this.mergedDisabled && (this.horizontal || this.menuCollapsed)
}
},
@ -80,7 +83,7 @@ export default {
}
},
handlePopoverShowChange (value) {
this.popoverShow = value
this.dropdownShow = value
}
},
render () {
@ -94,11 +97,10 @@ export default {
title,
extra,
horizontal,
selectedInside,
childActive,
icon,
handleClick,
popoverShow,
insidePopover
dropdownShow
} = this
return h(NMenuItemContent, {
paddingLeft: delayedPaddingLeft,
@ -108,15 +110,14 @@ export default {
activeIconSize,
title,
extra,
showArrow: !horizontal && !insidePopover,
uncollapsable: insidePopover,
childActive: selectedInside,
showArrow: !horizontal,
childActive: childActive,
icon,
hover: popoverShow,
hover: dropdownShow,
onClick: handleClick
})
}
const createSubmenuChildren = (insidePopover = false) => {
const createSubmenuChildren = () => {
return h(NFadeInExpandTransition, null, {
default: () => {
const { tmNodes, collapsed } = this
@ -126,27 +127,31 @@ export default {
{
class: 'n-submenu-children'
},
tmNodes.map((item) => itemRenderer(item, insidePopover))
tmNodes.map((item) => itemRenderer(item))
),
[[vShow, insidePopover || !collapsed]]
[[vShow, !collapsed]]
)
}
})
}
return this.root
? h(
NPopover,
NDropdown,
{
class: 'n-menu-popover',
builtinThemeOverrides: {
fontSizeLarge: '14px',
optionIconSizeLarge: '18px'
},
size: 'large',
trigger: 'hover',
disabled: !this.popoverEnabled,
placement: this.popoverPlacement,
showArrow: false,
padded: false,
'onUpdate:show': this.handlePopoverShowChange
disabled: !this.dropdownEnabled,
placement: this.dropdownPlacement,
'onUpdate:show': this.handlePopoverShowChange,
options: this.rawNodes,
onSelect: this.NMenu.doSelect
},
{
trigger: () =>
default: () =>
h(
'div',
{
@ -154,18 +159,9 @@ export default {
},
[
createSubmenuItem(),
!this.horizontal ? createSubmenuChildren() : null
this.horizontal ? null : createSubmenuChildren()
]
),
default: () => {
return h(
'div',
{
class: 'n-menu'
},
createSubmenuChildren(true)
)
}
}
)
: h(
@ -176,4 +172,4 @@ export default {
[createSubmenuItem(), createSubmenuChildren()]
)
}
}
})

View File

@ -28,10 +28,6 @@ export default {
title: {
type: [String, Function],
default: undefined
},
insidePopover: {
type: Boolean,
default: false
}
},
data () {
@ -53,7 +49,7 @@ export default {
horizontal () {
return this.NMenu.mode === 'horizontal'
},
popoverPlacement () {
dropdownPlacement () {
if (this.horizontal) {
return 'bottom'
}
@ -84,7 +80,6 @@ export default {
return collapsedIconSize === undefined ? iconSize : collapsedIconSize
},
paddingLeft () {
// TODO handle popover
const {
NMenu: { collapsedWidth, indent, rootIndent },
NSubmenu,
@ -92,11 +87,8 @@ export default {
root,
horizontal,
collapsedIconSize,
menuCollapsed,
insidePopover,
level
menuCollapsed
} = this
if (insidePopover && level === 1) return 18
const mergedRootIndent = rootIndent === undefined ? indent : rootIndent
const menuCollapsedPaddingLeft =
collapsedWidth / 2 - collapsedIconSize / 2

View File

@ -22,268 +22,258 @@ import fadeInHeightExpandTransition from '../../../_styles/transitions/fade-in-h
// --item-text-color-child-active
// --item-extra-text-color-child-active
// --item-icon-color-child-active
export default c([
cB('menu-popover', {
minWidth: '180px'
}),
cB('menu', {
color: 'var(--item-text-color)',
overflow: 'hidden',
transition: 'background-color .3s var(--bezier)',
width: '100%',
boxSizing: 'border-box',
fontSize: 'var(--font-size)',
paddingBottom: '6px'
}, [
cM('transition-disabled', [
cB('menu-item-content', [
cE('icon', {
transition: 'none !important'
})
]),
cB('menu-item-content-header', {
export default cB('menu', {
color: 'var(--item-text-color)',
overflow: 'hidden',
transition: 'background-color .3s var(--bezier)',
width: '100%',
boxSizing: 'border-box',
fontSize: 'var(--font-size)',
paddingBottom: '6px'
}, [
cM('transition-disabled', [
cB('menu-item-content', [
cE('icon', {
transition: 'none !important'
}, [
cE('extra', {
transition: 'none !important'
})
])
})
]),
cM('horizontal', {
display: 'flex',
paddingBottom: 0
cB('menu-item-content-header', {
transition: 'none !important'
}, [
cB('submenu', {
margin: 0
}),
cB('menu-item', {
margin: 0
}, [
c('&::after', {
backgroundColor: 'transparent !important'
}),
cM('selected', [
cB('menu-item-content', {
borderBottom: '2px solid var(--border-color-horizontal)'
})
])
]),
cB('menu-item-content', {
padding: '0 20px',
borderBottom: '2px solid transparent'
}, [
cM('child-selected', {
borderBottom: '2px solid var(--border-color-horizontal)'
}),
cNotM('disabled', [
hoverStyle({
borderBottom: '2px solid var(--border-color-horizontal)'
})
])
])
]),
cM('collapsed', [
cB('menu-item', [
c('&::after', {
backgroundColor: 'transparent !important'
})
]),
cB('menu-item-content', [
cB('menu-item-content-header', {
opacity: 0
}),
cE('arrow', {
opacity: 0
}),
cE('icon', {
color: 'var(--item-icon-color-collapsed)'
})
])
]),
cE('extra', {
transition: 'none !important'
})
])
]),
cM('horizontal', {
display: 'flex',
paddingBottom: 0
}, [
cB('submenu', {
margin: 0
}),
cB('menu-item', {
transition: 'background-color .3s var(--bezier)',
height: '42px',
marginTop: '6px',
position: 'relative'
margin: 0
}, [
c('&::after', `
content: "";
background-color: transparent;
position: absolute;
left: 8px;
right: 8px;
top: 0;
bottom: 0;
pointer-events: none;
border-radius: var(--border-radius);
transition: background-color .3s var(--bezier);
`),
cNotM('disabled', [
c('&:active::after', {
backgroundColor: 'var(--item-color-active)'
})
]),
c('&::after', {
backgroundColor: 'transparent !important'
}),
cM('selected', [
c('&::after', {
backgroundColor: 'var(--item-color-active)'
}),
cB('menu-item-content', [
cE('icon', {
color: 'var(--item-icon-color-active)'
}),
cB('menu-item-content-header', {
color: 'var(--item-text-color-active)'
}, [
cE('extra', {
color: 'var(--item-extra-text-color-active)'
})
])
])
cB('menu-item-content', {
borderBottom: '2px solid var(--border-color-horizontal)'
})
])
]),
cB('menu-item-content', `
box-sizing: border-box;
line-height: 1.75;
height: 100%;
display: flex;
align-items: center;
cursor: pointer;
position: relative;
z-index: auto;
padding-right: 12px;
transition:
background-color .3s var(--bezier),
padding-left .3s var(--bezier),
border-color .3s var(--bezier);
`, [
cM('disabled', {
opacity: '.45',
cursor: 'not-allowed'
cB('menu-item-content', {
padding: '0 20px',
borderBottom: '2px solid transparent'
}, [
cM('child-active', {
borderBottom: '2px solid var(--border-color-horizontal)'
}),
cM('collapsed', [
cE('arrow', {
transition: `
transform 0.2s var(--bezier),
opacity 0.2s var(--bezier),
border-color 0.3s var(--bezier)
`,
transform: 'rotate(225deg)'
cNotM('disabled', [
hoverStyle({
borderBottom: '2px solid var(--border-color-horizontal)'
})
]),
cM('uncollapsable', {
cursor: 'default'
])
])
]),
cM('collapsed', [
cB('menu-item', [
c('&::after', {
backgroundColor: 'transparent !important'
})
]),
cB('menu-item-content', [
cB('menu-item-content-header', {
opacity: 0
}),
cM('child-selected', [
cE('arrow', {
opacity: 0
}),
cE('icon', {
color: 'var(--item-icon-color-collapsed)'
})
])
]),
cB('menu-item', {
transition: 'background-color .3s var(--bezier)',
height: '42px',
marginTop: '6px',
position: 'relative'
}, [
c('&::after', `
content: "";
background-color: transparent;
position: absolute;
left: 8px;
right: 8px;
top: 0;
bottom: 0;
pointer-events: none;
border-radius: var(--border-radius);
transition: background-color .3s var(--bezier);
`),
cNotM('disabled', [
c('&:active::after', {
backgroundColor: 'var(--item-color-active)'
})
]),
cM('selected', [
c('&::after', {
backgroundColor: 'var(--item-color-active)'
}),
cB('menu-item-content', [
cE('icon', {
color: 'var(--item-icon-color-active)'
}),
cB('menu-item-content-header', {
color: 'var(--item-text-color-child-active)'
color: 'var(--item-text-color-active)'
}, [
cE('extra', {
color: 'var(--item-extra-text-color-child-active)'
color: 'var(--item-extra-text-color-active)'
})
]),
cE('icon', {
color: 'var(--item-icon-color-child-active)'
})
]),
cNotM('disabled', [
cNotM('uncollapsable', [
hoverStyle(null, [
cE('icon', {
color: 'var(--item-icon-color-hover)'
}),
cB('menu-item-content-header', {
color: 'var(--item-text-color-hover)'
}, [
cE('extra', {
color: 'var(--item-extra-text-color-hover)'
})
])
])
])
]),
cE('icon', `
color: var(--item-icon-color);
transition:
color .3s var(--bezier),
font-size .3s var(--bezier),
padding-right .3s var(--bezier);
box-sizing: content-box;
flex-shrink: 0;
padding-right: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
`),
cE('arrow', `
content: '';
height: 6px;
width: 6px;
position: absolute;
right: 24px;
top: calc(50% - 2px);
transform: rotate(45deg);
transform-origin: 25% 25%;
opacity: 1;
border-left: 2px solid var(--submenu-arrow-color);
border-top: 2px solid var(--submenu-arrow-color);
transition:
])
])
]),
cB('menu-item-content', `
box-sizing: border-box;
line-height: 1.75;
height: 100%;
display: flex;
align-items: center;
cursor: pointer;
position: relative;
z-index: auto;
padding-right: 12px;
transition:
background-color .3s var(--bezier),
padding-left .3s var(--bezier),
border-color .3s var(--bezier);
`, [
cM('disabled', {
opacity: '.45',
cursor: 'not-allowed'
}),
cM('collapsed', [
cE('arrow', {
transition: `
transform 0.2s var(--bezier),
opacity 0.2s var(--bezier) .1s,
border-color 0.3s var(--bezier);
`),
cB('menu-item-content-header', `
transition:
color .3s var(--bezier),
opacity .3s var(--bezier);
opacity: 1;
flex-grow: 1;
flex-shrink: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--item-text-color);
`, [
cE('extra', `
white-space: nowrap;
margin-left: 6px;
display: inline-block;
transition: color 0.3s var(--bezier);
font-size: 13px;
color: var(--item-extra-text-color);
`)
])
opacity 0.2s var(--bezier),
border-color 0.3s var(--bezier)
`,
transform: 'rotate(225deg)'
})
]),
cB('submenu', {
cursor: 'pointer',
position: 'relative',
marginTop: '6px'
}, [
cB('menu-item-content', {
height: '42px'
}),
cB('submenu-children', {
overflow: 'hidden',
padding: 0
cM('child-active', [
cB('menu-item-content-header', {
color: 'var(--item-text-color-child-active)'
}, [
fadeInHeightExpandTransition({
duration: '.2s'
cE('extra', {
color: 'var(--item-extra-text-color-child-active)'
})
]),
cE('icon', {
color: 'var(--item-icon-color-child-active)'
})
]),
cNotM('disabled', [
hoverStyle(null, [
cE('icon', {
color: 'var(--item-icon-color-hover)'
}),
cB('menu-item-content-header', {
color: 'var(--item-text-color-hover)'
}, [
cE('extra', {
color: 'var(--item-extra-text-color-hover)'
})
])
])
]),
cB('menu-item-group', [
cB('menu-item-group-title', `
margin-top: 6px;
color: var(--group-text-color);
cursor: default;
cE('icon', `
color: var(--item-icon-color);
transition:
color .3s var(--bezier),
font-size .3s var(--bezier),
padding-right .3s var(--bezier);
box-sizing: content-box;
flex-shrink: 0;
padding-right: 8px;
display: inline-flex;
align-items: center;
justify-content: center;
`),
cE('arrow', `
content: '';
height: 6px;
width: 6px;
position: absolute;
right: 24px;
top: calc(50% - 2px);
transform: rotate(45deg);
transform-origin: 25% 25%;
opacity: 1;
border-left: 2px solid var(--arrow-color);
border-top: 2px solid var(--arrow-color);
transition:
transform 0.2s var(--bezier),
opacity 0.2s var(--bezier) .1s,
border-color 0.3s var(--bezier);
`),
cB('menu-item-content-header', `
transition:
color .3s var(--bezier),
opacity .3s var(--bezier);
opacity: 1;
flex-grow: 1;
flex-shrink: 1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: var(--item-text-color);
`, [
cE('extra', `
white-space: nowrap;
margin-left: 6px;
display: inline-block;
transition: color 0.3s var(--bezier);
font-size: 13px;
height: 36px;
display: flex;
align-items: center;
transition:
padding-left .3s var(--bezier),
color .3s var(--bezier);
color: var(--item-extra-text-color);
`)
])
]),
cB('submenu', {
cursor: 'pointer',
position: 'relative',
marginTop: '6px'
}, [
cB('menu-item-content', {
height: '42px'
}),
cB('submenu-children', {
overflow: 'hidden',
padding: 0
}, [
fadeInHeightExpandTransition({
duration: '.2s'
})
])
]),
cB('menu-item-group', [
cB('menu-item-group-title', `
margin-top: 6px;
color: var(--group-text-color);
cursor: default;
font-size: 13px;
height: 36px;
display: flex;
align-items: center;
transition:
padding-left .3s var(--bezier),
color .3s var(--bezier);
`)
])
])

View File

@ -12,10 +12,9 @@ const menuItemGroupProps = Object.keys(NMenuItemGroup.props).concat(
menuChildProps
)
export function itemRenderer (tmNode, insidePopover = false) {
export function itemRenderer (tmNode) {
const { rawNode, key, level } = tmNode
const props = {
insidePopover,
...rawNode,
extra: rawNode.extra ?? rawNode.titleExtra,
key,
@ -33,6 +32,7 @@ export function itemRenderer (tmNode, insidePopover = false) {
return h(
NSubmenu,
keep(props, submenuProps, {
rawNodes: tmNode.rawNode.children,
tmNodes: tmNode.children
})
)