Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
modal.js618 linesDownload Raw Back to src
1/**2 * --------------------------------------------------------------------------3 * Bootstrap (v4.6.2): modal.js4 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)5 * --------------------------------------------------------------------------6 */7 8import $ from 'jquery'9import Util from './util'10 11/**12 * Constants13 */14 15const NAME = 'modal'16const VERSION = '4.6.2'17const DATA_KEY = 'bs.modal'18const EVENT_KEY = `.${DATA_KEY}`19const DATA_API_KEY = '.data-api'20const JQUERY_NO_CONFLICT = $.fn[NAME]21const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key22 23const CLASS_NAME_SCROLLABLE = 'modal-dialog-scrollable'24const CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure'25const CLASS_NAME_BACKDROP = 'modal-backdrop'26const CLASS_NAME_OPEN = 'modal-open'27const CLASS_NAME_FADE = 'fade'28const CLASS_NAME_SHOW = 'show'29const CLASS_NAME_STATIC = 'modal-static'30 31const EVENT_HIDE = `hide${EVENT_KEY}`32const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`33const EVENT_HIDDEN = `hidden${EVENT_KEY}`34const EVENT_SHOW = `show${EVENT_KEY}`35const EVENT_SHOWN = `shown${EVENT_KEY}`36const EVENT_FOCUSIN = `focusin${EVENT_KEY}`37const EVENT_RESIZE = `resize${EVENT_KEY}`38const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`39const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`40const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`41const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`42const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`43 44const SELECTOR_DIALOG = '.modal-dialog'45const SELECTOR_MODAL_BODY = '.modal-body'46const SELECTOR_DATA_TOGGLE = '[data-toggle="modal"]'47const SELECTOR_DATA_DISMISS = '[data-dismiss="modal"]'48const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'49const SELECTOR_STICKY_CONTENT = '.sticky-top'50 51const Default = {52  backdrop: true,53  keyboard: true,54  focus: true,55  show: true56}57 58const DefaultType = {59  backdrop: '(boolean|string)',60  keyboard: 'boolean',61  focus: 'boolean',62  show: 'boolean'63}64 65/**66 * Class definition67 */68 69class Modal {70  constructor(element, config) {71    this._config = this._getConfig(config)72    this._element = element73    this._dialog = element.querySelector(SELECTOR_DIALOG)74    this._backdrop = null75    this._isShown = false76    this._isBodyOverflowing = false77    this._ignoreBackdropClick = false78    this._isTransitioning = false79    this._scrollbarWidth = 080  }81 82  // Getters83  static get VERSION() {84    return VERSION85  }86 87  static get Default() {88    return Default89  }90 91  // Public92  toggle(relatedTarget) {93    return this._isShown ? this.hide() : this.show(relatedTarget)94  }95 96  show(relatedTarget) {97    if (this._isShown || this._isTransitioning) {98      return99    }100 101    const showEvent = $.Event(EVENT_SHOW, {102      relatedTarget103    })104 105    $(this._element).trigger(showEvent)106 107    if (showEvent.isDefaultPrevented()) {108      return109    }110 111    this._isShown = true112 113    if ($(this._element).hasClass(CLASS_NAME_FADE)) {114      this._isTransitioning = true115    }116 117    this._checkScrollbar()118    this._setScrollbar()119 120    this._adjustDialog()121 122    this._setEscapeEvent()123    this._setResizeEvent()124 125    $(this._element).on(126      EVENT_CLICK_DISMISS,127      SELECTOR_DATA_DISMISS,128      event => this.hide(event)129    )130 131    $(this._dialog).on(EVENT_MOUSEDOWN_DISMISS, () => {132      $(this._element).one(EVENT_MOUSEUP_DISMISS, event => {133        if ($(event.target).is(this._element)) {134          this._ignoreBackdropClick = true135        }136      })137    })138 139    this._showBackdrop(() => this._showElement(relatedTarget))140  }141 142  hide(event) {143    if (event) {144      event.preventDefault()145    }146 147    if (!this._isShown || this._isTransitioning) {148      return149    }150 151    const hideEvent = $.Event(EVENT_HIDE)152 153    $(this._element).trigger(hideEvent)154 155    if (!this._isShown || hideEvent.isDefaultPrevented()) {156      return157    }158 159    this._isShown = false160    const transition = $(this._element).hasClass(CLASS_NAME_FADE)161 162    if (transition) {163      this._isTransitioning = true164    }165 166    this._setEscapeEvent()167    this._setResizeEvent()168 169    $(document).off(EVENT_FOCUSIN)170 171    $(this._element).removeClass(CLASS_NAME_SHOW)172 173    $(this._element).off(EVENT_CLICK_DISMISS)174    $(this._dialog).off(EVENT_MOUSEDOWN_DISMISS)175 176    if (transition) {177      const transitionDuration = Util.getTransitionDurationFromElement(this._element)178 179      $(this._element)180        .one(Util.TRANSITION_END, event => this._hideModal(event))181        .emulateTransitionEnd(transitionDuration)182    } else {183      this._hideModal()184    }185  }186 187  dispose() {188    [window, this._element, this._dialog]189      .forEach(htmlElement => $(htmlElement).off(EVENT_KEY))190 191    /**192     * `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API`193     * Do not move `document` in `htmlElements` array194     * It will remove `EVENT_CLICK_DATA_API` event that should remain195     */196    $(document).off(EVENT_FOCUSIN)197 198    $.removeData(this._element, DATA_KEY)199 200    this._config = null201    this._element = null202    this._dialog = null203    this._backdrop = null204    this._isShown = null205    this._isBodyOverflowing = null206    this._ignoreBackdropClick = null207    this._isTransitioning = null208    this._scrollbarWidth = null209  }210 211  handleUpdate() {212    this._adjustDialog()213  }214 215  // Private216  _getConfig(config) {217    config = {218      ...Default,219      ...config220    }221    Util.typeCheckConfig(NAME, config, DefaultType)222    return config223  }224 225  _triggerBackdropTransition() {226    const hideEventPrevented = $.Event(EVENT_HIDE_PREVENTED)227 228    $(this._element).trigger(hideEventPrevented)229    if (hideEventPrevented.isDefaultPrevented()) {230      return231    }232 233    const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight234 235    if (!isModalOverflowing) {236      this._element.style.overflowY = 'hidden'237    }238 239    this._element.classList.add(CLASS_NAME_STATIC)240 241    const modalTransitionDuration = Util.getTransitionDurationFromElement(this._dialog)242    $(this._element).off(Util.TRANSITION_END)243 244    $(this._element).one(Util.TRANSITION_END, () => {245      this._element.classList.remove(CLASS_NAME_STATIC)246      if (!isModalOverflowing) {247        $(this._element).one(Util.TRANSITION_END, () => {248          this._element.style.overflowY = ''249        })250          .emulateTransitionEnd(this._element, modalTransitionDuration)251      }252    })253      .emulateTransitionEnd(modalTransitionDuration)254    this._element.focus()255  }256 257  _showElement(relatedTarget) {258    const transition = $(this._element).hasClass(CLASS_NAME_FADE)259    const modalBody = this._dialog ? this._dialog.querySelector(SELECTOR_MODAL_BODY) : null260 261    if (!this._element.parentNode ||262        this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {263      // Don't move modal's DOM position264      document.body.appendChild(this._element)265    }266 267    this._element.style.display = 'block'268    this._element.removeAttribute('aria-hidden')269    this._element.setAttribute('aria-modal', true)270    this._element.setAttribute('role', 'dialog')271 272    if ($(this._dialog).hasClass(CLASS_NAME_SCROLLABLE) && modalBody) {273      modalBody.scrollTop = 0274    } else {275      this._element.scrollTop = 0276    }277 278    if (transition) {279      Util.reflow(this._element)280    }281 282    $(this._element).addClass(CLASS_NAME_SHOW)283 284    if (this._config.focus) {285      this._enforceFocus()286    }287 288    const shownEvent = $.Event(EVENT_SHOWN, {289      relatedTarget290    })291 292    const transitionComplete = () => {293      if (this._config.focus) {294        this._element.focus()295      }296 297      this._isTransitioning = false298      $(this._element).trigger(shownEvent)299    }300 301    if (transition) {302      const transitionDuration = Util.getTransitionDurationFromElement(this._dialog)303 304      $(this._dialog)305        .one(Util.TRANSITION_END, transitionComplete)306        .emulateTransitionEnd(transitionDuration)307    } else {308      transitionComplete()309    }310  }311 312  _enforceFocus() {313    $(document)314      .off(EVENT_FOCUSIN) // Guard against infinite focus loop315      .on(EVENT_FOCUSIN, event => {316        if (document !== event.target &&317            this._element !== event.target &&318            $(this._element).has(event.target).length === 0) {319          this._element.focus()320        }321      })322  }323 324  _setEscapeEvent() {325    if (this._isShown) {326      $(this._element).on(EVENT_KEYDOWN_DISMISS, event => {327        if (this._config.keyboard && event.which === ESCAPE_KEYCODE) {328          event.preventDefault()329          this.hide()330        } else if (!this._config.keyboard && event.which === ESCAPE_KEYCODE) {331          this._triggerBackdropTransition()332        }333      })334    } else if (!this._isShown) {335      $(this._element).off(EVENT_KEYDOWN_DISMISS)336    }337  }338 339  _setResizeEvent() {340    if (this._isShown) {341      $(window).on(EVENT_RESIZE, event => this.handleUpdate(event))342    } else {343      $(window).off(EVENT_RESIZE)344    }345  }346 347  _hideModal() {348    this._element.style.display = 'none'349    this._element.setAttribute('aria-hidden', true)350    this._element.removeAttribute('aria-modal')351    this._element.removeAttribute('role')352    this._isTransitioning = false353    this._showBackdrop(() => {354      $(document.body).removeClass(CLASS_NAME_OPEN)355      this._resetAdjustments()356      this._resetScrollbar()357      $(this._element).trigger(EVENT_HIDDEN)358    })359  }360 361  _removeBackdrop() {362    if (this._backdrop) {363      $(this._backdrop).remove()364      this._backdrop = null365    }366  }367 368  _showBackdrop(callback) {369    const animate = $(this._element).hasClass(CLASS_NAME_FADE) ?370      CLASS_NAME_FADE : ''371 372    if (this._isShown && this._config.backdrop) {373      this._backdrop = document.createElement('div')374      this._backdrop.className = CLASS_NAME_BACKDROP375 376      if (animate) {377        this._backdrop.classList.add(animate)378      }379 380      $(this._backdrop).appendTo(document.body)381 382      $(this._element).on(EVENT_CLICK_DISMISS, event => {383        if (this._ignoreBackdropClick) {384          this._ignoreBackdropClick = false385          return386        }387 388        if (event.target !== event.currentTarget) {389          return390        }391 392        if (this._config.backdrop === 'static') {393          this._triggerBackdropTransition()394        } else {395          this.hide()396        }397      })398 399      if (animate) {400        Util.reflow(this._backdrop)401      }402 403      $(this._backdrop).addClass(CLASS_NAME_SHOW)404 405      if (!callback) {406        return407      }408 409      if (!animate) {410        callback()411        return412      }413 414      const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)415 416      $(this._backdrop)417        .one(Util.TRANSITION_END, callback)418        .emulateTransitionEnd(backdropTransitionDuration)419    } else if (!this._isShown && this._backdrop) {420      $(this._backdrop).removeClass(CLASS_NAME_SHOW)421 422      const callbackRemove = () => {423        this._removeBackdrop()424        if (callback) {425          callback()426        }427      }428 429      if ($(this._element).hasClass(CLASS_NAME_FADE)) {430        const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)431 432        $(this._backdrop)433          .one(Util.TRANSITION_END, callbackRemove)434          .emulateTransitionEnd(backdropTransitionDuration)435      } else {436        callbackRemove()437      }438    } else if (callback) {439      callback()440    }441  }442 443  // ----------------------------------------------------------------------444  // the following methods are used to handle overflowing modals445  // todo (fat): these should probably be refactored out of modal.js446  // ----------------------------------------------------------------------447 448  _adjustDialog() {449    const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight450 451    if (!this._isBodyOverflowing && isModalOverflowing) {452      this._element.style.paddingLeft = `${this._scrollbarWidth}px`453    }454 455    if (this._isBodyOverflowing && !isModalOverflowing) {456      this._element.style.paddingRight = `${this._scrollbarWidth}px`457    }458  }459 460  _resetAdjustments() {461    this._element.style.paddingLeft = ''462    this._element.style.paddingRight = ''463  }464 465  _checkScrollbar() {466    const rect = document.body.getBoundingClientRect()467    this._isBodyOverflowing = Math.round(rect.left + rect.right) < window.innerWidth468    this._scrollbarWidth = this._getScrollbarWidth()469  }470 471  _setScrollbar() {472    if (this._isBodyOverflowing) {473      // Note: DOMNode.style.paddingRight returns the actual value or '' if not set474      //   while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set475      const fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT))476      const stickyContent = [].slice.call(document.querySelectorAll(SELECTOR_STICKY_CONTENT))477 478      // Adjust fixed content padding479      $(fixedContent).each((index, element) => {480        const actualPadding = element.style.paddingRight481        const calculatedPadding = $(element).css('padding-right')482        $(element)483          .data('padding-right', actualPadding)484          .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)485      })486 487      // Adjust sticky content margin488      $(stickyContent).each((index, element) => {489        const actualMargin = element.style.marginRight490        const calculatedMargin = $(element).css('margin-right')491        $(element)492          .data('margin-right', actualMargin)493          .css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`)494      })495 496      // Adjust body padding497      const actualPadding = document.body.style.paddingRight498      const calculatedPadding = $(document.body).css('padding-right')499      $(document.body)500        .data('padding-right', actualPadding)501        .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`)502    }503 504    $(document.body).addClass(CLASS_NAME_OPEN)505  }506 507  _resetScrollbar() {508    // Restore fixed content padding509    const fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT))510    $(fixedContent).each((index, element) => {511      const padding = $(element).data('padding-right')512      $(element).removeData('padding-right')513      element.style.paddingRight = padding ? padding : ''514    })515 516    // Restore sticky content517    const elements = [].slice.call(document.querySelectorAll(`${SELECTOR_STICKY_CONTENT}`))518    $(elements).each((index, element) => {519      const margin = $(element).data('margin-right')520      if (typeof margin !== 'undefined') {521        $(element).css('margin-right', margin).removeData('margin-right')522      }523    })524 525    // Restore body padding526    const padding = $(document.body).data('padding-right')527    $(document.body).removeData('padding-right')528    document.body.style.paddingRight = padding ? padding : ''529  }530 531  _getScrollbarWidth() { // thx d.walsh532    const scrollDiv = document.createElement('div')533    scrollDiv.className = CLASS_NAME_SCROLLBAR_MEASURER534    document.body.appendChild(scrollDiv)535    const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth536    document.body.removeChild(scrollDiv)537    return scrollbarWidth538  }539 540  // Static541  static _jQueryInterface(config, relatedTarget) {542    return this.each(function () {543      let data = $(this).data(DATA_KEY)544      const _config = {545        ...Default,546        ...$(this).data(),547        ...(typeof config === 'object' && config ? config : {})548      }549 550      if (!data) {551        data = new Modal(this, _config)552        $(this).data(DATA_KEY, data)553      }554 555      if (typeof config === 'string') {556        if (typeof data[config] === 'undefined') {557          throw new TypeError(`No method named "${config}"`)558        }559 560        data[config](relatedTarget)561      } else if (_config.show) {562        data.show(relatedTarget)563      }564    })565  }566}567 568/**569 * Data API implementation570 */571 572$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {573  let target574  const selector = Util.getSelectorFromElement(this)575 576  if (selector) {577    target = document.querySelector(selector)578  }579 580  const config = $(target).data(DATA_KEY) ?581    'toggle' : {582      ...$(target).data(),583      ...$(this).data()584    }585 586  if (this.tagName === 'A' || this.tagName === 'AREA') {587    event.preventDefault()588  }589 590  const $target = $(target).one(EVENT_SHOW, showEvent => {591    if (showEvent.isDefaultPrevented()) {592      // Only register focus restorer if modal will actually get shown593      return594    }595 596    $target.one(EVENT_HIDDEN, () => {597      if ($(this).is(':visible')) {598        this.focus()599      }600    })601  })602 603  Modal._jQueryInterface.call($(target), config, this)604})605 606/**607 * jQuery608 */609 610$.fn[NAME] = Modal._jQueryInterface611$.fn[NAME].Constructor = Modal612$.fn[NAME].noConflict = () => {613  $.fn[NAME] = JQUERY_NO_CONFLICT614  return Modal._jQueryInterface615}616 617export default Modal618 
codekingpro/portable-devtools · Team Ai