From ee9ce8a87ba6ff8829582f956531388a22033bd6 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Wed, 26 May 2021 12:43:03 -0400 Subject: [PATCH] remove popover util --- docs/resources/contributing.md | 4 - src/internal/popover.ts | 167 --------------------------------- 2 files changed, 171 deletions(-) delete mode 100644 src/internal/popover.ts diff --git a/docs/resources/contributing.md b/docs/resources/contributing.md index 3cdeac72f..d5ed6b5d3 100644 --- a/docs/resources/contributing.md +++ b/docs/resources/contributing.md @@ -241,7 +241,3 @@ Form controls should support validation through the following conventions: - All form controls must have a `reportValidity()` method that report their validity during form submission - All form controls should mirror their native validation attributes such as `required`, `pattern`, `minlength`, `maxlength`, etc. when possible - All form controls must be serialized by `` - -### Positioning Popovers - -Shoelace uses an internal popover utility for dropdowns, tooltips, etc. This is a light abstraction of Popper.js designed to make positioning and transitioning things easy and consistent throughout the library. When possible, use this utility instead of relying on Popper directly. See `src/utilities/popover.ts` for details. diff --git a/src/internal/popover.ts b/src/internal/popover.ts deleted file mode 100644 index 27c347480..000000000 --- a/src/internal/popover.ts +++ /dev/null @@ -1,167 +0,0 @@ -// -// A positioning utility for popovers that handles show/hide/transitionEnd events with simple callbacks. -// -// Powered by Popper.js. -// -// NOTE: -// -// - The popover MUST have at least one property that transitions, otherwise transitionEnd won't fire and the popover -// won't be hidden. If transitions are delegated to a child element, set the `transitionElement` property accordingly. -// -// - When the popover is shown, it's assigned `PopoverOptions.visibleClass`. You can use this class to provide different -// transitions for show/hide. -// -// - Popper uses `translate3d` to position elements, so adding a transition to the `transform` property may have an -// undesired effect when the element is shown and when its placement changes. -// -import { Instance as PopperInstance, createPopper } from '@popperjs/core/dist/esm'; - -export default class Popover { - anchor: HTMLElement; - isVisible: boolean; - popover: HTMLElement; - popper: PopperInstance; - options: PopoverOptions; - - constructor(anchor: HTMLElement, popover: HTMLElement, options?: PopoverOptions) { - this.handleTransitionEnd = this.handleTransitionEnd.bind(this); - - this.anchor = anchor; - this.popover = popover; - this.options = Object.assign( - { - skidding: 0, - distance: 0, - placement: 'bottom-start', - strategy: 'absolute', - transitionElement: this.popover, - visibleClass: 'popover-visible', - onAfterShow: () => {}, - onAfterHide: () => {}, - onTransitionEnd: () => {} - }, - options - ); - - this.isVisible = false; - this.popover.hidden = true; - this.popover.classList.remove(this.options.visibleClass!); - - this.popover.addEventListener('transitionend', this.handleTransitionEnd); - } - - handleTransitionEnd(event: TransitionEvent) { - const target = event.target as HTMLElement; - - // Make sure the transition event originates from from the correct element, and not one that has bubbled up - if (target === this.options.transitionElement) { - // This is called before the element is hidden so users can do things like reset scroll. It will fire once for - // every transition property. Use `event.propertyName` to determine which property has finished transitioning. - this.options.onTransitionEnd!.call(this, event); - - // Make sure we only do this once, since transitionend will fire for every transition - if (!this.isVisible && !this.popover.hidden) { - this.popover.hidden = true; - this.popover.classList.remove(this.options.visibleClass!); - this.options.onAfterHide!.call(this); - } - } - } - - destroy() { - this.popover.removeEventListener('transitionend', this.handleTransitionEnd); - - if (this.popper) { - this.popper.destroy(); - } - } - - show() { - this.isVisible = true; - this.popover.hidden = false; - this.popover.clientWidth; // force reflow - requestAnimationFrame(() => this.popover.classList.add(this.options.visibleClass!)); - - if (this.popper) { - this.popper.destroy(); - } - - this.popper = createPopper(this.anchor, this.popover, { - placement: this.options.placement, - strategy: this.options.strategy, - modifiers: [ - { - name: 'flip', - options: { - boundary: 'viewport' - } - }, - { - name: 'offset', - options: { - offset: [this.options.skidding, this.options.distance] - } - } - ] - }); - - this.popover.addEventListener('transitionend', () => this.options.onAfterShow!.call(this), { once: true }); - - // Reposition the menu after it appears in case a modifier kicked in - requestAnimationFrame(() => this.popper.update()); - } - - hide() { - // Apply the hidden styles and wait for the transition before hiding completely - this.isVisible = false; - this.popover.classList.remove(this.options.visibleClass!); - } - - reposition() { - this.popper.update(); - } - - setOptions(options: PopoverOptions) { - this.options = Object.assign(this.options, options); - this.isVisible - ? this.popover.classList.add(this.options.visibleClass!) - : this.popover.classList.remove(this.options.visibleClass!); - - // Update popper options - if (this.popper) { - this.popper.setOptions({ - placement: this.options.placement, - strategy: this.options.strategy - }); - - requestAnimationFrame(() => this.popper.update()); - } - } -} - -interface PopoverOptions { - distance?: number; - placement?: - | 'auto' - | 'auto-start' - | 'auto-end' - | 'top' - | 'top-start' - | 'top-end' - | 'bottom' - | 'bottom-start' - | 'bottom-end' - | 'right' - | 'right-start' - | 'right-end' - | 'left' - | 'left-start' - | 'left-end'; - skidding?: number; - strategy?: 'absolute' | 'fixed'; - transitionElement?: HTMLElement; - visibleClass?: string; - onAfterShow?: () => any; - onAfterHide?: () => any; - onTransitionEnd?: (event: TransitionEvent) => any; -}