init
This commit is contained in:
59
uni_modules/nutui-uni/components/menuitem/index.scss
Normal file
59
uni_modules/nutui-uni/components/menuitem/index.scss
Normal file
@@ -0,0 +1,59 @@
|
||||
@import '../popup/index';
|
||||
|
||||
.nut-theme-dark {
|
||||
.nut-menu-item__content {
|
||||
.nut-menu-item__option {
|
||||
color: $dark-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nut-menu-item {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: $menu-bar-opened-z-index;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
.active {
|
||||
font-weight: $menu-active-item-font-weight;
|
||||
color: $menu-item-active-text-color !important;
|
||||
}
|
||||
}
|
||||
|
||||
.nut-menu-item__content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
max-height: $menu-item-content-max-height;
|
||||
padding: $menu-item-content-padding;
|
||||
|
||||
&.nut-menu-item__overflow {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.nut-menu-item__option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-top: $menu-item-option-padding-top;
|
||||
padding-bottom: $menu-item-option-padding-bottom;
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
font-size: $font-size-2;
|
||||
color: $title-color;
|
||||
|
||||
.nut-menu-item__span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: $menu-item-option-i-margin-right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nut-menu-item-placeholder-element {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: $menu-bar-opened-z-index;
|
||||
background-color: transparent;
|
||||
}
|
||||
1
uni_modules/nutui-uni/components/menuitem/index.ts
Normal file
1
uni_modules/nutui-uni/components/menuitem/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './menuitem'
|
||||
74
uni_modules/nutui-uni/components/menuitem/menuitem.ts
Normal file
74
uni_modules/nutui-uni/components/menuitem/menuitem.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import type { ExtractPropTypes } from 'vue'
|
||||
import { CHANGE_EVENT, CLOSE_EVENT, OPEN_EVENT, UPDATE_MODEL_EVENT } from '../_constants'
|
||||
import { commonProps, makeArrayProp, makeNumberProp, makeStringProp } from '../_utils'
|
||||
|
||||
export interface MenuItemOption {
|
||||
text: string
|
||||
value: number | string
|
||||
}
|
||||
|
||||
export const menuitemProps = {
|
||||
...commonProps,
|
||||
/**
|
||||
* @@description 菜单项标题
|
||||
*/
|
||||
title: String,
|
||||
/**
|
||||
* @description 选项数组
|
||||
*/
|
||||
options: makeArrayProp<MenuItemOption>([]),
|
||||
/**
|
||||
* @description 是否禁用菜单
|
||||
*/
|
||||
disabled: Boolean,
|
||||
modelValue: [String, Number],
|
||||
/**
|
||||
* @description 可以设置一行展示多少列 `options`
|
||||
*/
|
||||
cols: makeNumberProp(1),
|
||||
/**
|
||||
* @description 选项选中时自定义标题样式类
|
||||
*/
|
||||
activeTitleClass: String,
|
||||
/**
|
||||
* @description 选项非选中时自定义标题样式类
|
||||
*/
|
||||
inactiveTitleClass: String,
|
||||
/**
|
||||
* @description 选项选中时选中图标
|
||||
*/
|
||||
optionIcon: makeStringProp('Check'),
|
||||
}
|
||||
|
||||
export type MenuItemProps = ExtractPropTypes<typeof menuitemProps>
|
||||
|
||||
/* eslint-disable unused-imports/no-unused-vars */
|
||||
export const menuitemEmits = {
|
||||
[UPDATE_MODEL_EVENT]: (value: number | string) => true,
|
||||
[CHANGE_EVENT]: (value: number | string) => true,
|
||||
[OPEN_EVENT]: () => true,
|
||||
[CLOSE_EVENT]: () => true,
|
||||
itemClick: (item: MenuItemOption) => true,
|
||||
}
|
||||
/* eslint-enable unused-imports/no-unused-vars */
|
||||
|
||||
export type MenuitemEmits = typeof menuitemEmits
|
||||
|
||||
export interface MenuItemInst {
|
||||
/**
|
||||
* @description 变更选择项
|
||||
*/
|
||||
change: (value?: number | string) => any
|
||||
/**
|
||||
* @description 切换菜单展示状态,传 `true` 为显示,`false` 为隐藏,不传参为取反
|
||||
*/
|
||||
toggle: (show?: boolean) => boolean
|
||||
/**
|
||||
* @description 打开菜单栏
|
||||
*/
|
||||
open: () => void
|
||||
/**
|
||||
* @description 关闭菜单栏
|
||||
*/
|
||||
close: () => void
|
||||
}
|
||||
196
uni_modules/nutui-uni/components/menuitem/menuitem.vue
Normal file
196
uni_modules/nutui-uni/components/menuitem/menuitem.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<script lang="ts">
|
||||
import type { Ref } from 'vue'
|
||||
import { computed, defineComponent, reactive } from 'vue'
|
||||
import { CLOSE_EVENT, OPEN_EVENT, PREFIX } from '../_constants'
|
||||
import { useInject } from '../_hooks'
|
||||
import { getMainClass, getMainStyle } from '../_utils'
|
||||
import Icon from '../icon/icon.vue'
|
||||
import type { MenuProps } from '../menu'
|
||||
import { MENU_KEY } from '../menu/menu'
|
||||
import PopUp from '../popup/popup.vue'
|
||||
import type { MenuItemOption } from './menuitem'
|
||||
import { menuitemEmits, menuitemProps } from './menuitem'
|
||||
|
||||
const componentName = `${PREFIX}-menu-item`
|
||||
|
||||
export default defineComponent({
|
||||
name: componentName,
|
||||
options: {
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
components: {
|
||||
PopUp,
|
||||
Icon,
|
||||
},
|
||||
props: menuitemProps,
|
||||
emits: menuitemEmits,
|
||||
setup(props, { emit, expose }) {
|
||||
const state = reactive({
|
||||
showPopup: false,
|
||||
showWrapper: false,
|
||||
})
|
||||
|
||||
const { parent } = useInject<{
|
||||
props : MenuProps
|
||||
offset : Ref<number>
|
||||
}>(MENU_KEY)
|
||||
|
||||
const classes = computed(() => {
|
||||
return getMainClass(props, componentName, {
|
||||
'nut-hidden': !state.showWrapper,
|
||||
})
|
||||
})
|
||||
|
||||
const styles = computed(() => {
|
||||
if (parent?.props.offset > 0) {
|
||||
const obj = parent?.props.direction === 'down'
|
||||
? { top: `${parent?.props.offset}px` }
|
||||
: { bottom: `${parent?.offset.value}px` }
|
||||
return getMainStyle(props, obj)
|
||||
} else {
|
||||
const obj = parent?.props.direction === 'down'
|
||||
? { top: `${parent?.offset.value}px` }
|
||||
: { bottom: `${parent?.offset.value}px` }
|
||||
return getMainStyle(props, obj)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
const placeholderElementStyle = computed(() => {
|
||||
const heightStyle = { height: `${parent?.offset.value}px` }
|
||||
|
||||
if (parent?.props.direction === 'down')
|
||||
return { ...heightStyle, top: 0 }
|
||||
|
||||
return { ...heightStyle, top: 'auto' }
|
||||
})
|
||||
|
||||
const open = () => {
|
||||
// TODO 触发更新offset
|
||||
state.showPopup = true
|
||||
state.showWrapper = true
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
state.showPopup = false
|
||||
}
|
||||
|
||||
const toggle = (show = !state.showPopup) => {
|
||||
if (show === state.showPopup)
|
||||
return
|
||||
|
||||
if (show)
|
||||
open()
|
||||
else
|
||||
close()
|
||||
}
|
||||
|
||||
const change = (value : MenuItemOption['value']) => {
|
||||
if (value === props.modelValue)
|
||||
return
|
||||
|
||||
emit('update:modelValue', value)
|
||||
emit('change', value)
|
||||
}
|
||||
|
||||
const renderTitle = () => {
|
||||
if (props.title)
|
||||
return props.title
|
||||
|
||||
const match : any = props.options?.find((option : any) => option.value === props.modelValue)
|
||||
|
||||
return match ? match.text : ''
|
||||
}
|
||||
|
||||
const onClick = (option : MenuItemOption) => {
|
||||
state.showPopup = false
|
||||
|
||||
emit('itemClick', option)
|
||||
|
||||
change(option.value)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
state.showWrapper = false
|
||||
}
|
||||
|
||||
const handleClickOutside = () => {
|
||||
state.showPopup = false
|
||||
}
|
||||
|
||||
const handleVisible = (visible : boolean) => {
|
||||
if (visible)
|
||||
emit(OPEN_EVENT)
|
||||
|
||||
else
|
||||
emit(CLOSE_EVENT)
|
||||
}
|
||||
|
||||
expose({
|
||||
change,
|
||||
open,
|
||||
close,
|
||||
toggle,
|
||||
})
|
||||
|
||||
return {
|
||||
classes,
|
||||
styles,
|
||||
placeholderElementStyle,
|
||||
renderTitle,
|
||||
state,
|
||||
parent,
|
||||
toggle,
|
||||
onClick,
|
||||
handleClose,
|
||||
handleVisible,
|
||||
handleClickOutside,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view :class="classes" :style="styles">
|
||||
<view class="nut-menu-item-placeholder-element"
|
||||
:class="{ 'nut-hidden': !state.showPopup, 'placeholder-element-up': parent?.props.direction === 'up' }"
|
||||
:style="placeholderElementStyle" @click="handleClickOutside" />
|
||||
<PopUp v-bind="$attrs" v-model:visible="state.showPopup" :custom-style="{ position: 'absolute' }"
|
||||
:overlay-style="{ position: 'absolute' }" :position="parent?.props.direction === 'down' ? 'top' : 'bottom'"
|
||||
:duration="parent?.props.duration" pop-class="nut-menu__pop" :destroy-on-close="false"
|
||||
:safe-area-inset-top="false" :overlay="parent?.props.overlay" :lock-scroll="parent?.props.lockScroll"
|
||||
:close-on-click-overlay="parent?.props.closeOnClickOverlay" @closed="handleClose"
|
||||
@open="handleVisible(true)" @close="handleVisible(false)">
|
||||
<scroll-view :scroll-y="true">
|
||||
<view id="nut-menu-item__content" class="nut-menu-item__content">
|
||||
<view v-for="(option, index) in options" :key="index" class="nut-menu-item__option"
|
||||
:class="[{ active: option.value === modelValue }]" :style="{ 'flex-basis': `${100 / cols}%` }"
|
||||
@click="onClick(option)">
|
||||
<view v-if="option.value === modelValue" class="nut-menu-item__span"
|
||||
:class="[option.value === modelValue ? activeTitleClass : inactiveTitleClass]">
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<slot name="icon">
|
||||
<Icon name="Check" :custom-color="parent?.props.activeColor" />
|
||||
</slot>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<Icon :name="optionIcon" :custom-color="parent?.props.activeColor" />
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view :class="[option.value === modelValue ? activeTitleClass : inactiveTitleClass]"
|
||||
:style="{ color: option.value === modelValue ? parent?.props.activeColor : '' }">
|
||||
{{ option.text }}
|
||||
</view>
|
||||
</view>
|
||||
<slot />
|
||||
</view>
|
||||
</scroll-view>
|
||||
</PopUp>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import './index';
|
||||
</style>
|
||||
Reference in New Issue
Block a user