mirror of
https://github.com/tusen-ai/naive-ui.git
synced 2025-02-17 13:20:52 +08:00
refactor(menu): code clean
This commit is contained in:
parent
f212d3f26c
commit
eed89f91d6
@ -22,10 +22,10 @@
|
||||
</n-sub-menu>
|
||||
</n-sub-menu>
|
||||
<n-sub-menu title="subMenu3" name="subMenu3">
|
||||
<n-item-group title="group">
|
||||
<n-menu-handleSelectup title="group">
|
||||
<n-menu-item title="sub1" name="sub6"></n-menu-item>
|
||||
<n-menu-item title="sub1" name="sub7"></n-menu-item>
|
||||
</n-item-group>
|
||||
</n-menu-handleSelectup>
|
||||
</n-sub-menu>
|
||||
</n-menu>
|
||||
</div>
|
||||
|
@ -24,11 +24,26 @@ export default {
|
||||
{
|
||||
title: 'subMenu',
|
||||
name: 'subMenu',
|
||||
groupTitle: 'group1',
|
||||
children: [
|
||||
{
|
||||
title:'sub1',
|
||||
title:'group1',
|
||||
name: 'sub1',
|
||||
group: true,
|
||||
children: [
|
||||
{
|
||||
title: 'subsub001',
|
||||
name: 'subsub001'
|
||||
},
|
||||
{
|
||||
title: 'subsub002',
|
||||
name: 'subsub002'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title:'group2',
|
||||
name: 'sub1',
|
||||
group: true,
|
||||
children: [
|
||||
{
|
||||
title: 'subsub001',
|
||||
|
@ -7,8 +7,7 @@
|
||||
:defaultOpenNames="initOpenKeys"
|
||||
@openNamesChange="changeOpen"
|
||||
@select="changeSelect"
|
||||
|
||||
>
|
||||
>
|
||||
<template v-slot:drawer-header-icon>
|
||||
<div style="overflow:hidden">1111</div>
|
||||
</template>
|
||||
@ -24,21 +23,20 @@
|
||||
</n-sub-menu>
|
||||
</n-sub-menu>
|
||||
<n-sub-menu title="subMenu3" name="subMenu3">
|
||||
<n-item-group title="group">
|
||||
<n-menu-item-group title="group">
|
||||
<n-menu-item title="sub1" name="sub6"></n-menu-item>
|
||||
<n-menu-item title="sub1" name="sub7"></n-menu-item>
|
||||
</n-item-group>
|
||||
</n-menu-item-group>
|
||||
</n-sub-menu>
|
||||
</n-menu>
|
||||
Items:
|
||||
<n-menu
|
||||
v-model="selected"
|
||||
:openNames="opens"
|
||||
:items="items"
|
||||
@select="changeSelect"
|
||||
@openNamesChange="changeOpen"
|
||||
>
|
||||
</n-menu>
|
||||
v-model="selected"
|
||||
:openNames="opens"
|
||||
:items="items"
|
||||
@select="changeSelect"
|
||||
@openNamesChange="changeOpen"
|
||||
/>
|
||||
</div>
|
||||
|
||||
```
|
||||
@ -58,10 +56,10 @@ export default {
|
||||
{
|
||||
title: 'subMenu',
|
||||
name: 'subMenu',
|
||||
groupTitle: 'group1',
|
||||
children: [
|
||||
{
|
||||
title:'sub1',
|
||||
group: true,
|
||||
name: 'sub1',
|
||||
children: [
|
||||
{
|
||||
@ -81,9 +79,11 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
changeOpen (names) {
|
||||
this.opens = names
|
||||
console.log('names', names)
|
||||
},
|
||||
changeSelect (val) {
|
||||
changeSelect (name) {
|
||||
this.selected = name
|
||||
console.log('changeSelect', val)
|
||||
}
|
||||
}
|
||||
|
@ -29,11 +29,11 @@ export default {
|
||||
{
|
||||
title: 'subMenu',
|
||||
name: 'subMenu',
|
||||
groupTitle: 'group1',
|
||||
children: [
|
||||
{
|
||||
title:'sub1',
|
||||
name: 'sub1',
|
||||
group: true,
|
||||
children: [
|
||||
{
|
||||
title: 'subsub001',
|
||||
@ -46,9 +46,9 @@ export default {
|
||||
{
|
||||
title: 'subMenu2',
|
||||
name: 'subMenu2',
|
||||
groupTitle: 'group1',
|
||||
children: [
|
||||
{
|
||||
group: true,
|
||||
title:'sub2',
|
||||
name: 'sub2',
|
||||
children: [
|
||||
@ -63,9 +63,9 @@ export default {
|
||||
{
|
||||
title: 'subMenu3',
|
||||
name: 'subMenu3',
|
||||
groupTitle: 'group1',
|
||||
children: [
|
||||
{
|
||||
group: true,
|
||||
title:'sub3',
|
||||
name: 'sub3',
|
||||
children: [
|
||||
|
@ -3,11 +3,10 @@
|
||||
Menu1:
|
||||
<n-menu
|
||||
v-model="selected"
|
||||
:openNames="initOpenKeys"
|
||||
:openNames="opens"
|
||||
:items="items"
|
||||
@select="changeSelect"
|
||||
>
|
||||
</n-menu>
|
||||
/>
|
||||
Menu2:
|
||||
<n-menu
|
||||
v-model="selected"
|
||||
@ -15,15 +14,13 @@ Menu2:
|
||||
:items="items"
|
||||
@select="changeSelect"
|
||||
@openNamesChange="changeOpen"
|
||||
>
|
||||
</n-menu>
|
||||
/>
|
||||
```
|
||||
```js
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
selected: 'sub1',
|
||||
initOpenKeys: ['subMenu'],
|
||||
opens: ['subMenu'],
|
||||
items: [
|
||||
{
|
||||
@ -33,9 +30,9 @@ export default {
|
||||
{
|
||||
title: 'subMenu',
|
||||
name: 'subMenu',
|
||||
groupTitle: 'group1',
|
||||
children: [
|
||||
{
|
||||
group: true,
|
||||
title:'sub1',
|
||||
name: 'sub1',
|
||||
children: [
|
||||
|
@ -1,14 +1,13 @@
|
||||
import Menu from './src/main.vue'
|
||||
import MenuItem from './src/menuItem.vue'
|
||||
import SubMenu from './src/subMenu.vue'
|
||||
import ItemGroup from './src/itemGroup.vue'
|
||||
import Menu from './src/MenuAdapter.vue'
|
||||
import MenuItem from './src/MenuItem.vue'
|
||||
import SubMenu from './src/SubMenu.vue'
|
||||
import MenuItemGroup from './src/MenuItemGroup.vue'
|
||||
|
||||
Menu.install = function (Vue) {
|
||||
// Menu.Item = MenuItem
|
||||
Vue.component(Menu.name, Menu)
|
||||
Vue.component(MenuItem.name, MenuItem)
|
||||
Vue.component(SubMenu.name, SubMenu)
|
||||
Vue.component(ItemGroup.name, ItemGroup)
|
||||
Vue.component(MenuItemGroup.name, MenuItemGroup)
|
||||
}
|
||||
|
||||
export default Menu
|
||||
|
88
packages/common/Menu/src/Menu.vue
Normal file
88
packages/common/Menu/src/Menu.vue
Normal file
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div
|
||||
class="n-menu"
|
||||
:class="{
|
||||
[`n-${synthesizedTheme}-theme`]: synthesizedTheme,
|
||||
[`n-menu--${mode}`]: mode,
|
||||
}"
|
||||
>
|
||||
<ul class="n-menu-list">
|
||||
<slot />
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import withapp from '../../../mixins/withapp'
|
||||
import themeable from '../../../mixins/themeable'
|
||||
|
||||
export default {
|
||||
name: 'Menu',
|
||||
provide () {
|
||||
return {
|
||||
NMenu: this
|
||||
}
|
||||
},
|
||||
mixins: [withapp, themeable],
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
rootIndent: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
indent: {
|
||||
type: Number,
|
||||
default: 32
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'vertical'
|
||||
},
|
||||
defaultOpenNames: {
|
||||
type: Array,
|
||||
default: undefined
|
||||
},
|
||||
openNames: {
|
||||
type: Array,
|
||||
default: undefined
|
||||
}
|
||||
// hasIcon: {
|
||||
// type: Boolean,
|
||||
// default: false
|
||||
// }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
internalOpenNames: this.openNames || this.defaultOpenNames || []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
synthesizedOpenNames () {
|
||||
if (this.openNames !== undefined) return this.openNames || []
|
||||
else return this.internalOpenNames
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelect (value) {
|
||||
this.$emit('select', value)
|
||||
this.$emit('input', value)
|
||||
},
|
||||
handleOpenNamesChange (name) {
|
||||
const currentOpenNames = Array.from(this.synthesizedOpenNames)
|
||||
const index = currentOpenNames.findIndex(openName => openName === name)
|
||||
if (~index) {
|
||||
currentOpenNames.splice(index, 1)
|
||||
} else {
|
||||
currentOpenNames.push(name)
|
||||
}
|
||||
if (this.openNames === undefined) {
|
||||
this.internalOpenNames = currentOpenNames
|
||||
}
|
||||
this.$emit('openNamesChange', currentOpenNames)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
69
packages/common/Menu/src/MenuAdapter.vue
Normal file
69
packages/common/Menu/src/MenuAdapter.vue
Normal file
@ -0,0 +1,69 @@
|
||||
|
||||
<script>
|
||||
import Menu from './Menu.vue'
|
||||
import MenuItem from './MenuItem.vue'
|
||||
import SubMenu from './SubMenu.vue'
|
||||
import MenuItemGroup from './MenuItemGroup.vue'
|
||||
|
||||
export default {
|
||||
name: 'NMenu',
|
||||
functional: true,
|
||||
render (h, context) {
|
||||
if (context.props.items) {
|
||||
const createItems = items => {
|
||||
return items.map(item => {
|
||||
const props = {
|
||||
title: item.title,
|
||||
name: item.name,
|
||||
disabled: !!item.disabled
|
||||
}
|
||||
if (item.children) {
|
||||
const scopedSlots = {}
|
||||
if (typeof item.title === 'function') {
|
||||
delete props.title
|
||||
scopedSlots.header = item.title
|
||||
}
|
||||
if (item.group) {
|
||||
return h(MenuItemGroup, {
|
||||
props,
|
||||
scopedSlots
|
||||
}, createItems(item.children))
|
||||
} else {
|
||||
return h(SubMenu, {
|
||||
props,
|
||||
scopedSlots
|
||||
}, createItems(item.children))
|
||||
}
|
||||
} else {
|
||||
const scopedSlots = {}
|
||||
if (typeof item.title === 'function') {
|
||||
delete props.title
|
||||
scopedSlots.default = item.title
|
||||
}
|
||||
return h(MenuItem, {
|
||||
props,
|
||||
scopedSlots
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
return h(Menu,
|
||||
{
|
||||
props: context.props,
|
||||
scopedSlots: { ...context.scopedSlots },
|
||||
on: context.listeners,
|
||||
attrs: context.data.attrs
|
||||
},
|
||||
createItems(context.props.items)
|
||||
)
|
||||
} else {
|
||||
return h(Menu, {
|
||||
props: context.props,
|
||||
scopedSlots: { ...context.scopedSlots },
|
||||
on: context.listeners,
|
||||
attrs: context.data.attrs
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
91
packages/common/Menu/src/MenuItem.vue
Normal file
91
packages/common/Menu/src/MenuItem.vue
Normal file
@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<li
|
||||
class="n-menu-item"
|
||||
:style="{ paddingLeft: paddingLeft + 'px' }"
|
||||
:class="{
|
||||
'n-menu-item--selected': isSelected,
|
||||
'n-menu-item--disabled': synthesizedDisabled
|
||||
}"
|
||||
@click="handleClick"
|
||||
>
|
||||
<!-- <span
|
||||
v-if="hasIcon"
|
||||
class="n-menu-title-icon"
|
||||
/> -->
|
||||
<slot>
|
||||
<render :render="title" />
|
||||
</slot>
|
||||
</li>
|
||||
</template>
|
||||
<script>
|
||||
import registerable from '../../../mixins/registerable'
|
||||
import withapp from '../../../mixins/withapp'
|
||||
import themeable from '../../../mixins/themeable'
|
||||
import render from '../../../utils/render'
|
||||
|
||||
export default {
|
||||
name: 'NMenuItem',
|
||||
components: {
|
||||
render
|
||||
},
|
||||
mixins: [
|
||||
registerable('NMenu'),
|
||||
withapp,
|
||||
themeable
|
||||
],
|
||||
inject: {
|
||||
NMenu: {
|
||||
default: null
|
||||
},
|
||||
NSubMenu: {
|
||||
default: null
|
||||
},
|
||||
NMenuItemGroup: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
paddingLeft () {
|
||||
if (this.NMenuItemGroup) {
|
||||
return this.NMenu.indent / 2 + this.NMenuItemGroup.paddingLeft
|
||||
} else if (this.NSubMenu) {
|
||||
return this.NMenu.indent + this.NSubMenu.paddingLeft
|
||||
} else {
|
||||
return this.NMenu.rootIndent || this.NMenu.indent
|
||||
}
|
||||
},
|
||||
synthesizedDisabled () {
|
||||
return ((this.NSubMenu && this.NSubMenu.synthesizedDisabled) || this.disabled)
|
||||
},
|
||||
isSelected () {
|
||||
if (this.NMenu.value === this.name) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick () {
|
||||
if (!this.synthesizedDisabled) {
|
||||
this.NMenu.handleSelect(this.name)
|
||||
this.$emit('click', this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
54
packages/common/Menu/src/MenuItemGroup.vue
Normal file
54
packages/common/Menu/src/MenuItemGroup.vue
Normal file
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<li class="n-menu-item-group">
|
||||
<span class="n-menu-item-group-title" :style="{ paddingLeft: paddingLeft + 'px' }">
|
||||
<slot name="header"><render :render="title" /></slot>
|
||||
</span>
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<script>
|
||||
import render from '../../../utils/render'
|
||||
|
||||
export default {
|
||||
name: 'NMenuItemGroup',
|
||||
components: {
|
||||
render
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: [String, Function],
|
||||
default: null
|
||||
}
|
||||
},
|
||||
provide () {
|
||||
return {
|
||||
NMenuItemGroup: this,
|
||||
NSubMenu: null
|
||||
}
|
||||
},
|
||||
inject: {
|
||||
NMenuItemGroup: {
|
||||
default: null
|
||||
},
|
||||
NMenu: {
|
||||
default: null
|
||||
},
|
||||
NSubMenu: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
paddingLeft () {
|
||||
if (this.NMenuItemGroup) {
|
||||
return this.NMenu.indent / 2 + this.NMenuItemGroup.paddingLeft
|
||||
} else if (this.NSubMenu) {
|
||||
return this.NMenu.indent / 2 + this.NSubMenu.paddingLeft
|
||||
} else {
|
||||
return (this.NMenu.rootIndent || this.NMenu.indent) / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
100
packages/common/Menu/src/SubMenu.vue
Normal file
100
packages/common/Menu/src/SubMenu.vue
Normal file
@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<li
|
||||
class="n-sub-menu"
|
||||
>
|
||||
<div
|
||||
class="n-sub-menu-header"
|
||||
:style="{paddingLeft: paddingLeft + 'px'}"
|
||||
:class="{
|
||||
'n-sub-menu-header--collapsed': isCollapsed,
|
||||
'n-sub-menu-header--active': !isCollapsed,
|
||||
'n-sub-menu-header--disabled': disabled,
|
||||
}"
|
||||
@click="handleClick"
|
||||
>
|
||||
<!-- <span
|
||||
v-if="hasIcon"
|
||||
class="n-menu-title-icon"
|
||||
/> -->
|
||||
<slot name="header">
|
||||
<render :render="title" />
|
||||
</slot>
|
||||
</div>
|
||||
<fade-in-height-expand-transition>
|
||||
<ul
|
||||
v-if="!isCollapsed"
|
||||
class="n-sub-menu-content"
|
||||
>
|
||||
<slot />
|
||||
</ul>
|
||||
</fade-in-height-expand-transition>
|
||||
</li>
|
||||
</template>
|
||||
<script>
|
||||
import FadeInHeightExpandTransition from '../../../transition/FadeInHeightExpandTransition'
|
||||
import render from '../../../utils/render'
|
||||
|
||||
export default {
|
||||
name: 'NSubMenu',
|
||||
provide () {
|
||||
return {
|
||||
NSubMenu: this,
|
||||
NMenuItemGroup: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
FadeInHeightExpandTransition,
|
||||
render
|
||||
},
|
||||
inject: {
|
||||
NMenu: {
|
||||
default: null
|
||||
},
|
||||
NSubMenu: {
|
||||
default: null
|
||||
},
|
||||
NMenuItemGroup: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
synthesizedDisabled () {
|
||||
return (this.NMenu && this.NMenu.disabled) || this.disabled
|
||||
},
|
||||
paddingLeft () {
|
||||
if (this.NMenuItemGroup) {
|
||||
return this.NMenu.indent / 2 + this.NMenuItemGroup.paddingLeft
|
||||
} else if (this.NSubMenu) {
|
||||
return this.NMenu.indent + this.NSubMenu.paddingLeft
|
||||
} else {
|
||||
return this.NMenu.rootIndent || this.NMenu.indent
|
||||
}
|
||||
},
|
||||
isCollapsed () {
|
||||
return !(this.NMenu.synthesizedOpenNames.includes(this.name))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick () {
|
||||
if (!this.disabled) {
|
||||
this.NMenu.handleOpenNamesChange(this.name)
|
||||
this.$emit('click', this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,38 +0,0 @@
|
||||
<template>
|
||||
<li class="n-menu-item-group">
|
||||
<span class="n-menu-item-group-title" :style="{paddingLeft: paddingLeft + 'px'}">
|
||||
{{ title }}
|
||||
</span>
|
||||
<div>
|
||||
<slot />
|
||||
</div>
|
||||
</li>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'NItemGroup',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
inject: {
|
||||
NMenu: {
|
||||
default: null
|
||||
},
|
||||
NSubMenu: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
paddingLeft () {
|
||||
let padding = this.NMenu.indent / 2
|
||||
if (this.NSubMenu) {
|
||||
padding = padding + this.NSubMenu.paddingLeft
|
||||
}
|
||||
return padding
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -1,65 +0,0 @@
|
||||
|
||||
<script>
|
||||
import Menu from './menu.vue'
|
||||
import MenuItem from './menuItem.vue'
|
||||
import SubMenu from './subMenu.vue'
|
||||
import ItemGroup from './itemGroup.vue'
|
||||
|
||||
export default {
|
||||
name: 'NMenu',
|
||||
functional: true,
|
||||
render (h, context) {
|
||||
if (context.props.items && !(context.$slots && context.$slots.default && context.$slots.default.length)) {
|
||||
let test = function (list) {
|
||||
return list.map(function (item, index) {
|
||||
let props = {
|
||||
title: item.title,
|
||||
name: item.name,
|
||||
disabled: !!item.disabled
|
||||
}
|
||||
if (item.children) {
|
||||
if (item.groupTitle) {
|
||||
let groupProps = {
|
||||
title: item.groupTitle
|
||||
}
|
||||
return h(SubMenu, {
|
||||
props: props
|
||||
},
|
||||
[
|
||||
h(ItemGroup, {
|
||||
props: groupProps
|
||||
}, test(item.children))
|
||||
]
|
||||
)
|
||||
} else {
|
||||
return h(SubMenu, {
|
||||
props: props
|
||||
},
|
||||
test(item.children)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
return h(MenuItem, {
|
||||
props: props
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
return h(Menu,
|
||||
{
|
||||
props: context.props,
|
||||
scopedSlots: context.scopedSlots,
|
||||
on: context.listeners
|
||||
},
|
||||
test(context.props.items)
|
||||
)
|
||||
} else {
|
||||
return h(Menu, {
|
||||
props: context.props,
|
||||
scopedSlots: context.scopedSlots,
|
||||
on: context.listeners
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -3,30 +3,12 @@
|
||||
class="n-menu"
|
||||
:class="{
|
||||
[`n-${synthesizedTheme}-theme`]: synthesizedTheme,
|
||||
'n-menu--collapsed': isCollapsed,
|
||||
'n-menu--active': !isCollapsed,
|
||||
[`n-menu--${mode}`]: mode,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="n-menu-container"
|
||||
:class="{
|
||||
'n-menu-container--collapsed': isCollapsed,
|
||||
'n-menu-container--active': !isCollapsed,
|
||||
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="n-menu-content"
|
||||
:class="{
|
||||
[`n-menu-content--${mode}`]: mode,
|
||||
}"
|
||||
>
|
||||
<ul class="n-menu-list">
|
||||
<slot />
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="n-menu-list">
|
||||
<slot />
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -47,6 +29,10 @@ export default {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
rootIndent: {
|
||||
type: Number,
|
||||
default: null
|
||||
},
|
||||
indent: {
|
||||
type: Number,
|
||||
default: 32
|
||||
@ -57,56 +43,45 @@ export default {
|
||||
},
|
||||
defaultOpenNames: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return undefined
|
||||
}
|
||||
default: undefined
|
||||
},
|
||||
openNames: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return undefined
|
||||
}
|
||||
},
|
||||
hasIcon: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: undefined
|
||||
}
|
||||
|
||||
// hasIcon: {
|
||||
// type: Boolean,
|
||||
// default: false
|
||||
// }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
componentName: 'NMenu',
|
||||
isCollapsed: false,
|
||||
currentOpenNames: this.openNames || this.defaultOpenNames || []
|
||||
internalOpenNames: this.openNames || this.defaultOpenNames || []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
openNames (val) {
|
||||
this.currentOpenNames = val
|
||||
},
|
||||
defaultOpenNames (val) {
|
||||
this.currentOpenNames = val
|
||||
computed: {
|
||||
synthesizedOpenNames () {
|
||||
if (this.openNames !== undefined) return this.openNames || []
|
||||
else return this.internalOpenNames
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggle () {
|
||||
this.isCollapsed = !this.isCollapsed
|
||||
},
|
||||
changeSelect (value) {
|
||||
handleSelect (value) {
|
||||
this.$emit('select', value)
|
||||
this.$emit('input', value)
|
||||
},
|
||||
openKeysChangeCallback (val) {
|
||||
let indexs = [...this.currentOpenNames]
|
||||
if (indexs.includes(val)) {
|
||||
indexs.splice(indexs.findIndex(item => item === val), 1)
|
||||
handleOpenNamesChange (name) {
|
||||
const currentOpenNames = Array.from(this.synthesizedOpenNames)
|
||||
const index = currentOpenNames.findIndex(openName => openName === name)
|
||||
if (~index) {
|
||||
currentOpenNames.splice(index, 1)
|
||||
} else {
|
||||
indexs.push(val)
|
||||
currentOpenNames.push(name)
|
||||
}
|
||||
if (typeof (this.openNames) === 'undefined') {
|
||||
this.currentOpenNames = indexs
|
||||
if (this.openNames === undefined) {
|
||||
this.internalOpenNames = currentOpenNames
|
||||
}
|
||||
this.$emit('openNamesChange', indexs)
|
||||
this.$emit('openNamesChange', currentOpenNames)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,33 @@
|
||||
<template>
|
||||
<li
|
||||
class="n-menu-item"
|
||||
:style="{paddingLeft: paddingLeft + 'px'}"
|
||||
:style="{ paddingLeft: paddingLeft + 'px' }"
|
||||
:class="{
|
||||
'n-menu-item--selected': isSelected,
|
||||
'n-menu-item--disabled': isDisabled
|
||||
'n-menu-item--disabled': synthesizedDisabled
|
||||
}"
|
||||
@click="handleClick"
|
||||
>
|
||||
<span
|
||||
<!-- <span
|
||||
v-if="hasIcon"
|
||||
class="n-menu-title-icon"
|
||||
/>
|
||||
<span>{{ title }}</span>
|
||||
/> -->
|
||||
<slot>
|
||||
<render :render="title" />
|
||||
</slot>
|
||||
</li>
|
||||
</template>
|
||||
<script>
|
||||
import registerable from '../../../mixins/registerable'
|
||||
import withapp from '../../../mixins/withapp'
|
||||
import themeable from '../../../mixins/themeable'
|
||||
import render from '../../../utils/render'
|
||||
|
||||
export default {
|
||||
name: 'NMenuItem',
|
||||
componentName: 'NMenuItem',
|
||||
components: {
|
||||
render
|
||||
},
|
||||
mixins: [
|
||||
registerable('NMenu'),
|
||||
withapp,
|
||||
@ -34,11 +39,14 @@ export default {
|
||||
},
|
||||
NSubMenu: {
|
||||
default: null
|
||||
},
|
||||
NMenuItemGroup: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
name: {
|
||||
@ -51,18 +59,17 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasIcon () {
|
||||
return this.NMenu.hasIcon && this.$parent.componentName === 'NMenu'
|
||||
},
|
||||
paddingLeft () {
|
||||
let padding = this.NMenu.indent
|
||||
if (this.NSubMenu) {
|
||||
padding = padding + this.NSubMenu.paddingLeft
|
||||
if (this.NMenuItemGroup) {
|
||||
return this.NMenu.indent / 2 + this.NMenuItemGroup.paddingLeft
|
||||
} else if (this.NSubMenu) {
|
||||
return this.NMenu.indent + this.NSubMenu.paddingLeft
|
||||
} else {
|
||||
return this.NMenu.rootIndent || this.NMenu.indent
|
||||
}
|
||||
return padding
|
||||
},
|
||||
isDisabled () {
|
||||
return ((this.NSubMenu && this.NSubMenu.disabled) || this.disabled)
|
||||
synthesizedDisabled () {
|
||||
return ((this.NSubMenu && this.NSubMenu.synthesizedDisabled) || this.disabled)
|
||||
},
|
||||
isSelected () {
|
||||
if (this.NMenu.value === this.name) {
|
||||
@ -74,8 +81,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleClick () {
|
||||
if (!this.isDisabled) {
|
||||
this.NMenu.changeSelect(this.name)
|
||||
if (!this.synthesizedDisabled) {
|
||||
this.NMenu.handleSelect(this.name)
|
||||
this.$emit('click', this)
|
||||
}
|
||||
}
|
||||
|
@ -8,16 +8,17 @@
|
||||
:class="{
|
||||
'n-sub-menu-header--collapsed': isCollapsed,
|
||||
'n-sub-menu-header--active': !isCollapsed,
|
||||
'n-sub-menu-header--has-icon': hasIcon,
|
||||
'n-sub-menu-header--disabled': disabled,
|
||||
}"
|
||||
@click="handleClick"
|
||||
>
|
||||
<span
|
||||
<!-- <span
|
||||
v-if="hasIcon"
|
||||
class="n-menu-title-icon"
|
||||
/>
|
||||
<span>{{ title }}</span>
|
||||
/> -->
|
||||
<slot name="header">
|
||||
<render :render="title" />
|
||||
</slot>
|
||||
</div>
|
||||
<fade-in-height-expand-transition>
|
||||
<ul
|
||||
@ -31,16 +32,19 @@
|
||||
</template>
|
||||
<script>
|
||||
import FadeInHeightExpandTransition from '../../../transition/FadeInHeightExpandTransition'
|
||||
import render from '../../../utils/render'
|
||||
|
||||
export default {
|
||||
name: 'NSubMenu',
|
||||
provide () {
|
||||
return {
|
||||
NSubMenu: this
|
||||
NSubMenu: this,
|
||||
NMenuItemGroup: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
FadeInHeightExpandTransition
|
||||
FadeInHeightExpandTransition,
|
||||
render
|
||||
},
|
||||
inject: {
|
||||
NMenu: {
|
||||
@ -48,11 +52,14 @@ export default {
|
||||
},
|
||||
NSubMenu: {
|
||||
default: null
|
||||
},
|
||||
NMenuItemGroup: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
type: [String, Function],
|
||||
default: null
|
||||
},
|
||||
name: {
|
||||
@ -65,24 +72,26 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasIcon () {
|
||||
return this.NMenu.haIcon && this.$parent.$options.name === 'NMenu'
|
||||
synthesizedDisabled () {
|
||||
return (this.NMenu && this.NMenu.disabled) || this.disabled
|
||||
},
|
||||
paddingLeft () {
|
||||
let padding = this.NMenu.indent
|
||||
if (this.NSubMenu) {
|
||||
padding = padding + this.NSubMenu.paddingLeft
|
||||
if (this.NMenuItemGroup) {
|
||||
return this.NMenu.indent / 2 + this.NMenuItemGroup.paddingLeft
|
||||
} else if (this.NSubMenu) {
|
||||
return this.NMenu.indent + this.NSubMenu.paddingLeft
|
||||
} else {
|
||||
return this.NMenu.rootIndent || this.NMenu.indent
|
||||
}
|
||||
return padding
|
||||
},
|
||||
isCollapsed () {
|
||||
return !(this.NMenu.currentOpenNames.includes(this.name))
|
||||
return !(this.NMenu.synthesizedOpenNames.includes(this.name))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick () {
|
||||
if (!this.disabled) {
|
||||
this.NMenu.openKeysChangeCallback(this.name)
|
||||
this.NMenu.handleOpenNamesChange(this.name)
|
||||
this.$emit('click', this)
|
||||
}
|
||||
}
|
||||
|
163
styles/Menu.scss
163
styles/Menu.scss
@ -2,110 +2,75 @@
|
||||
@import './mixins/mixins.scss';
|
||||
@import './themes/vars.scss';
|
||||
|
||||
$layout-nav-height: 64px;
|
||||
|
||||
@include themes-mixin() {
|
||||
@include b(menu) {
|
||||
width: 100%;
|
||||
transition:width .3s;
|
||||
@include b(menu-container) {
|
||||
transition: transform .3s $--n-ease-in-out-cubic-bezier, opacity .3s $--n-ease-in-out-cubic-bezier, background-color .3s $--n-ease-in-out-cubic-bezier, border-color .3s $--n-ease-in-out-cubic-bezier;
|
||||
@include b(menu-divider) {
|
||||
margin: 0px 25px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .08);
|
||||
}
|
||||
@include b(menu-content) {
|
||||
overflow: hidden;
|
||||
transition: opacity .3s $--n-ease-in-out-cubic-bezier;
|
||||
|
||||
@include b(menu-list) {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@include b(menu-item) {
|
||||
cursor: pointer;
|
||||
padding-left: 24px;
|
||||
position: relative;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
// padding-left: 48px;
|
||||
font-size: 14px;
|
||||
list-style: none;
|
||||
@include e(icon) {
|
||||
&::before {
|
||||
content: '';
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
background-image:linear-gradient(47deg,rgba(120, 205, 104, 1) 0%,rgba(20, 166, 165, 1) 100%);
|
||||
top: 20px;
|
||||
left: 30px;
|
||||
-webkit-clip-path: polygon(100% 0, 100% 100%, 0% 100%);
|
||||
clip-path: polygon(100% 0, 100% 100%, 0% 100%);
|
||||
}
|
||||
}
|
||||
// @include m(selected) {
|
||||
// background-image: $menu-item-selected-background-image;
|
||||
// }
|
||||
&::before { // item background
|
||||
content: "";
|
||||
background-size: 300%;
|
||||
background-image: $menu-item-selected-background-image;
|
||||
background-position: $--menu-item-background-position;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 0;
|
||||
transition: opacity 0.3s $--n-ease-in-out-cubic-bezier, background-position .3s $--n-ease-in-out-cubic-bezier;
|
||||
opacity: 0;
|
||||
}
|
||||
@include m(selected) {
|
||||
&::before {
|
||||
opacity: .9;
|
||||
}
|
||||
}
|
||||
@include m(disabled) {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
@include b(menu-list) {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@include b(menu-item) {
|
||||
cursor: pointer;
|
||||
color: $--menu-item-text-color;
|
||||
transition: color .3s $--n-ease-in-out-cubic-bezier;
|
||||
position: relative;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
list-style: none;
|
||||
line-height: 1.5;
|
||||
@include e(icon) {
|
||||
&::before {
|
||||
content: '';
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
position: absolute;
|
||||
background-image:linear-gradient(47deg,rgba(120, 205, 104, 1) 0%,rgba(20, 166, 165, 1) 100%);
|
||||
top: 20px;
|
||||
left: 30px;
|
||||
clip-path: polygon(100% 0, 100% 100%, 0% 100%);
|
||||
}
|
||||
}
|
||||
@include m(horizontal) {
|
||||
@include b(menu-header) {
|
||||
display: none;
|
||||
}
|
||||
@include b(menu-list) {
|
||||
> * {
|
||||
float: left;
|
||||
}
|
||||
&::before {
|
||||
content: "";
|
||||
background-size: 300%;
|
||||
background-image: $--menu-item-background-image;
|
||||
background-position: $--menu-item-background-position;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 0;
|
||||
transition: opacity 0.3s $--n-ease-in-out-cubic-bezier, background-position .3s $--n-ease-in-out-cubic-bezier;
|
||||
opacity: 0;
|
||||
}
|
||||
@include m(selected) {
|
||||
&::before {
|
||||
opacity: .9;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@include b(menu-title-icon) {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 10px;
|
||||
&::before {
|
||||
content: '';
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
background-image: linear-gradient(47deg, #78cd68 0%, #14a6a5 100%);
|
||||
display: inline-block;
|
||||
-webkit-clip-path: polygon(100% 0, 100% 100%, 0% 100%);
|
||||
clip-path: polygon(100% 0, 100% 100%, 0% 100%);
|
||||
@include m(disabled) {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
}
|
||||
@include b(sub-menu) {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
@include b(sub-menu-header) {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: $--menu-sub-menu-text-color;
|
||||
transition: color .3s $--n-ease-in-out-cubic-bezier;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
padding: 16px 0;
|
||||
&::after { // down arrow
|
||||
content: '';
|
||||
height: 6px;
|
||||
@ -113,15 +78,15 @@ $layout-nav-height: 64px;
|
||||
border-left: 2px solid $--n-primary-color;
|
||||
border-top: 2px solid $--n-primary-color;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
right: 24px;
|
||||
top: calc(50% - 3px);
|
||||
transform: rotate(45deg) ;
|
||||
transform-origin: 25% 25%;
|
||||
transition: transform 0.3s $--n-ease-in-out-cubic-bezier, opacity 0.3s $--n-ease-in-out-cubic-bezier, border-color 0.3s $--n-ease-in-out-cubic-bezier;
|
||||
transition: transform 0.2s $--n-ease-in-out-cubic-bezier, opacity 0.3s $--n-ease-in-out-cubic-bezier, border-color 0.3s $--n-ease-in-out-cubic-bezier;
|
||||
}
|
||||
@include m(collapsed) {
|
||||
&::after {
|
||||
transform: rotate(225deg) ;
|
||||
transform: rotate(225deg);
|
||||
}
|
||||
}
|
||||
@include m(disabled) {
|
||||
@ -131,15 +96,17 @@ $layout-nav-height: 64px;
|
||||
}
|
||||
@include b(sub-menu-content) {
|
||||
padding: 0;
|
||||
@include fade-in-height-expand-transition();
|
||||
@include fade-in-height-expand-transition($duration: .2s);
|
||||
}
|
||||
}
|
||||
@include b(menu-item-group) {
|
||||
@include b(menu-item-group-title) {
|
||||
display: block;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
color: #999;
|
||||
cursor: default;
|
||||
height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: $--menu-item-group-text-color;
|
||||
transition: color .3s $--n-ease-in-out-cubic-bezier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
@mixin setup-dark-menu {
|
||||
$menu-background-color: $--n-card-color !global;
|
||||
$menu-item-selected-background-image: linear-gradient(90deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .03) 40%, rgba(0, 0, 0, .09) 60%, rgba(0, 0, 0, .09) 100%) !global;
|
||||
$--menu-item-background-image: linear-gradient(90deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .03) 40%, rgba(0, 0, 0, .09) 60%, rgba(0, 0, 0, .09) 100%) !global;
|
||||
$--menu-item-background-position: 0% !global;
|
||||
$--menu-item-group-text-color: $--n-meta-text-color !global;
|
||||
$--menu-item-text-color: $--n-secondary-text-color !global;
|
||||
$--menu-sub-menu-text-color: $--n-text-color !global;
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
@mixin setup-light-menu {
|
||||
$menu-background-color: $--neutral-10 !global;
|
||||
$menu-item-selected-background-image: linear-gradient(90deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .03) 40%, rgba(0, 0, 0, .09) 60%, rgba(0, 0, 0, .09) 100%) !global;
|
||||
$--menu-item-background-image: linear-gradient(90deg, rgba(255, 255, 255, .3) 0%, rgba(255, 255, 255, .03) 40%, rgba(0, 0, 0, .09) 60%, rgba(0, 0, 0, .09) 100%) !global;
|
||||
$--menu-item-background-position: 100% !global;
|
||||
$--menu-item-group-text-color: $--n-meta-text-color !global;
|
||||
$--menu-item-text-color: $--n-secondary-text-color !global;
|
||||
$--menu-sub-menu-text-color: $--n-text-color !global;
|
||||
}
|
Loading…
Reference in New Issue
Block a user