carbon-custom-elements
Version:
A Carbon Design System variant that's as easy to use as native HTML elements, with no framework tax, no framework silo.
1 lines • 13 kB
Source Map (JSON)
{"version":3,"sources":["components/floating-menu/floating-menu.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC,OAAO,MAAM,MAAM,+BAA+B,CAAC;AACnD,OAAO,qBAAqB,MAAM,yBAAyB,CAAC;AAE5D;;GAEG;AACH,oBAAY,gCAAgC;IAC1C;;OAEG;IACH,GAAG,QAAQ;IAEX;;OAEG;IACH,GAAG,QAAQ;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;OAEG;IACH,SAAS,EAAE,gCAAgC,CAAC;IAE5C;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,oBAAY,uBAAuB;IACjC;;OAEG;IACH,KAAK,UAAU;IAEf;;OAEG;IACH,MAAM,WAAW;IAEjB;;OAEG;IACH,GAAG,QAAQ;CACZ;AAED;;GAEG;AACH,oBAAY,uBAAuB;IACjC;;OAEG;IACH,IAAI,SAAS;IAEb;;OAEG;IACH,GAAG,QAAQ;IAEX;;OAEG;IACH,KAAK,UAAU;IAEf;;OAEG;IACH,MAAM,WAAW;CAClB;AAED;;GAEG;AACH,oBAAY,6BAA6B;IACvC;;OAEG;IACH,UAAU,eAAe;IAEzB;;OAEG;IACH,QAAQ,aAAa;CACtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBD;;GAEG;AACH,uBAAe,cAAe,SAAQ,mBAAyC;IAC7E;;OAEG;IACH,OAAO,CAAC,qBAAqB,CAAuB;IAEpD;;OAEG;IACH,OAAO,CAAC,wBAAwB,CAAuB;IAEvD;;OAEG;IAGH,OAAO,CAAC,eAAe,CAOpB;IAIH,OAAO,CAAC,WAAW,CAQjB;IAEF;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAAQ;IAEtD;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,uBAAuB,CAAC;IAE5C;;OAEG;IACH,QAAQ,CAAC,SAAS,EAAE,uBAAuB,CAAC;IAE5C;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAEvB;;OAEG;IACH,IAAI,kBAAkB,kCAOrB;IAED;;OAEG;IACH,IAAI,SAAS,YAEZ;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,oBAAoB,CA2EnC;IAED,gBAAgB;IAIhB,oBAAoB;IASpB,OAAO,CAAC,iBAAiB,KAAA;IA6BzB;;OAEG;IACH,MAAM,CAAC,aAAa,UAAQ;IAE5B;;OAEG;IACH,MAAM,KAAK,iBAAiB,WAE3B;CACF;AAED,eAAe,cAAc,CAAC","file":"floating-menu.d.ts","sourcesContent":["/**\n * @license\n *\n * Copyright IBM Corp. 2019, 2020\n *\n * This source code is licensed under the Apache-2.0 license found in the\n * LICENSE file in the root directory of this source tree.\n */\n\nimport { LitElement } from 'lit-element';\nimport HostListener from '../../globals/decorators/host-listener';\nimport FocusMixin from '../../globals/mixins/focus';\nimport HostListenerMixin from '../../globals/mixins/host-listener';\nimport Handle from '../../globals/internal/handle';\nimport BXFloatingMenuTrigger from './floating-menu-trigger';\n\n/**\n * The LTR/RTL direction used for positioning floating menu.\n */\nexport enum FLOATING_MENU_POSITION_DIRECTION {\n /**\n * LTR.\n */\n LTR = 'ltr',\n\n /**\n * RTL.\n */\n RTL = 'rtl',\n}\n\n/**\n * Position of floating menu, or trigger button of floating menu.\n */\nexport interface FloatingMenuPosition {\n /**\n * The LTR/RTL direction used for positioning floating menu.\n */\n direction: FLOATING_MENU_POSITION_DIRECTION;\n\n /**\n * The start position (Left position for LTR, right position for RTL).\n */\n start: number;\n\n /**\n * The top position.\n */\n top: number;\n}\n\n/**\n * The alignment choices of floating menu.\n */\nexport enum FLOATING_MENU_ALIGNMENT {\n /**\n * Align the top/left position menu body to the one of its trigger button.\n */\n START = 'start',\n\n /**\n * Align the center position menu body to the one of its trigger button.\n */\n CENTER = 'center',\n\n /**\n * Align the bottom/right position menu body to the one of its trigger button.\n */\n END = 'end',\n}\n\n/**\n * The direction/positioning/orientation choices of floating menu.\n */\nexport enum FLOATING_MENU_DIRECTION {\n /**\n * Put menu body at the left of its trigger button.\n */\n LEFT = 'left',\n\n /**\n * Put menu body at the top of its trigger button.\n */\n TOP = 'top',\n\n /**\n * Put menu body at the right of its trigger button.\n */\n RIGHT = 'right',\n\n /**\n * Put menu body at the bottom of its trigger button.\n */\n BOTTOM = 'bottom',\n}\n\n/**\n * The group of the direction/positioning/orientation choices of floating menu.\n */\nexport enum FLOATING_MENU_DIRECTION_GROUP {\n /**\n * Put menu body at the left/right of its trigger button.\n */\n HORIZONTAL = 'horizontal',\n\n /**\n * Put menu body at the top/bottom of its trigger button.\n */\n VERTICAL = 'vertical',\n}\n\n/**\n * Observes resize of the given element with the given resize observer.\n * @param observer The resize observer.\n * @param elem The element to observe the resize.\n */\nconst observeResize = (observer: ResizeObserver, elem: Element) => {\n observer.observe(elem);\n return {\n release() {\n observer.unobserve(elem);\n return null;\n },\n } as Handle;\n};\n\n/**\n * Floating menu.\n */\nabstract class BXFloatingMenu extends HostListenerMixin(FocusMixin(LitElement)) {\n /**\n * The handle for observing resize of the element containing the trigger button.\n */\n private _hObserveResizeParent: Handle | null = null;\n\n /**\n * The handle for observing resize of the floating menu container.\n */\n private _hObserveResizeContainer: Handle | null = null;\n\n /**\n * The `ResizeObserver` instance for observing element resizes for re-positioning floating menu position.\n */\n // TODO: Wait for `.d.ts` update to support `ResizeObserver`\n // @ts-ignore\n private _resizeObserver = new ResizeObserver(() => {\n const { container, open, parent, position } = this;\n if (container && open && parent) {\n const { direction, start, top } = position;\n this.style[direction !== FLOATING_MENU_POSITION_DIRECTION.RTL ? 'left' : 'right'] = `${start}px`;\n this.style.top = `${top}px`;\n }\n });\n\n @HostListener('focusout')\n // @ts-ignore: The decorator refers to this method but TS thinks this method is not referred to\n private _handleBlur = ({ relatedTarget }: FocusEvent) => {\n if (!this.contains(relatedTarget as Node)) {\n const { parent } = this;\n if (parent) {\n parent.open = false;\n HTMLElement.prototype.focus.call(this.parent); // SVGElement in IE11 does not have `.focus()` method\n }\n }\n };\n\n /**\n * The DOM element, typically a custom element in this library, launching this floating menu.\n */\n protected parent: BXFloatingMenuTrigger | null = null;\n\n /**\n * How the menu is aligned to the trigger button.\n */\n abstract alignment: FLOATING_MENU_ALIGNMENT;\n\n /**\n * The menu direction.\n */\n abstract direction: FLOATING_MENU_DIRECTION;\n\n /**\n * `true` if the menu should be open.\n */\n abstract open: boolean;\n\n /**\n * The horizontal/vertical direction with regard to how the menu is aligned to the trigger button.\n */\n get alignmentDirection() {\n return {\n [FLOATING_MENU_DIRECTION.LEFT]: FLOATING_MENU_DIRECTION_GROUP.VERTICAL,\n [FLOATING_MENU_DIRECTION.TOP]: FLOATING_MENU_DIRECTION_GROUP.HORIZONTAL,\n [FLOATING_MENU_DIRECTION.RIGHT]: FLOATING_MENU_DIRECTION_GROUP.VERTICAL,\n [FLOATING_MENU_DIRECTION.BOTTOM]: FLOATING_MENU_DIRECTION_GROUP.HORIZONTAL,\n }[this.direction];\n }\n\n /**\n * The DOM element to put this menu into.\n */\n get container() {\n return this.closest((this.constructor as typeof BXFloatingMenu).selectorContainer) || this.ownerDocument!.body;\n }\n\n /**\n * The position of this floating menu.\n */\n get position(): FloatingMenuPosition {\n const { triggerPosition } = this.parent!;\n if (!triggerPosition) {\n throw new TypeError('Missing information of trigger button position.');\n }\n\n const { container } = this;\n const { left: refLeft = 0, top: refTop = 0, right: refRight = 0, bottom: refBottom = 0 } = triggerPosition;\n const { width, height } = this.getBoundingClientRect();\n const { left: containerLeft = 0, right: containerRight = 0, top: containerTop = 0 } = container.getBoundingClientRect();\n const refCenterHorizontal = (refLeft + refRight) / 2;\n const refCenterVertical = (refTop + refBottom) / 2;\n\n const containerComputedStyle = container.ownerDocument!.defaultView!.getComputedStyle(container);\n const positionDirection = containerComputedStyle.getPropertyValue('direction') as FLOATING_MENU_POSITION_DIRECTION;\n const isRtl = positionDirection === FLOATING_MENU_POSITION_DIRECTION.RTL;\n const containerStartFromViewport = !isRtl ? containerLeft : container.ownerDocument!.defaultView!.innerWidth - containerRight;\n const refStartFromContainer = !isRtl ? refLeft - containerLeft : containerRight - refRight;\n const refEndFromContainer = !isRtl ? refRight - containerLeft : containerRight - refLeft;\n const refCenterHorizontalFromContainer = !isRtl ? refCenterHorizontal - containerLeft : containerRight - refCenterHorizontal;\n const refTopFromContainer = refTop - containerTop;\n const refCenterVerticalFromContainer = refCenterVertical - containerTop;\n\n if (\n (container !== this.ownerDocument!.body || containerStartFromViewport !== 0 || containerTop !== 0) &&\n containerComputedStyle.getPropertyValue('position') === 'static'\n ) {\n throw new Error('Floating menu container must not have `position:static`.');\n }\n\n const { alignment, alignmentDirection, direction } = this;\n if (Object.values(FLOATING_MENU_ALIGNMENT).indexOf(alignment) < 0) {\n throw new Error(`Wrong menu alignment: ${alignment}`);\n }\n if (Object.values(FLOATING_MENU_DIRECTION).indexOf(direction) < 0) {\n throw new Error(`Wrong menu position direction: ${direction}`);\n }\n\n const alignmentStart = {\n [FLOATING_MENU_DIRECTION_GROUP.HORIZONTAL]: {\n [FLOATING_MENU_ALIGNMENT.START]: () => refStartFromContainer,\n [FLOATING_MENU_ALIGNMENT.CENTER]: () => refCenterHorizontalFromContainer - width / 2,\n [FLOATING_MENU_ALIGNMENT.END]: () => refEndFromContainer - width,\n },\n [FLOATING_MENU_DIRECTION_GROUP.VERTICAL]: {\n [FLOATING_MENU_ALIGNMENT.START]: () => refTopFromContainer,\n [FLOATING_MENU_ALIGNMENT.CENTER]: () => refCenterVerticalFromContainer - height / 2,\n [FLOATING_MENU_ALIGNMENT.END]: () => refBottom - height,\n },\n }[alignmentDirection][alignment]();\n\n const { start, top } = {\n [FLOATING_MENU_DIRECTION.LEFT]: () => ({\n start: refStartFromContainer - width,\n top: alignmentStart,\n }),\n [FLOATING_MENU_DIRECTION.TOP]: () => ({\n start: alignmentStart,\n top: refTopFromContainer - height,\n }),\n [FLOATING_MENU_DIRECTION.RIGHT]: () => ({\n start: refEndFromContainer,\n top: alignmentStart,\n }),\n [FLOATING_MENU_DIRECTION.BOTTOM]: () => ({\n start: alignmentStart,\n top: refBottom,\n }),\n }[direction]();\n\n return {\n direction: positionDirection,\n start,\n top,\n };\n }\n\n createRenderRoot() {\n return this.attachShadow({ mode: 'open', delegatesFocus: true });\n }\n\n disconnectedCallback() {\n if (this._hObserveResizeContainer) {\n this._hObserveResizeContainer = this._hObserveResizeContainer.release();\n }\n if (this._hObserveResizeParent) {\n this._hObserveResizeParent = this._hObserveResizeParent.release();\n }\n }\n\n updated(changedProperties) {\n const { container, open, parent } = this;\n if ((changedProperties.has('alignment') || changedProperties.has('direction') || changedProperties.has('open')) && open) {\n if (!parent) {\n this.parent = this.parentElement as BXFloatingMenuTrigger;\n container.appendChild(this);\n }\n // Note: `this.position` cannot be referenced until `this.parent` is set\n const { direction, start, top } = this.position;\n this.style[direction !== FLOATING_MENU_POSITION_DIRECTION.RTL ? 'left' : 'right'] = `${start}px`;\n this.style.top = `${top}px`;\n }\n if (changedProperties.has('open')) {\n if (this._hObserveResizeContainer) {\n this._hObserveResizeContainer = this._hObserveResizeContainer.release();\n }\n if (this._hObserveResizeParent) {\n this._hObserveResizeParent = this._hObserveResizeParent.release();\n }\n if (open) {\n const { parentElement } = this.parent ?? {};\n this._hObserveResizeContainer = observeResize(this._resizeObserver, container);\n if (parentElement) {\n this._hObserveResizeParent = observeResize(this._resizeObserver, parentElement);\n }\n }\n }\n }\n\n /**\n * A constant indicating that this class is a floating menu.\n */\n static FLOATING_MENU = true;\n\n /**\n * The CSS selector to find the element to put this floating menu in.\n */\n static get selectorContainer() {\n return '[data-floating-menu-container],bx-modal';\n }\n}\n\nexport default BXFloatingMenu;\n"]}