Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

menuable.js 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  2. import Vue from 'vue';
  3. import Positionable from './positionable';
  4. import Stackable from './stackable';
  5. /* eslint-disable object-property-newline */
  6. var dimensions = {
  7. activator: {
  8. top: 0, left: 0,
  9. bottom: 0, right: 0,
  10. width: 0, height: 0,
  11. offsetTop: 0, scrollHeight: 0
  12. },
  13. content: {
  14. top: 0, left: 0,
  15. bottom: 0, right: 0,
  16. width: 0, height: 0,
  17. offsetTop: 0, scrollHeight: 0
  18. },
  19. hasWindow: false
  20. };
  21. /* eslint-enable object-property-newline */
  22. /**
  23. * Menuable
  24. *
  25. * @mixin
  26. *
  27. * Used for fixed or absolutely positioning
  28. * elements within the DOM
  29. * Can calculate X and Y axis overflows
  30. * As well as be manually positioned
  31. */
  32. /* @vue/component */
  33. export default Vue.extend({
  34. name: 'menuable',
  35. mixins: [Positionable, Stackable],
  36. props: {
  37. activator: {
  38. default: null,
  39. validator: function validator(val) {
  40. return ['string', 'object'].includes(typeof val === 'undefined' ? 'undefined' : _typeof(val));
  41. }
  42. },
  43. allowOverflow: Boolean,
  44. inputActivator: Boolean,
  45. light: Boolean,
  46. dark: Boolean,
  47. maxWidth: {
  48. type: [Number, String],
  49. default: 'auto'
  50. },
  51. minWidth: [Number, String],
  52. nudgeBottom: {
  53. type: [Number, String],
  54. default: 0
  55. },
  56. nudgeLeft: {
  57. type: [Number, String],
  58. default: 0
  59. },
  60. nudgeRight: {
  61. type: [Number, String],
  62. default: 0
  63. },
  64. nudgeTop: {
  65. type: [Number, String],
  66. default: 0
  67. },
  68. nudgeWidth: {
  69. type: [Number, String],
  70. default: 0
  71. },
  72. offsetOverflow: Boolean,
  73. positionX: {
  74. type: Number,
  75. default: null
  76. },
  77. positionY: {
  78. type: Number,
  79. default: null
  80. },
  81. zIndex: {
  82. type: [Number, String],
  83. default: null
  84. }
  85. },
  86. data: function data() {
  87. return {
  88. absoluteX: 0,
  89. absoluteY: 0,
  90. activatorFixed: false,
  91. dimensions: Object.assign({}, dimensions),
  92. isContentActive: false,
  93. pageWidth: 0,
  94. pageYOffset: 0,
  95. stackClass: 'v-menu__content--active',
  96. stackMinZIndex: 6
  97. };
  98. },
  99. computed: {
  100. computedLeft: function computedLeft() {
  101. var a = this.dimensions.activator;
  102. var c = this.dimensions.content;
  103. var activatorLeft = (this.isAttached ? a.offsetLeft : a.left) || 0;
  104. var minWidth = Math.max(a.width, c.width);
  105. var left = 0;
  106. left += this.left ? activatorLeft - (minWidth - a.width) : activatorLeft;
  107. if (this.offsetX) {
  108. var maxWidth = isNaN(this.maxWidth) ? a.width : Math.min(a.width, this.maxWidth);
  109. left += this.left ? -maxWidth : a.width;
  110. }
  111. if (this.nudgeLeft) left -= parseInt(this.nudgeLeft);
  112. if (this.nudgeRight) left += parseInt(this.nudgeRight);
  113. return left;
  114. },
  115. computedTop: function computedTop() {
  116. var a = this.dimensions.activator;
  117. var c = this.dimensions.content;
  118. var top = 0;
  119. if (this.top) top += a.height - c.height;
  120. if (this.isAttached) top += a.offsetTop;else top += a.top + this.pageYOffset;
  121. if (this.offsetY) top += this.top ? -a.height : a.height;
  122. if (this.nudgeTop) top -= parseInt(this.nudgeTop);
  123. if (this.nudgeBottom) top += parseInt(this.nudgeBottom);
  124. return top;
  125. },
  126. hasActivator: function hasActivator() {
  127. return !!this.$slots.activator || !!this.$scopedSlots.activator || this.activator || this.inputActivator;
  128. },
  129. isAttached: function isAttached() {
  130. return this.attach !== false;
  131. }
  132. },
  133. watch: {
  134. disabled: function disabled(val) {
  135. val && this.callDeactivate();
  136. },
  137. isActive: function isActive(val) {
  138. if (this.disabled) return;
  139. val ? this.callActivate() : this.callDeactivate();
  140. },
  141. positionX: 'updateDimensions',
  142. positionY: 'updateDimensions'
  143. },
  144. beforeMount: function beforeMount() {
  145. this.checkForWindow();
  146. },
  147. methods: {
  148. absolutePosition: function absolutePosition() {
  149. return {
  150. offsetTop: 0,
  151. offsetLeft: 0,
  152. scrollHeight: 0,
  153. top: this.positionY || this.absoluteY,
  154. bottom: this.positionY || this.absoluteY,
  155. left: this.positionX || this.absoluteX,
  156. right: this.positionX || this.absoluteX,
  157. height: 0,
  158. width: 0
  159. };
  160. },
  161. activate: function activate() {},
  162. calcLeft: function calcLeft(menuWidth) {
  163. return (this.isAttached ? this.computedLeft : this.calcXOverflow(this.computedLeft, menuWidth)) + 'px';
  164. },
  165. calcTop: function calcTop() {
  166. return (this.isAttached ? this.computedTop : this.calcYOverflow(this.computedTop)) + 'px';
  167. },
  168. calcXOverflow: function calcXOverflow(left, menuWidth) {
  169. var xOverflow = left + menuWidth - this.pageWidth + 12;
  170. if ((!this.left || this.right) && xOverflow > 0) {
  171. left = Math.max(left - xOverflow, 0);
  172. } else {
  173. left = Math.max(left, 12);
  174. }
  175. return left + this.getOffsetLeft();
  176. },
  177. calcYOverflow: function calcYOverflow(top) {
  178. var documentHeight = this.getInnerHeight();
  179. var toTop = this.pageYOffset + documentHeight;
  180. var activator = this.dimensions.activator;
  181. var contentHeight = this.dimensions.content.height;
  182. var totalHeight = top + contentHeight;
  183. var isOverflowing = toTop < totalHeight;
  184. // If overflowing bottom and offset
  185. // TODO: set 'bottom' position instead of 'top'
  186. if (isOverflowing && this.offsetOverflow &&
  187. // If we don't have enough room to offset
  188. // the overflow, don't offset
  189. activator.top > contentHeight) {
  190. top = this.pageYOffset + (activator.top - contentHeight);
  191. // If overflowing bottom
  192. } else if (isOverflowing && !this.allowOverflow) {
  193. top = toTop - contentHeight - 12;
  194. // If overflowing top
  195. } else if (top < this.pageYOffset && !this.allowOverflow) {
  196. top = this.pageYOffset + 12;
  197. }
  198. return top < 12 ? 12 : top;
  199. },
  200. callActivate: function callActivate() {
  201. if (!this.hasWindow) return;
  202. this.activate();
  203. },
  204. callDeactivate: function callDeactivate() {
  205. this.isContentActive = false;
  206. this.deactivate();
  207. },
  208. checkForWindow: function checkForWindow() {
  209. if (!this.hasWindow) {
  210. this.hasWindow = typeof window !== 'undefined';
  211. }
  212. },
  213. checkForPageYOffset: function checkForPageYOffset() {
  214. if (this.hasWindow) {
  215. this.pageYOffset = this.activatorFixed ? 0 : this.getOffsetTop();
  216. }
  217. },
  218. checkActivatorFixed: function checkActivatorFixed() {
  219. if (this.attach !== false) return;
  220. var el = this.getActivator();
  221. while (el) {
  222. if (window.getComputedStyle(el).position === 'fixed') {
  223. this.activatorFixed = true;
  224. return;
  225. }
  226. el = el.offsetParent;
  227. }
  228. this.activatorFixed = false;
  229. },
  230. deactivate: function deactivate() {},
  231. getActivator: function getActivator(e) {
  232. if (this.inputActivator) {
  233. return this.$el.querySelector('.v-input__slot');
  234. }
  235. if (this.activator) {
  236. return typeof this.activator === 'string' ? document.querySelector(this.activator) : this.activator;
  237. }
  238. if (this.$refs.activator) {
  239. return this.$refs.activator.children.length > 0 ? this.$refs.activator.children[0] : this.$refs.activator;
  240. }
  241. if (e) {
  242. this.activatedBy = e.currentTarget || e.target;
  243. return this.activatedBy;
  244. }
  245. if (this.activatedBy) return this.activatedBy;
  246. if (this.activatorNode) {
  247. var activator = Array.isArray(this.activatorNode) ? this.activatorNode[0] : this.activatorNode;
  248. var el = activator && activator.elm;
  249. if (el) return el;
  250. }
  251. },
  252. getInnerHeight: function getInnerHeight() {
  253. if (!this.hasWindow) return 0;
  254. return window.innerHeight || document.documentElement.clientHeight;
  255. },
  256. getOffsetLeft: function getOffsetLeft() {
  257. if (!this.hasWindow) return 0;
  258. return window.pageXOffset || document.documentElement.scrollLeft;
  259. },
  260. getOffsetTop: function getOffsetTop() {
  261. if (!this.hasWindow) return 0;
  262. return window.pageYOffset || document.documentElement.scrollTop;
  263. },
  264. getRoundedBoundedClientRect: function getRoundedBoundedClientRect(el) {
  265. var rect = el.getBoundingClientRect();
  266. return {
  267. top: Math.round(rect.top),
  268. left: Math.round(rect.left),
  269. bottom: Math.round(rect.bottom),
  270. right: Math.round(rect.right),
  271. width: Math.round(rect.width),
  272. height: Math.round(rect.height)
  273. };
  274. },
  275. measure: function measure(el) {
  276. if (!el || !this.hasWindow) return null;
  277. var rect = this.getRoundedBoundedClientRect(el);
  278. // Account for activator margin
  279. if (this.isAttached) {
  280. var style = window.getComputedStyle(el);
  281. rect.left = parseInt(style.marginLeft);
  282. rect.top = parseInt(style.marginTop);
  283. }
  284. return rect;
  285. },
  286. sneakPeek: function sneakPeek(cb) {
  287. var _this = this;
  288. requestAnimationFrame(function () {
  289. var el = _this.$refs.content;
  290. if (!el || _this.isShown(el)) return cb();
  291. el.style.display = 'inline-block';
  292. cb();
  293. el.style.display = 'none';
  294. });
  295. },
  296. startTransition: function startTransition() {
  297. var _this2 = this;
  298. return new Promise(function (resolve) {
  299. return requestAnimationFrame(function () {
  300. _this2.isContentActive = _this2.hasJustFocused = _this2.isActive;
  301. resolve();
  302. });
  303. });
  304. },
  305. isShown: function isShown(el) {
  306. return el.style.display !== 'none';
  307. },
  308. updateDimensions: function updateDimensions() {
  309. var _this3 = this;
  310. this.checkForWindow();
  311. this.checkActivatorFixed();
  312. this.checkForPageYOffset();
  313. this.pageWidth = document.documentElement.clientWidth;
  314. var dimensions = {};
  315. // Activator should already be shown
  316. if (!this.hasActivator || this.absolute) {
  317. dimensions.activator = this.absolutePosition();
  318. } else {
  319. var activator = this.getActivator();
  320. dimensions.activator = this.measure(activator);
  321. dimensions.activator.offsetLeft = activator.offsetLeft;
  322. if (this.isAttached) {
  323. // account for css padding causing things to not line up
  324. // this is mostly for v-autocomplete, hopefully it won't break anything
  325. dimensions.activator.offsetTop = activator.offsetTop;
  326. } else {
  327. dimensions.activator.offsetTop = 0;
  328. }
  329. }
  330. // Display and hide to get dimensions
  331. this.sneakPeek(function () {
  332. dimensions.content = _this3.measure(_this3.$refs.content);
  333. _this3.dimensions = dimensions;
  334. });
  335. }
  336. }
  337. });
  338. //# sourceMappingURL=menuable.js.map