项目原始demo,不改动
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
Dieses Repo ist archiviert. Du kannst Dateien sehen und es klonen, kannst aber nicht pushen oder Issues/Pull-Requests öffnen.
 
 
 
 

182 Zeilen
4.9 KiB

  1. // Mixins
  2. import Delayable from '../delayable';
  3. import Toggleable from '../toggleable'; // Utilities
  4. import mixins from '../../util/mixins';
  5. import { getSlot, getSlotType } from '../../util/helpers';
  6. import { consoleError } from '../../util/console';
  7. const baseMixins = mixins(Delayable, Toggleable);
  8. /* @vue/component */
  9. export default baseMixins.extend({
  10. name: 'activatable',
  11. props: {
  12. activator: {
  13. default: null,
  14. validator: val => {
  15. return ['string', 'object'].includes(typeof val);
  16. }
  17. },
  18. disabled: Boolean,
  19. internalActivator: Boolean,
  20. openOnHover: Boolean
  21. },
  22. data: () => ({
  23. // Do not use this directly, call getActivator() instead
  24. activatorElement: null,
  25. activatorNode: [],
  26. events: ['click', 'mouseenter', 'mouseleave'],
  27. listeners: {}
  28. }),
  29. watch: {
  30. activator: 'resetActivator',
  31. openOnHover: 'resetActivator'
  32. },
  33. mounted() {
  34. const slotType = getSlotType(this, 'activator', true);
  35. if (slotType && ['v-slot', 'normal'].includes(slotType)) {
  36. consoleError(`The activator slot must be bound, try '<template v-slot:activator="{ on }"><v-btn v-on="on">'`, this);
  37. }
  38. this.addActivatorEvents();
  39. },
  40. beforeDestroy() {
  41. this.removeActivatorEvents();
  42. },
  43. methods: {
  44. addActivatorEvents() {
  45. if (!this.activator || this.disabled || !this.getActivator()) return;
  46. this.listeners = this.genActivatorListeners();
  47. const keys = Object.keys(this.listeners);
  48. for (const key of keys) {
  49. this.getActivator().addEventListener(key, this.listeners[key]);
  50. }
  51. },
  52. genActivator() {
  53. const node = getSlot(this, 'activator', Object.assign(this.getValueProxy(), {
  54. on: this.genActivatorListeners(),
  55. attrs: this.genActivatorAttributes()
  56. })) || [];
  57. this.activatorNode = node;
  58. return node;
  59. },
  60. genActivatorAttributes() {
  61. return {
  62. role: 'button',
  63. 'aria-haspopup': true,
  64. 'aria-expanded': String(this.isActive)
  65. };
  66. },
  67. genActivatorListeners() {
  68. if (this.disabled) return {};
  69. const listeners = {};
  70. if (this.openOnHover) {
  71. listeners.mouseenter = e => {
  72. this.getActivator(e);
  73. this.runDelay('open');
  74. };
  75. listeners.mouseleave = e => {
  76. this.getActivator(e);
  77. this.runDelay('close');
  78. };
  79. } else {
  80. listeners.click = e => {
  81. const activator = this.getActivator(e);
  82. if (activator) activator.focus();
  83. e.stopPropagation();
  84. this.isActive = !this.isActive;
  85. };
  86. }
  87. return listeners;
  88. },
  89. getActivator(e) {
  90. // If we've already fetched the activator, re-use
  91. if (this.activatorElement) return this.activatorElement;
  92. let activator = null;
  93. if (this.activator) {
  94. const target = this.internalActivator ? this.$el : document;
  95. if (typeof this.activator === 'string') {
  96. // Selector
  97. activator = target.querySelector(this.activator);
  98. } else if (this.activator.$el) {
  99. // Component (ref)
  100. activator = this.activator.$el;
  101. } else {
  102. // HTMLElement | Element
  103. activator = this.activator;
  104. }
  105. } else if (this.activatorNode.length === 1 || this.activatorNode.length && !e) {
  106. // Use the contents of the activator slot
  107. // There's either only one element in it or we
  108. // don't have a click event to use as a last resort
  109. const vm = this.activatorNode[0].componentInstance;
  110. if (vm && vm.$options.mixins && // Activatable is indirectly used via Menuable
  111. vm.$options.mixins.some(m => m.options && ['activatable', 'menuable'].includes(m.options.name))) {
  112. // Activator is actually another activatible component, use its activator (#8846)
  113. activator = vm.getActivator();
  114. } else {
  115. activator = this.activatorNode[0].elm;
  116. }
  117. } else if (e) {
  118. // Activated by a click event
  119. activator = e.currentTarget || e.target;
  120. }
  121. this.activatorElement = activator;
  122. return this.activatorElement;
  123. },
  124. getContentSlot() {
  125. return getSlot(this, 'default', this.getValueProxy(), true);
  126. },
  127. getValueProxy() {
  128. const self = this;
  129. return {
  130. get value() {
  131. return self.isActive;
  132. },
  133. set value(isActive) {
  134. self.isActive = isActive;
  135. }
  136. };
  137. },
  138. removeActivatorEvents() {
  139. if (!this.activator || !this.activatorElement) return;
  140. const keys = Object.keys(this.listeners);
  141. for (const key of keys) {
  142. this.activatorElement.removeEventListener(key, this.listeners[key]);
  143. }
  144. this.listeners = {};
  145. },
  146. resetActivator() {
  147. this.removeActivatorEvents();
  148. this.activatorElement = null;
  149. this.getActivator();
  150. this.addActivatorEvents();
  151. }
  152. }
  153. });
  154. //# sourceMappingURL=index.js.map