| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- <template>
- <view :class="classes">
- <view class="nut-menu__bar" :class="{ opened: opened }" ref="barRef">
- <template v-for="(item, index) in children" :key="index">
- <view
- class="nut-menu__item"
- @click="!item.disabled && toggleItem(index)"
- :class="{ disabled: item.disabled, active: item.state.showPopup }"
- :style="{ color: item.state.showPopup ? activeColor : '' }"
- >
- <view class="nut-menu__title" :class="getClasses(item.state.showPopup)">
- <view class="nut-menu__title-text">{{ item.renderTitle() }}</view>
- <nut-icon
- v-bind="$attrs"
- :name="titleIcon || (direction === 'up' ? 'arrow-up' : 'down-arrow')"
- size="10"
- class="nut-menu__title-icon"
- ></nut-icon>
- </view>
- </view>
- </template>
- </view>
- <slot></slot>
- </view>
- </template>
- <script lang="ts">
- import { reactive, provide, computed, ref, onMounted, onUnmounted } from 'vue';
- import { createComponent } from '@/packages/utils/create';
- import { useRect } from '@/packages/utils/useRect';
- const { componentName, create } = createComponent('menu');
- export default create({
- props: {
- activeColor: {
- type: String,
- default: ''
- },
- overlay: {
- type: Boolean,
- default: true as const
- },
- lockScroll: {
- type: Boolean,
- default: true as const
- },
- duration: {
- type: [Number, String],
- default: 0
- },
- titleIcon: String,
- closeOnClickOverlay: {
- type: Boolean,
- default: true
- },
- direction: {
- type: String,
- default: 'down'
- },
- scrollFixed: {
- type: [Boolean, String, Number],
- default: false
- },
- titleClass: [String]
- },
- setup(props, { emit, slots }) {
- const barRef = ref<HTMLElement>();
- const offset = ref(0);
- const isScrollFixed = ref(false);
- const useChildren = () => {
- const publicChildren: any[] = reactive([]);
- const internalChildren: any[] = reactive([]);
- const linkChildren = (value?: any) => {
- const link = (child: any) => {
- if (child.proxy) {
- internalChildren.push(child);
- publicChildren.push(child.proxy as any);
- }
- };
- const removeLink = (child: any) => {
- if (child.proxy) {
- let internalIndex = internalChildren.indexOf(child);
- if (internalIndex > -1) {
- internalChildren.splice(internalIndex, 1);
- }
- let publicIndex = publicChildren.indexOf(child.proxy);
- if (internalIndex > -1) {
- publicChildren.splice(publicIndex, 1);
- }
- }
- };
- provide(
- 'menuParent',
- Object.assign(
- {
- removeLink,
- link,
- children: publicChildren,
- internalChildren
- },
- value
- )
- );
- };
- return {
- children: publicChildren,
- linkChildren
- };
- };
- const { children, linkChildren } = useChildren();
- const opened = computed(() => children.some((item) => item.state.showWrapper));
- const classes = computed(() => {
- const prefixCls = componentName;
- return {
- [prefixCls]: true,
- 'scroll-fixed': isScrollFixed.value
- };
- });
- const updateOffset = () => {
- if (barRef.value) {
- const rect = useRect(barRef);
- if (props.direction === 'down') {
- offset.value = rect.bottom;
- } else {
- offset.value = window.innerHeight - rect.top;
- }
- }
- };
- linkChildren({ props, offset });
- const toggleItem = (active: number) => {
- children.forEach((item, index) => {
- if (index === active) {
- updateOffset();
- item.toggle();
- } else if (item.state.showPopup) {
- item.toggle(false, { immediate: true });
- }
- });
- };
- const getScrollTop = (el: Element | Window) => {
- return Math.max(0, 'scrollTop' in el ? el.scrollTop : el.pageYOffset);
- };
- const onScroll = () => {
- const { scrollFixed } = props;
- const scrollTop = getScrollTop(window);
- isScrollFixed.value = scrollTop > (typeof scrollFixed === 'boolean' ? 30 : Number(scrollFixed));
- };
- const getClasses = (showPopup: boolean) => {
- let str = '';
- const { titleClass } = props;
- if (showPopup) {
- str += 'active';
- }
- if (titleClass) {
- str += ` ${titleClass}`;
- }
- return str;
- };
- onMounted(() => {
- const { scrollFixed } = props;
- if (scrollFixed) {
- window.addEventListener('scroll', onScroll);
- }
- });
- onUnmounted(() => {
- const { scrollFixed } = props;
- if (scrollFixed) {
- window.removeEventListener('scroll', onScroll);
- }
- });
- return {
- toggleItem,
- children,
- opened,
- classes,
- barRef,
- getClasses
- };
- }
- });
- </script>
|