DropdownMenu.vue 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. <template>
  2. <view class="nana-dropdown-menu">
  3. <view class="nana-dropdown-menu-bar">
  4. <slot />
  5. </view>
  6. <scroll-view scroll-x>
  7. <DropdownMenuProvide :isExtra="true">
  8. <view class="nana-dropdown-menu-extra">
  9. <slot name="extra" />
  10. </view>
  11. </DropdownMenuProvide>
  12. </scroll-view>
  13. </view>
  14. </template>
  15. <script setup lang="ts">
  16. import { provide, toRef, type Ref } from 'vue';
  17. import { propGetThemeVar, type ViewStyle } from '../theme/ThemeDefine';
  18. import DropdownMenuProvide from './DropdownMenuProvide.vue';
  19. export interface DropdownMenuProps {
  20. /**
  21. * 菜单标题和选项的选中态颜色
  22. */
  23. activeColor?: string;
  24. /**
  25. * 背景颜色
  26. */
  27. backgroundColor?: string;
  28. /**
  29. * 菜单选项的样式
  30. */
  31. itemStyle?: ViewStyle;
  32. /**
  33. * 菜单额外选项的样式
  34. */
  35. itemExtraStyle?: ViewStyle;
  36. /**
  37. * 菜单的方向
  38. */
  39. direction?: 'up'|'down';
  40. /**
  41. * 动画时长,单位ms,设置为 0 可以禁用动画
  42. */
  43. duration?: number;
  44. /**
  45. * 是否包含导航栏空间,这会影响弹出菜单的定位
  46. * @default true
  47. */
  48. includeNavBarSpace?: boolean;
  49. }
  50. const props = withDefaults(defineProps<DropdownMenuProps>(), {
  51. activeColor: () => propGetThemeVar('DropdownMenuActiveColor', 'primary'),
  52. backgroundColor: () => propGetThemeVar('DropdownMenuBackgroundColor', 'white'),
  53. direction: 'down',
  54. duration: 300,
  55. includeNavBarSpace: true,
  56. });
  57. let closeCb: (() => void)|null = null;
  58. function close() {
  59. if (closeCb) {
  60. closeCb();
  61. closeCb = null;
  62. }
  63. }
  64. export interface DropdownMenuContext {
  65. activeColor: Ref<string>;
  66. backgroundColor: Ref<string>;
  67. itemStyle: Ref<ViewStyle|undefined>;
  68. includeNavBarSpace: Ref<boolean>;
  69. itemExtraStyle: Ref<ViewStyle|undefined>;
  70. direction: Ref<'up'|'down'>;
  71. duration: Ref<number>;
  72. setCurrentOpen: (cb: () => void) => void,
  73. }
  74. provide<DropdownMenuContext>('DropdownMenuContext', {
  75. activeColor: toRef(props, 'activeColor'),
  76. backgroundColor: toRef(props, 'backgroundColor'),
  77. includeNavBarSpace: toRef(props, 'includeNavBarSpace'),
  78. itemStyle: toRef(props, 'itemStyle'),
  79. itemExtraStyle: toRef(props, 'itemExtraStyle'),
  80. direction: toRef(props, 'direction'),
  81. duration: toRef(props, 'duration'),
  82. setCurrentOpen(cb: () => void) {
  83. if (closeCb)
  84. closeCb();
  85. closeCb = cb;
  86. },
  87. });
  88. export interface DropdownMenuInstance {
  89. close: () => void;
  90. }
  91. defineExpose<DropdownMenuInstance>({
  92. close,
  93. })
  94. defineOptions({
  95. options: {
  96. inheritAttrs: false,
  97. styleIsolation: 'shared',
  98. }
  99. })
  100. </script>
  101. <style lang="scss">
  102. .nana-dropdown-menu {
  103. display: flex;
  104. flex-direction: column;
  105. width: 100%;
  106. }
  107. .nana-dropdown-menu-bar {
  108. display: flex;
  109. flex-direction: row;
  110. justify-content: space-between;
  111. align-items: center;
  112. }
  113. .nana-dropdown-menu-extra {
  114. display: flex;
  115. flex-direction: row;
  116. justify-content: flex-start;
  117. align-items: center;
  118. padding: 16rpx 24rpx;
  119. }
  120. </style>