UNPKG

helix-ui

Version:
1,692 lines (1,458 loc) 306 kB
/** @license @nocompile Copyright (c) 2018 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt */ (function () { 'use strict'; (function(){if(void 0===window.Reflect||void 0===window.customElements||window.customElements.polyfillWrapFlushCallback)return;const a=HTMLElement;window.HTMLElement={HTMLElement:function HTMLElement(){return Reflect.construct(a,[],this.constructor)}}.HTMLElement,HTMLElement.prototype=a.prototype,HTMLElement.prototype.constructor=HTMLElement,Object.setPrototypeOf(HTMLElement,a);})(); }()); /*! @license @nocompile Copyright 2017-2021 Rackspace US, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ 'use strict'; // Modified from https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove#Polyfill [Element.prototype, CharacterData.prototype, DocumentType.prototype].forEach(function (proto) { if (!proto.hasOwnProperty('remove')) { Object.defineProperty(proto, 'remove', { configurable: true, enumerable: true, writable: true, value: function value() { if (this.parentNode !== null) { this.parentNode.removeChild(this); } } }); } }); // Modified from https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill /* * If browser supports a variation of matches(), (IE9+) * normalize to 'matches' on the prototype. */ if (!Element.prototype.matches) { Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; } if (!Element.prototype.closest) { Element.prototype.closest = function (selectors) { var el = this; // fail fast if element isn't attached to the document if (!document.documentElement.contains(el)) { return null; } // Check if any ancestors match selectors while (el !== null && el.nodeType === 1) { if (el.matches(selectors)) { return el; } else { el = el.parentElement || el.parentNode; } } // Return null if no ancestors match return null; }; } function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function (obj) { return typeof obj; }; } else { _typeof = function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } function _possibleConstructorReturn(self, call) { if (call && (typeof call === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); } function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } // Keep track of prepared templates var TEMPLATE_CACHE = {}; /** * Define functionality common to all HelixUI elements. * * @extends external:HTMLElement * @hideconstructor * @since 0.2.0 */ var HXElement = /*#__PURE__*/function (_HTMLElement) { _inherits(HXElement, _HTMLElement); var _super = _createSuper(HXElement); _createClass(HXElement, [{ key: "$onCreate", /** * HelixUI lifecycle callback called at the end of construction. * * Use this callback to apply pre-connect setup logic. * * @abstract * @ignore */ value: function $onCreate() {} /** * HelixUI lifecycle method called at the end of the connectedCallback() * Custom Element lifecycle method. * * Use this callback to initialize an element's behavior. * * @abstract * @ignore */ }, { key: "$onConnect", value: function $onConnect() {} /** * HelixUI lifecycle method. Called at the end of {@link HXElement.disconnectedCallback}. * * @abstract * @ignore */ }, { key: "$onDisconnect", value: function $onDisconnect() {} /** * HelixUI lifecycle method called when an observed attribute's value changes. * * @abstract * @ignore * @param {String} attr - name of the attribute that changed * @param {String} newVal - value of the attribute after the change * @param {String} oldVal - value of the attribute before the change */ }, { key: "$onAttributeChange", value: function $onAttributeChange(attr, oldVal, newVal) {} // eslint-disable-line no-unused-vars /** * Register class with the customElements registry. * Note: the custom element is only registered if the "is" class property is defined. */ }], [{ key: "$define", value: function $define() { if (this.is) { customElements.define(this.is, this); } } // Called when an instance is created }, { key: "is", /** * Defines the name of the element to register in the Custom Element registry * * @abstract * @default undefined * @type {String} */ get: function get() {} /** * Defines the innerHTML of the ShadowDOM. * * If undefined, no Shadow Root will be created. * * @abstract * @default undefined * @type {String} */ }, { key: "template", get: function get() {} /** * Defines a list of attributes to watch for changes * (in addition to those defined by {@link HXElement.observedAttributes}). * * @abstract * @default [] * @ignore * @type {Array<String>} */ }, { key: "$observedAttributes", get: function get() { return []; } }]); function HXElement() { var _this; _classCallCheck(this, HXElement); _this = _super.call(this); _this._$setupShadowDOM(); _this.$onAttributeChange = _this.$onAttributeChange.bind(_assertThisInitialized(_this)); _this.$onConnect = _this.$onConnect.bind(_assertThisInitialized(_this)); _this.$onCreate = _this.$onCreate.bind(_assertThisInitialized(_this)); _this.$onDisconnect = _this.$onDisconnect.bind(_assertThisInitialized(_this)); _this.$relayEvent = _this.$relayEvent.bind(_assertThisInitialized(_this)); _this.$onCreate(); return _this; } //constructor // Called when an instance of the element is attached to the DOM. _createClass(HXElement, [{ key: "connectedCallback", value: function connectedCallback() { this._$tabIndex = this.getAttribute('tabindex'); this.$upgradeProperty('disabled'); this.setAttribute('hx-defined', ''); this._$styleElement(); this.$onConnect(); } // Called when an instance of the element is removed from the DOM. }, { key: "disconnectedCallback", value: function disconnectedCallback() { this.$onDisconnect(); } /** * Custom Elements API property used to determine when to call the * attributeChangedCallback() lifecycle method. * * @default ['disabled'] * @ignore * @see HXElement.$observedAttributes * @type {Array<String>} */ }, { key: "attributeChangedCallback", // Called when an attribute is SET (not just when it changes). value: function attributeChangedCallback(attr, oldVal, newVal) { if (attr === 'disabled') { if (newVal !== null) { this.removeAttribute('tabindex'); this.setAttribute('aria-disabled', true); this.blur(); } else { if (this._$tabIndex) { this.setAttribute('tabindex', this._$tabIndex); } this.removeAttribute('aria-disabled'); } } // Always call $onAttributeChange, so that we can run additional // logic against common attributes in subclasses, too. if (newVal !== oldVal) { this.$onAttributeChange(attr, oldVal, newVal); } } //attributeChangedCallback /* ===== PUBLIC PROPERTIES ===== */ /** * Indicates whether the element is disabled. * A disabled element is nonfunctional and noninteractive. * * @default false * @type {Boolean} */ }, { key: "$defaultAttribute", /* ===== PUBLIC METHODS ===== */ /** * Assign a value to an HTML attribute, if the attribute isn't present. * * @param {String} name - name of the attribute to set * @param {String} val - value to assign * @see https://goo.gl/MUFHD8 */ value: function $defaultAttribute(name, val) { if (!this.hasAttribute(name)) { this.setAttribute(name, val); } } /** * Emit a custom event * * @param {String} evtName - name of event * @param {Object} opts - options to configure the event * @param {Boolean} [opts.cancelable=true] - whether the event can be canceled * @param {Boolean} [opts.bubbles=false] - whether the event bubbles up the DOM tree * @param {*} [opts.detail] - additional information to communicated along with the event * * @returns {Boolean} * Returns true if the event was not canceled by an event listener. * * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent */ }, { key: "$emit", value: function $emit(evtName, opts) { var options = Object.assign({}, { cancelable: true, bubbles: false }, opts); var evt = new CustomEvent(evtName, options); return this.dispatchEvent(evt); } //$emit /** * Relay an event within the ShadowDOM, retargeting the event to the custom element * * @param {Event} oldEvent - event to relay * @returns {Boolean} Returns true if the event was not canceled by an event listener. */ }, { key: "$relayEvent", value: function $relayEvent(oldEvent) { // Emit new event of same name var newEvent = new CustomEvent(oldEvent.type, { bubbles: oldEvent.bubbles, cancelable: oldEvent.cancelable }); return this.dispatchEvent(newEvent); } //$relayEvent() /** * Relay events that do not bubble. For instance, `focus` and `blur` events * on an `<input>` within the ShadowDOM. * * @param {HTMLElement} el - element to attach non-bubbling event listeners */ }, { key: "$relayNonBubblingEvents", value: function $relayNonBubblingEvents(el) { el.addEventListener('focus', this.$relayEvent); el.addEventListener('blur', this.$relayEvent); } /** * Remove events relayed by `$relayNonBubblingEvents` * * @param {HTMLElement} el - element to remove non-bubbline event listeners */ }, { key: "$removeNonBubblingRelays", value: function $removeNonBubblingRelays(el) { el.removeEventListener('focus', this.$relayEvent); el.removeEventListener('blur', this.$relayEvent); } /** * Captures the value from the unupgraded instance and deletes the property * so it does not shadow the custom element's own property setter. This way, * when the element's definition does finally load, it can immediately * reflect the correct state. * * @param {String} prop - property name to upgrade * @see https://goo.gl/MDp6j5 */ }, { key: "$upgradeProperty", value: function $upgradeProperty(prop) { if (this.hasOwnProperty(prop)) { var value = this[prop]; delete this[prop]; this[prop] = value; } } /* ===== PRIVATE PROPERTIES ===== */ // TBD /* ===== PRIVATE METHODS ===== */ /** * @private * @description * Prepares a template for injection into the shadow root * @param {String} strTemplate - HTML template content * @returns {HTMLTemplate} */ }, { key: "_$prepareTemplate", value: function _$prepareTemplate(strTemplate) { var _elementName = this.constructor.is; if (TEMPLATE_CACHE[_elementName]) { return TEMPLATE_CACHE[_elementName]; } var _template = document.createElement('template'); _template.innerHTML = strTemplate; if (window.ShadyCSS) { // modifies 'template' variable in-place ShadyCSS.prepareTemplate(_template, _elementName); } // cache prepared template, so it isn't prepared more than once TEMPLATE_CACHE[_elementName] = _template; return _template; } //_$prepareTemplate() /** * @private * @description * If a ShadowDOM needs to be setup, this method handles: * * 1. preparing the <template> element * 2. attaching a shadow root * 3. applying ShadyDOM styling (if needed) * 4. stamping the template */ }, { key: "_$setupShadowDOM", value: function _$setupShadowDOM() { // Don't do anything unless the "template" class property is defined. if (this.constructor.template) { var _template = this._$prepareTemplate(this.constructor.template); this.attachShadow({ mode: 'open' }); this.shadowRoot.appendChild(_template.content.cloneNode(true)); } } //_$setupShadowDOM() /** * @description * Style the element using ShadyCSS, if needed. * * @note: has the potential to modify the `[class]` attribute * of the element, so avoid running in the constructor. */ }, { key: "_$styleElement", value: function _$styleElement() { // short circuit if browser natively supports ShadowDOM if (!window.ShadyCSS) { return; } ShadyCSS.styleElement(this); } }, { key: "disabled", get: function get() { return this.hasAttribute('disabled'); }, set: function set(value) { if (value) { this.setAttribute('disabled', ''); } else { this.removeAttribute('disabled'); } } }], [{ key: "observedAttributes", get: function get() { var common = ['disabled']; var extra = this.$observedAttributes; return [].concat(common, _toConsumableArray(extra)); } }]); return HXElement; }( /*#__PURE__*/_wrapNativeSuper(HTMLElement)); /** * @module HelixUI/Utils/Alignment * @description * Alignment logic in regards to positioning * * See https://codepen.io/CITguy/pen/b1286136d695391a40a6d708b765361c */ /** * @typedef {String} AlignmentString * @global * * @description * Positions are composed of alignments (e.g., the position `top-left` * has alignments `top` and `left`). Some alignments are axis-specific, * while others are not. Alignments `start` and `end` may apply to either * horizontal (x-axis) alignment OR vertical (y-axis) aligment. * * Values: * - `bottom` * - `center` * - `end` * - `left` * - `middle` * - `right` * - `start` * - `top` */ /** * @typedef {String} PositionString * @global * * @description * * **Supported Positions** * * The following, normalized values are suported. * - `bottom-start` * - `bottom-left` * - `bottom-center` * - `bottom-right` * - `bottom-end` * - `center-middle` * - `left-start` * - `left-top` * - `left-middle` * - `left-bottom` * - `left-end` * - `right-start` * - `right-top` * - `right-middle` * - `right-bottom` * - `right-end` * - `top-start` * - `top-left` * - `top-center` * - `top-right` * - `top-end` * * * **Deprecated Positions** * * Support for the following values will be removed in a future release. * - `bottom` * - `center` * - `left` * - `right` * - `top` */ var OPPOSITE_ALIGNMENTS = { 'bottom': 'top', 'center': 'center', 'end': 'start', 'left': 'right', 'middle': 'middle', 'right': 'left', 'start': 'end', 'top': 'bottom' }; /** * Convert position string into vertical alignment, horizontal alignment, * and main axis properties. * * @param {PositionString} position user-configured position string * @returns {Object} alignment metadata */ function getAlignment(position) { var crossAlign; // cross-axis alignment var crossAxis = getCrossAxis(position); var mainAlign; // main-axis alignment var mainAxis = getMainAxis(position); // x-axis and y-axis alignment (in relation to viewport coordinates) var yAlign = getVerticalAlignment(position); var xAlign = getHorizontalAlignment(position); // https://regex101.com/r/1oRJf8/7 var startEndMatch = position.match(/(start|end)$/); if (startEndMatch) { if (mainAxis === 'x') { yAlign = startEndMatch[0]; } else { xAlign = startEndMatch[0]; } } // determine main-axis and cross-axis alignment if (mainAxis === 'x') { mainAlign = xAlign; crossAlign = yAlign; } else { mainAlign = yAlign; crossAlign = xAlign; } return { crossAlign: crossAlign, crossAxis: crossAxis, mainAlign: mainAlign, mainAxis: mainAxis, xAlign: xAlign, yAlign: yAlign }; } /** * Determine secondary axis (x or y; opposite of main axis) from position. * * @param {PositionString} position * @returns {Enum<String>} */ function getCrossAxis(position) { return getMainAxis(position) === 'x' ? 'y' : 'x'; } /** * Determine x-axis alignment from position * * @param {PositionString} position * @returns {AlignmentString} */ function getHorizontalAlignment(position) { var xAlign = 'center'; // https://regex101.com/r/1oRJf8/5 var hMatch = position.match(/^(left|right)|(left|right)$/); if (hMatch) { xAlign = hMatch[0]; } return xAlign; } /** * Determine primary axis (x or y) from position * * @param {PositionString} position * @returns {Enum} */ function getMainAxis(position) { // https://regex101.com/r/1oRJf8/1 if (/^(top|bottom)/.test(position)) { return 'y'; } else { return 'x'; } } /** * Determine y-axis alignment from position * * @param {PositionString} position * @returns {AlignmentString} */ function getVerticalAlignment(position) { var yAlign = 'middle'; // https://regex101.com/r/1oRJf8/4 var vMatch = position.match(/^(top|bottom)|(top|bottom)$/); if (vMatch) { yAlign = vMatch[0]; } return yAlign; } /** * Calculates position string that is horizontally opposite of given position. * * @param {PositionString} position * @returns {PositionString} horizontally inverted position string */ function invertPositionHorizontally(position) { var _getAlignment = getAlignment(position), mainAxis = _getAlignment.mainAxis, xAlign = _getAlignment.xAlign, yAlign = _getAlignment.yAlign; var newXAlign = OPPOSITE_ALIGNMENTS[xAlign]; return mainAxis === 'x' ? "".concat(newXAlign, "-").concat(yAlign) : "".concat(yAlign, "-").concat(newXAlign); } /** * Calculates position string that is vertically opposite of given position. * * @param {PositionString} position * @returns {PositionString} vertically inverted position string */ function invertPositionVertically(position) { var _getAlignment2 = getAlignment(position), mainAxis = _getAlignment2.mainAxis, xAlign = _getAlignment2.xAlign, yAlign = _getAlignment2.yAlign; var newYAlign = OPPOSITE_ALIGNMENTS[yAlign]; return mainAxis === 'x' ? "".concat(xAlign, "-").concat(newYAlign) : "".concat(newYAlign, "-").concat(xAlign); } /** * Normalize user-configured position to "{mainAlign}-{crossAlign}" format. * * - "top" -> "top-center" * - "right" -> "right-middle" * - "center" -> "center-middle" * - etc. * * @param {PositionString} position * @returns {PositionString} */ function normalizePosition(position) { var _getAlignment3 = getAlignment(position), crossAlign = _getAlignment3.crossAlign, mainAlign = _getAlignment3.mainAlign; return "".concat(mainAlign, "-").concat(crossAlign); } /** * @param {PositionString} position * @param {PredicateCollisions} collides */ function optimizePositionForCollisions(position, collides) { var _getAlignment4 = getAlignment(position), xAlign = _getAlignment4.xAlign, yAlign = _getAlignment4.yAlign; // ----- COLLIDE WITH TOP EDGE ----- // CHANGE // - 'top-*' -> 'bottom-*' // - '(left|right)-top' -> '(left|right)-bottom' // - '(left|right)-end' -> '(left|right)-start' // // IGNORE // - 'bottom-*' // - '{H}-bottom' // - '{H}-start' // - '{H}-middle' if (collides.top && yAlign.match(/top|end/)) { position = invertPositionVertically(position); } // ----- COLLIDE WITH BOTTOM EDGE ----- // CHANGE // - 'bottom-*' -> 'top-*' // - '(left|right)-bottom' -> '(left|right)-top' // - '(left|right)-start' -> '(left|right)-end' // // IGNORE // - 'top-*' // - '{H}-top' // - '{H}-middle' // - '{H}-end' if (collides.bottom && yAlign.match(/bottom|start/)) { position = invertPositionVertically(position); } // ----- COLLIDE WITH LEFT EDGE ----- // CHANGE // - 'left-*' -> 'right-*' // - '(top|bottom)-left' -> '(top|bottom)-right' // - '(top|bottom)-end' -> '(top|bottom)-start' // // IGNORE // - 'right-*' // - '{V}-right' // - '{V}-start' // - '{V}-center' if (collides.left && xAlign.match(/left|end/)) { position = invertPositionHorizontally(position); } // ----- COLLIDE WITH RIGHT EDGE ----- // CHANGE // - 'right-*' -> 'left-*' // - '(top|bottom)-right' -> '(top|bottom)-left' // - '(top|bottom)-start' -> '(top|bottom)-end' // // IGNORE // - 'left-*' // - '(top|bottom)-left' // - '(top|bottom)-center' // - '(top|bottom)-end' if (collides.right && xAlign.match(/right|start/)) { position = invertPositionHorizontally(position); } // TODO: What if both sides of an axis collide? // e.g., both left/right or top/bottom collide return position; } var index = /*#__PURE__*/Object.freeze({ __proto__: null, getAlignment: getAlignment, getCrossAxis: getCrossAxis, getHorizontalAlignment: getHorizontalAlignment, getMainAxis: getMainAxis, getVerticalAlignment: getVerticalAlignment, invertPositionHorizontally: invertPositionHorizontally, invertPositionVertically: invertPositionVertically, normalizePosition: normalizePosition, optimizePositionForCollisions: optimizePositionForCollisions }); /** * @module HelixUI/Utils/Offset * @description * Utilities to calculate coordinates of an offset element * in relation to a relative element. * * @example <caption>Positioning a menu below a button</caption> * let elOffset = document.querySelector('menu'); * let elReference = document.querySelector('button'); * * // grab bounding DOMRects * let offRect = elOffset.getBoundingClientRect(); * let refRect = elRef.getBoundingClientRect(); * * // Calculate coordinates * let { x, y } = getBottom(offRect, refRect); */ /** * @external DOMRect * @description Object returned by Element.getBoundingClientRect(). * * - MDN: [DOMRect](https://developer.mozilla.org/en-US/docs/Web/API/DOMRect) */ /** * @global * @typedef {Number} Coordinate * @description Numeric, pixel coordinate */ /** * @typedef {Object} OffsetDelta * @description * Calculated metadata * * @prop {Integer} dH - height difference between target element and offset element * @prop {Integer} dW - width difference between target element and offset element * @prop {Integer} dX - X delta (a positive value shifts the target RIGHT) * @prop {Integer} dY - Y delta (a positive value shifts the target DOWN) */ /** * @typedef {Object} OffsetOptions * @description * Offset configuration object * * @default {} * @prop {Integer} [dX=0] - X offset (a positive value shifts the target RIGHT) * @prop {Integer} [dY=0] - Y offset (a positive value shifts the target DOWN) */ /** * @typedef {Object} XYCoordinates * @global * * @prop {Coordinate} x - X coordinate * @prop {Coordinate} y - Y coordinate */ /** * Utility function to calculate delta metadata * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {OffsetDelta} */ function _getDeltas(off, ref) { var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; // height delta var dH = ref.height - off.height; // width delta var dW = ref.width - off.width; // X delta var dX = opts.dX || 0; // Y delta var dY = opts.dY || 0; return { dH: dH, dW: dW, dX: dX, dY: dY }; } /** * Calculate { x, y } coordinates needed to center align two elements. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getCenter(off, ref) { var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; var _getDeltas2 = _getDeltas(off, ref, opts), dW = _getDeltas2.dW, dH = _getDeltas2.dH, dX = _getDeltas2.dX, dY = _getDeltas2.dY; var x = ref.left + dW / 2 + dX; var y = ref.top + dH / 2 + dY; return { x: x, y: y }; } /** * Calculate { x, y } coordinates needed to position a target element above a * reference element, with their y-axes aligned. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getTop(off, ref, opts) { var _getDeltas3 = _getDeltas(off, ref, opts), dY = _getDeltas3.dY; var _getCenter = getCenter(off, ref, opts), x = _getCenter.x; var y = ref.top - off.height + dY; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element below a * reference element, with their y-axes aligned. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getBottom(off, ref, opts) { var _getDeltas4 = _getDeltas(off, ref, opts), dY = _getDeltas4.dY; var _getCenter2 = getCenter(off, ref, opts), x = _getCenter2.x; var y = ref.top + ref.height + dY; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element left of a * reference element, with their x-axes aligned. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getLeft(off, ref, opts) { var _getDeltas5 = _getDeltas(off, ref, opts), dX = _getDeltas5.dX; var _getCenter3 = getCenter(off, ref, opts), y = _getCenter3.y; var x = ref.left - off.width + dX; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element right of a * reference element, with their x-axes aligned. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getRight(off, ref, opts) { var _getDeltas6 = _getDeltas(off, ref, opts), dX = _getDeltas6.dX; var _getCenter4 = getCenter(off, ref, opts), y = _getCenter4.y; var x = ref.left + ref.width + dX; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element above and to the * left of a reference element, so that the right edge of the target element aligns * with the y-axis of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getTopLeft(off, ref, opts) { var _getTop = getTop(off, ref, opts), xT = _getTop.x, y = _getTop.y; var x = xT - off.width / 2; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element above and to the * left of a reference element, so that the left edge of the target element aligns * with the left edge of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getTopStart(off, ref, opts) { var _getDeltas7 = _getDeltas(off, ref, opts), dX = _getDeltas7.dX; var x = ref.left + dX; var _getTop2 = getTop(off, ref, opts), y = _getTop2.y; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element above and to the * right of a reference element, so that the right edge of the target element aligns * with the right edge of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getTopEnd(off, ref, opts) { var _getDeltas8 = _getDeltas(off, ref, opts), dX = _getDeltas8.dX; var x = ref.right - off.width + dX; var _getTop3 = getTop(off, ref, opts), y = _getTop3.y; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element above and to the * right of a reference element, so that the left edge of the target element aligns * with the y-axis of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getTopRight(off, ref, opts) { var _getTop4 = getTop(off, ref, opts), xT = _getTop4.x, y = _getTop4.y; var x = xT + off.width / 2; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element right and slightly higher * than the target element, so that the bottom edge of the target element aligns with the * x-axis of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getRightTop(off, ref, opts) { var _getRight = getRight(off, ref, opts), yR = _getRight.y, x = _getRight.x; var y = yR - off.height / 2; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element right and slightly higher * than the target element, so that the top edge of the target element aligns with the * top edge of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getRightStart(off, ref, opts) { var _getDeltas9 = _getDeltas(off, ref, opts), dY = _getDeltas9.dY; var _getRight2 = getRight(off, ref, opts), x = _getRight2.x; var y = ref.top + dY; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element right and slightly lower * than the target element, so that the bottom edge of the target element aligns with the * bottom edge of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getRightEnd(off, ref, opts) { var _getDeltas10 = _getDeltas(off, ref, opts), dY = _getDeltas10.dY; var _getRight3 = getRight(off, ref, opts), x = _getRight3.x; var y = ref.bottom - off.height + dY; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element right and * slightly lower than the target element, so that the top edge of the target * element aligns with the x-axis of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getRightBottom(off, ref, opts) { var _getRight4 = getRight(off, ref, opts), x = _getRight4.x, yR = _getRight4.y; var y = yR + off.height / 2; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element below and to the * right of a reference element, so that the left edge of the target element aligns * with the y-axis of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getBottomRight(off, ref, opts) { var _getBottom = getBottom(off, ref, opts), xB = _getBottom.x, y = _getBottom.y; var x = xB + off.width / 2; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element below and to the * right of a reference element, so that the right edge of the target element aligns * with the right edge of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getBottomEnd(off, ref, opts) { var _getDeltas11 = _getDeltas(off, ref, opts), dX = _getDeltas11.dX; var x = ref.right - off.width + dX; var _getBottom2 = getBottom(off, ref, opts), y = _getBottom2.y; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element below and to the * left of a reference element, so that the left edge of the target element aligns * with the left edge of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getBottomStart(off, ref, opts) { var _getDeltas12 = _getDeltas(off, ref, opts), dX = _getDeltas12.dX; var _getBottom3 = getBottom(off, ref, opts), y = _getBottom3.y; var x = ref.left + dX; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element below and to the * left of a reference element, so that the right edge of the target element aligns * with the y-axis of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getBottomLeft(off, ref, opts) { var _getBottom4 = getBottom(off, ref, opts), xB = _getBottom4.x, y = _getBottom4.y; var x = xB - off.width / 2; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element left and slightly lower * than the target element, so that the top edge of the target element aligns with the * x-axis of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getLeftBottom(off, ref, opts) { var _getLeft = getLeft(off, ref, opts), x = _getLeft.x, yL = _getLeft.y; var y = yL + off.height / 2; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element left and slightly lower * than the target element, so that the bottom edge of the target element aligns with the * bottom edge of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getLeftEnd(off, ref, opts) { var _getDeltas13 = _getDeltas(off, ref, opts), dY = _getDeltas13.dY; var _getLeft2 = getLeft(off, ref, opts), x = _getLeft2.x; var y = ref.bottom - off.height + dY; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element left and slightly higher * than the target element, so that the top edge of the target element aligns with the * top edge of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getLeftStart(off, ref, opts) { var _getDeltas14 = _getDeltas(off, ref, opts), dY = _getDeltas14.dY; var _getLeft3 = getLeft(off, ref, opts), x = _getLeft3.x; var y = ref.top + dY; return { x: x, y: y }; } /** * Calculate (x,y) coordinates needed to position a target element left and slightly higher * than the target element, so that the bottom edge of the target element aligns with the * x-axis of the reference element. * * @param {DOMRect} off - bounding rectangle for the target element * @param {DOMRect} ref - bounding rectangle for the reference element * @param {OffsetOptions} [opts={}] - offset configuration * @returns {XYCoordinates} */ function getLeftTop(off, ref, opts) { var _getLeft4 = getLeft(off, ref, opts), x = _getLeft4.x, yL = _getLeft4.y; var y = yL - off.height / 2; return { x: x, y: y }; } /** * Key/value map of position values and their respective offset calculation function * * @enum {Function} * @name offsetFunctionMap */ var fnMap = { 'bottom-center': getBottom, 'bottom-end': getBottomEnd, 'bottom-left': getBottomLeft, 'bottom-right': getBottomRight, 'bottom-start': getBottomStart, 'center-middle': getCenter, 'left-bottom': getLeftBottom, 'left-end': getLeftEnd, 'left-middle': getLeft, 'left-start': getLeftStart, 'left-top': getLeftTop, 'right-bottom': getRightBottom, 'right-end': getRightEnd, 'right-middle': getRight, 'right-start': getRightStart, 'right-top': getRightTop, 'top-center': getTop, 'top-end': getTopEnd, 'top-left': getTopLeft, 'top-right': getTopRight, 'top-start': getTopStart }; // position aliases fnMap['left'] = fnMap['left-middle']; fnMap['top'] = fnMap['top-center']; fnMap['right'] = fnMap['right-middle']; fnMap['bottom'] = fnMap['bottom-center']; fnMap['center'] = fnMap['center-middle']; var offsetFunctionMap = fnMap; var index$1 = /*#__PURE__*/Object.freeze({ __proto__: null, getCenter: getCenter, getTop: getTop, getBottom: getBottom, getLeft: getLeft, getRight: getRight, getTopLeft: getTopLeft, getTopStart: getTopStart, getTopEnd: getTopEnd, getTopRight: getTopRight, getRightTop: getRightTop, getRightStart: getRightStart, getRightEnd: getRightEnd, getRightBottom: getRightBottom, getBottomRight: getBottomRight, getBottomEnd: getBottomEnd, getBottomStart: getBottomStart, getBottomLeft: getBottomLeft, getLeftBottom: getLeftBottom, getLeftEnd: getLeftEnd, getLeftStart: getLeftStart, getLeftTop: getLeftTop, offsetFunctionMap: offsetFunctionMap }); var _account = "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='M14.3209877,1.925 C15.1937339,1.925 15.9012346,2.55266234 15.9012346,3.32692308 L15.9012346,3.32692308 L15.9012346,12.6730769 C15.9012346,13.4473377 15.1937339,14.075 14.3209877,14.075 L14.3209877,14.075 L1.67901235,14.075 C0.806266074,14.075 0.0987654321,13.4473377 0.0987654321,12.6730769 L0.0987654321,12.6730769 L0.0987654321,3.32692308 C0.0987654321,2.55266234 0.806266074,1.925 1.67901235,1.925 L1.67901235,1.925 Z M14.3817664,2.9375 L1.61823362,2.9375 C1.32452093,2.9375 1.08641975,3.14355077 1.08641975,3.39772727 L1.08641975,3.39772727 L1.08641975,12.6022727 C1.08641975,12.8564492 1.32452093,13.0625 1.61823362,13.0625 L1.61823362,13.0625 L14.3817664,13.0625 C14.6754791,13.0625 14.9135802,12.8564492 14.9135802,12.6022727 L14.9135802,12.6022727 L14.9135802,3.39772727 C14.9135802,3.14355077 14.6754791,2.9375 14.3817664,2.9375 L14.3817664,2.9375 Z M4.77352384,5.03703704 C5.77220738,5.03703704 6.58170474,5.81216876 6.58170474,6.76994417 C6.58170474,7.19249135 6.42234125,7.59457478 6.13494361,7.91051384 L6.06686574,7.97726827 L6.18291452,8.06228815 L6.29795063,8.1551932 L6.39377546,8.239955 L6.48679499,8.3291328 L6.68280502,8.53971463 L6.84791255,8.74898994 C7.04338882,9.0174286 7.20171194,9.31720762 7.32429099,9.65124215 L7.40954528,9.90823651 L7.46141134,10.0969232 L7.49780689,10.2516718 L7.53075218,10.4169046 L7.64120973,11.1604938 L1.925,11.1604938 L2.03232231,10.4343866 L2.08549306,10.1783266 C2.16989209,9.81969718 2.29870746,9.47966351 2.47147878,9.15946536 C2.70468678,8.72571269 2.97907604,8.37974802 3.29842966,8.12096395 L3.46187102,7.99883415 L3.48499074,7.98407211 L3.3953975,7.89205113 C3.2497582,7.72806948 3.13731194,7.53957972 3.06369163,7.33510034 L3.01584429,7.17887586 L2.98308476,7.01745389 L2.96874942,6.88682149 L2.96458913,6.76994417 C2.96458913,5.81233572 3.77466191,5.03703704 4.77352384,5.03703704 Z M4.37095617,8.59928737 C3.96856309,8.73434917 3.62777007,9.05942158 3.33874494,9.59698967 C3.28312664,9.70006777 3.23327796,9.80596332 3.1897883,9.91301411 L3.12939674,10.0742489 L3.08606574,10.212816 L6.47996574,10.212816 L6.42802096,10.0382764 C6.4033844,9.96589884 6.37683649,9.8959478 6.3484803,9.82860894 L6.25809002,9.63461451 L6.18186356,9.49642558 L6.12110517,9.39871902 L6.05918119,9.30839189 L5.93276374,9.14582664 L5.78285898,8.98564388 L5.71528945,8.92161261 C5.28312788,8.52644011 4.87530461,8.43034392 4.37095617,8.59928737 Z M13.315625,8.49382716 C13.4953641,8.49382716 13.6410714,8.71492111 13.6410714,8.98765432 C13.6410714,9.23008384 13.5259446,9.43171197 13.3741244,9.47352526 L13.315625,9.48148148 L8.759375,9.48148148 C8.5796359,9.48148148 8.43392857,9.26038753 8.43392857,8.98765432 C8.43392857,8.7452248 8.54905535,8.54359667 8.70087559,8.50178338 L8.759375,8.49382716 L13.315625,8.49382716 Z M4.77352384,5.98355417 C4.31383137,5.98355417 3.94261036,6.33613037 3.94261036,6.76994417 C3.94261036,7.20398751 4.31406712,7.55705396 4.77352384,7.55705396 C5.23274576,7.55705396 5.60368352,7.20421623 5.60368352,6.76994417 C5.60368352,6.33590169 5.23298146,5.98355417 4.77352384,5.98355417 Z M13.315625,6.51851852 C13.4953641,6.51851852 1