From 531529a13c3ec7858ccb4092b98311f797daabd3 Mon Sep 17 00:00:00 2001 From: Bartosz-Do Date: Thu, 24 Sep 2026 18:16:22 +0200 Subject: [PATCH] Restore jQuery bridge for Bootstrap component events (#3168) --- .changeset/fix-jquery-bootstrap-events.md | 5 +++ core/js/src/bootstrap/alert.ts | 20 ++++++++++ core/js/src/bootstrap/button.ts | 14 +++++++ core/js/src/bootstrap/carousel.ts | 24 ++++++++++- core/js/src/bootstrap/collapse.ts | 25 +++++++++++- core/js/src/bootstrap/dom/event-handler.ts | 35 +++++++++++++++- core/js/src/bootstrap/dropdown.ts | 21 +++++++++- core/js/src/bootstrap/modal.ts | 21 +++++++++- core/js/src/bootstrap/offcanvas.ts | 21 +++++++++- core/js/src/bootstrap/popover.ts | 20 ++++++++++ core/js/src/bootstrap/scrollspy.ts | 21 +++++++++- core/js/src/bootstrap/tab.ts | 21 +++++++++- core/js/src/bootstrap/toast.ts | 20 +++++++++- core/js/src/bootstrap/tooltip.ts | 22 ++++++++++- core/js/src/bootstrap/types.ts | 29 ++++++++++++++ core/js/src/bootstrap/util/index.ts | 46 +++++++++++++++++++++- core/js/src/global.d.ts | 13 ++++-- 17 files changed, 359 insertions(+), 19 deletions(-) create mode 100644 .changeset/fix-jquery-bootstrap-events.md diff --git a/.changeset/fix-jquery-bootstrap-events.md b/.changeset/fix-jquery-bootstrap-events.md new file mode 100644 index 000000000..1198b1e3d --- /dev/null +++ b/.changeset/fix-jquery-bootstrap-events.md @@ -0,0 +1,5 @@ +--- +"@tabler/core": patch +--- + +Fixed jQuery `.on()` listeners not receiving Bootstrap events like `show.bs.dropdown` in all components. diff --git a/core/js/src/bootstrap/alert.ts b/core/js/src/bootstrap/alert.ts index 90ba20bdb..7abcb49e4 100644 --- a/core/js/src/bootstrap/alert.ts +++ b/core/js/src/bootstrap/alert.ts @@ -8,6 +8,8 @@ import BaseComponent from './base-component' import EventHandler from './dom/event-handler.js' import { enableDismissTrigger } from './util/component-functions' +import { defineJQueryPlugin } from './util/index.js' +import type { JQueryCollectionLike } from './types' const NAME = 'alert' const DATA_KEY = 'bs.alert' @@ -41,8 +43,26 @@ class Alert extends BaseComponent { EventHandler.trigger(this._element, EVENT_CLOSED) this.dispose() } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Alert.getOrCreateInstance(this) as unknown as Record unknown> + + if (typeof config !== 'string') { + return + } + + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`) + } + + data[config](this) + }) + } } enableDismissTrigger(Alert, 'close') +defineJQueryPlugin(Alert) + export default Alert diff --git a/core/js/src/bootstrap/button.ts b/core/js/src/bootstrap/button.ts index ea9fd2a61..8ae26d40c 100644 --- a/core/js/src/bootstrap/button.ts +++ b/core/js/src/bootstrap/button.ts @@ -7,6 +7,8 @@ import BaseComponent from './base-component' import EventHandler from './dom/event-handler.js' +import { defineJQueryPlugin } from './util/index.js' +import type { JQueryCollectionLike } from './types' const NAME = 'button' const DATA_KEY = 'bs.button' @@ -27,6 +29,16 @@ class Button extends BaseComponent { toggle(): void { this._element.setAttribute('aria-pressed', String(this._element.classList.toggle(CLASS_NAME_ACTIVE))) } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Button.getOrCreateInstance(this) as Button + + if (config === 'toggle') { + data.toggle() + } + }) + } } EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, (event: Event) => { @@ -41,4 +53,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, (event: Ev data.toggle() }) +defineJQueryPlugin(Button) + export default Button diff --git a/core/js/src/bootstrap/carousel.ts b/core/js/src/bootstrap/carousel.ts index d6657d1c1..a21c0c99e 100644 --- a/core/js/src/bootstrap/carousel.ts +++ b/core/js/src/bootstrap/carousel.ts @@ -9,7 +9,8 @@ import BaseComponent from './base-component' import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' import SelectorEngine from './dom/selector-engine' -import { getNextActiveElement, isRTL, isVisible, reflow, triggerTransitionEnd } from './util/index' +import { defineJQueryPlugin, getNextActiveElement, isRTL, isVisible, reflow, triggerTransitionEnd } from './util/index' +import type { ComponentConfig as BaseComponentConfig, JQueryCollectionLike } from './types' import Swipe from './util/swipe' type ComponentConfig = { @@ -393,6 +394,25 @@ class Carousel extends BaseComponent { return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Carousel.getOrCreateInstance(this, config as BaseComponentConfig) as unknown as Record unknown> + + if (typeof config === 'number') { + data.to(config) + return + } + + if (typeof config === 'string') { + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`) + } + + data[config]() + } + }) + } } EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (this: HTMLElement, event: Event) { @@ -431,4 +451,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { } }) +defineJQueryPlugin(Carousel) + export default Carousel diff --git a/core/js/src/bootstrap/collapse.ts b/core/js/src/bootstrap/collapse.ts index 0da932623..b19a79187 100644 --- a/core/js/src/bootstrap/collapse.ts +++ b/core/js/src/bootstrap/collapse.ts @@ -8,9 +8,9 @@ import BaseComponent from './base-component' import EventHandler from './dom/event-handler' import SelectorEngine from './dom/selector-engine' -import { getElement, reflow } from './util/index' +import { defineJQueryPlugin, getElement, reflow } from './util/index' import type { DelegatedEvent } from './dom/event-handler' -import type { ElementSelector } from './types' +import type { ComponentConfig as BaseComponentConfig, ElementSelector, JQueryCollectionLike } from './types' type ComponentConfig = { parent: HTMLElement | null @@ -247,6 +247,25 @@ class Collapse extends BaseComponent { element.setAttribute('aria-expanded', String(isOpen)) } } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + const _config: BaseComponentConfig = {} + if (typeof config === 'string' && /show|hide/.test(config)) { + _config.toggle = false + } + + return this.each(function (this: HTMLElement) { + const data = Collapse.getOrCreateInstance(this, _config) as unknown as Record unknown> + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) + } + + data[config]() + } + }) + } } EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (this: HTMLElement, event: Event) { @@ -260,4 +279,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( } }) +defineJQueryPlugin(Collapse) + export default Collapse diff --git a/core/js/src/bootstrap/dom/event-handler.ts b/core/js/src/bootstrap/dom/event-handler.ts index 41d1cd1bc..77c2e3e78 100644 --- a/core/js/src/bootstrap/dom/event-handler.ts +++ b/core/js/src/bootstrap/dom/event-handler.ts @@ -5,6 +5,9 @@ * -------------------------------------------------------------------------- */ +import { getjQuery } from '../util/index.js' +import type { JQueryEventLike } from '../types' + // `this` is the element the handler was bound to, or the delegate target export type EventCallback = (this: HTMLElement, event: E) => void @@ -261,9 +264,37 @@ const EventHandler = { return null } - const evt = hydrateObj(new Event(event, { bubbles: true, cancelable: true }), args) + const $ = getjQuery() + const typeEvent = getTypeEvent(event) + const inNamespace = event !== typeEvent - element.dispatchEvent(evt) + let jQueryEvent: JQueryEventLike | null = null + let bubbles = true + let nativeDispatch = true + let defaultPrevented = false + + if (inNamespace && $) { + jQueryEvent = $.Event(event, args) + + $(element).trigger(jQueryEvent) + bubbles = !jQueryEvent.isPropagationStopped() + nativeDispatch = !jQueryEvent.isImmediatePropagationStopped() + defaultPrevented = jQueryEvent.isDefaultPrevented() + } + + const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args) + + if (defaultPrevented) { + evt.preventDefault() + } + + if (nativeDispatch) { + element.dispatchEvent(evt) + } + + if (evt.defaultPrevented && jQueryEvent) { + jQueryEvent.preventDefault() + } return evt }, diff --git a/core/js/src/bootstrap/dropdown.ts b/core/js/src/bootstrap/dropdown.ts index 267ea560f..23e763c52 100644 --- a/core/js/src/bootstrap/dropdown.ts +++ b/core/js/src/bootstrap/dropdown.ts @@ -10,8 +10,9 @@ import BaseComponent from './base-component' import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' import SelectorEngine from './dom/selector-engine' -import { execute, getElement, getNextActiveElement, isDisabled, isElement, isRTL, isVisible, noop } from './util/index' +import { defineJQueryPlugin, execute, getElement, getNextActiveElement, isDisabled, isElement, isRTL, isVisible, noop } from './util/index' import type { DelegatedEvent } from './dom/event-handler' +import type { ComponentConfig as BaseComponentConfig, JQueryCollectionLike } from './types' type PopperOffsetData = { placement: Popper.Placement; reference: Popper.Rect; popper: Popper.Rect } type PopperOffsetFunction = (popperData: PopperOffsetData) => number[] @@ -395,6 +396,22 @@ class Dropdown extends BaseComponent { getToggleButton!.focus() } } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Dropdown.getOrCreateInstance(this, config as BaseComponentConfig) as unknown as Record unknown> + + if (typeof config !== 'string') { + return + } + + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) + } + + data[config]() + }) + } } EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler) @@ -406,4 +423,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( ;(Dropdown.getOrCreateInstance(this) as Dropdown).toggle() }) +defineJQueryPlugin(Dropdown) + export default Dropdown diff --git a/core/js/src/bootstrap/modal.ts b/core/js/src/bootstrap/modal.ts index 92c22451d..8b7a63392 100644 --- a/core/js/src/bootstrap/modal.ts +++ b/core/js/src/bootstrap/modal.ts @@ -11,8 +11,9 @@ import SelectorEngine from './dom/selector-engine' import Backdrop from './util/backdrop' import { enableDismissTrigger } from './util/component-functions' import FocusTrap from './util/focustrap' -import { isRTL, isVisible, reflow } from './util/index' +import { defineJQueryPlugin, isRTL, isVisible, reflow } from './util/index' import ScrollBarHelper from './util/scrollbar' +import type { ComponentConfig as BaseComponentConfig, JQueryCollectionLike } from './types' /** * Constants @@ -318,6 +319,22 @@ class Modal extends BaseComponent { this._element.style.paddingLeft = '' this._element.style.paddingRight = '' } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown, relatedTarget?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Modal.getOrCreateInstance(this, config as BaseComponentConfig) as unknown as Record unknown> + + if (typeof config !== 'string') { + return + } + + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) + } + + data[config](relatedTarget) + }) + } } /** @@ -359,4 +376,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function ( enableDismissTrigger(Modal) +defineJQueryPlugin(Modal) + export default Modal diff --git a/core/js/src/bootstrap/offcanvas.ts b/core/js/src/bootstrap/offcanvas.ts index 6a609b688..496ab9c0b 100644 --- a/core/js/src/bootstrap/offcanvas.ts +++ b/core/js/src/bootstrap/offcanvas.ts @@ -11,8 +11,9 @@ import SelectorEngine from './dom/selector-engine' import Backdrop from './util/backdrop' import { enableDismissTrigger } from './util/component-functions' import FocusTrap from './util/focustrap' -import { isDisabled, isVisible } from './util/index' +import { defineJQueryPlugin, isDisabled, isVisible } from './util/index' import ScrollBarHelper from './util/scrollbar' +import type { ComponentConfig as BaseComponentConfig, JQueryCollectionLike } from './types' /** * Constants @@ -212,6 +213,22 @@ class Offcanvas extends BaseComponent { EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED) }) } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Offcanvas.getOrCreateInstance(this, config as BaseComponentConfig) as unknown as Record unknown> + + if (typeof config !== 'string') { + return + } + + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`) + } + + data[config](this) + }) + } } /** @@ -264,4 +281,6 @@ EventHandler.on(window, EVENT_RESIZE, () => { enableDismissTrigger(Offcanvas) +defineJQueryPlugin(Offcanvas) + export default Offcanvas diff --git a/core/js/src/bootstrap/popover.ts b/core/js/src/bootstrap/popover.ts index eac70133d..39bb51a1e 100644 --- a/core/js/src/bootstrap/popover.ts +++ b/core/js/src/bootstrap/popover.ts @@ -7,6 +7,8 @@ import Tooltip from './tooltip' import type { TooltipConfig, TooltipContent, TooltipContentMap } from './tooltip' +import { defineJQueryPlugin } from './util/index' +import type { ComponentConfig as BaseComponentConfig, JQueryCollectionLike } from './types' /** * Constants @@ -75,6 +77,24 @@ class Popover extends Tooltip { _getContent(): string | HTMLElement | null { return this._resolvePossibleFunction(this._config.content) } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Popover.getOrCreateInstance(this, config as BaseComponentConfig) as unknown as Record unknown> + + if (typeof config !== 'string') { + return + } + + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) + } + + data[config]() + }) + } } +defineJQueryPlugin(Popover) + export default Popover diff --git a/core/js/src/bootstrap/scrollspy.ts b/core/js/src/bootstrap/scrollspy.ts index 07d3a8bfa..c9e954977 100644 --- a/core/js/src/bootstrap/scrollspy.ts +++ b/core/js/src/bootstrap/scrollspy.ts @@ -8,7 +8,8 @@ import BaseComponent from './base-component' import EventHandler from './dom/event-handler' import SelectorEngine from './dom/selector-engine' -import { getElement, isDisabled, isVisible, parseSelector } from './util/index' +import { defineJQueryPlugin, getElement, isDisabled, isVisible, parseSelector } from './util/index' +import type { ComponentConfig as BaseComponentConfig, JQueryCollectionLike } from './types' type ComponentConfig = { offset: number | null @@ -253,6 +254,22 @@ class ScrollSpy extends BaseComponent { node.classList.remove(CLASS_NAME_ACTIVE) } } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = ScrollSpy.getOrCreateInstance(this, config as BaseComponentConfig) as unknown as Record unknown> + + if (typeof config !== 'string') { + return + } + + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`) + } + + data[config]() + }) + } } EventHandler.on(window, EVENT_LOAD_DATA_API, () => { @@ -261,4 +278,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { } }) +defineJQueryPlugin(ScrollSpy) + export default ScrollSpy diff --git a/core/js/src/bootstrap/tab.ts b/core/js/src/bootstrap/tab.ts index 5aa45fabc..2597423f1 100644 --- a/core/js/src/bootstrap/tab.ts +++ b/core/js/src/bootstrap/tab.ts @@ -8,7 +8,8 @@ import BaseComponent from './base-component' import EventHandler from './dom/event-handler' import SelectorEngine from './dom/selector-engine' -import { getNextActiveElement, isDisabled } from './util/index' +import { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index' +import type { JQueryCollectionLike } from './types' const NAME = 'tab' const DATA_KEY = 'bs.tab' @@ -245,6 +246,22 @@ class Tab extends BaseComponent { _getOuterElement(elem: HTMLElement): HTMLElement { return elem.closest(SELECTOR_OUTER) || elem } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Tab.getOrCreateInstance(this) as unknown as Record unknown> + + if (typeof config !== 'string') { + return + } + + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`) + } + + data[config]() + }) + } } EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (this: HTMLElement, event: Event) { @@ -265,4 +282,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => { } }) +defineJQueryPlugin(Tab) + export default Tab diff --git a/core/js/src/bootstrap/toast.ts b/core/js/src/bootstrap/toast.ts index 331d4eec3..f5fa8d18a 100644 --- a/core/js/src/bootstrap/toast.ts +++ b/core/js/src/bootstrap/toast.ts @@ -8,8 +8,8 @@ import BaseComponent from './base-component' import EventHandler from './dom/event-handler.js' import { enableDismissTrigger } from './util/component-functions' -import { reflow } from './util/index.js' -import type { ElementSelector } from './types' +import { defineJQueryPlugin, reflow } from './util/index.js' +import type { ComponentConfig as BaseComponentConfig, ElementSelector, JQueryCollectionLike } from './types' type ComponentConfig = { animation: boolean @@ -196,8 +196,24 @@ class Toast extends BaseComponent { clearTimeout(this._timeout!) this._timeout = null } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Toast.getOrCreateInstance(this, config as BaseComponentConfig) as unknown as Record unknown> + + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) + } + + data[config](this) + } + }) + } } enableDismissTrigger(Toast) +defineJQueryPlugin(Toast) + export default Toast diff --git a/core/js/src/bootstrap/tooltip.ts b/core/js/src/bootstrap/tooltip.ts index a8c7e3fe0..08fc281c1 100644 --- a/core/js/src/bootstrap/tooltip.ts +++ b/core/js/src/bootstrap/tooltip.ts @@ -9,10 +9,10 @@ import * as Popper from '@popperjs/core' import BaseComponent from './base-component' import EventHandler from './dom/event-handler' import Manipulator from './dom/manipulator' -import { execute, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index' +import { defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index' import { DefaultAllowlist } from './util/sanitizer' import TemplateFactory from './util/template-factory' -import type { AllowList, SanitizeFn } from './types' +import type { AllowList, ComponentConfig as BaseComponentConfig, JQueryCollectionLike, SanitizeFn } from './types' /** * Constants @@ -631,6 +631,24 @@ class Tooltip extends BaseComponent { this.tip = null } } + + static jQueryInterface(this: JQueryCollectionLike, config?: unknown): unknown { + return this.each(function (this: HTMLElement) { + const data = Tooltip.getOrCreateInstance(this, config as BaseComponentConfig) as unknown as Record unknown> + + if (typeof config !== 'string') { + return + } + + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) + } + + data[config]() + }) + } } +defineJQueryPlugin(Tooltip) + export default Tooltip diff --git a/core/js/src/bootstrap/types.ts b/core/js/src/bootstrap/types.ts index 467c34c45..03c7ba39e 100644 --- a/core/js/src/bootstrap/types.ts +++ b/core/js/src/bootstrap/types.ts @@ -25,3 +25,32 @@ export interface BaseComponentStatic extends ConfigStatic { export type AllowList = Record export type SanitizeFn = (unsafeHtml: string) => string + +export interface JQueryEventLike { + isPropagationStopped(): boolean + isImmediatePropagationStopped(): boolean + isDefaultPrevented(): boolean + preventDefault(): void +} + +export interface JQueryCollectionLike { + trigger(event: JQueryEventLike): unknown + each(callback: (this: HTMLElement, index: number) => void): unknown +} + +export interface JQueryPluginFunction { + (this: JQueryCollectionLike, config?: unknown, ...args: unknown[]): unknown + Constructor?: unknown + noConflict?: () => JQueryPluginFunction +} + +export interface JQueryStaticLike { + fn: Record + (selector: EventTarget): JQueryCollectionLike + Event(name: string, args?: Record): JQueryEventLike +} + +export interface JQueryPluginStatic { + NAME: string + jQueryInterface: JQueryPluginFunction +} diff --git a/core/js/src/bootstrap/util/index.ts b/core/js/src/bootstrap/util/index.ts index 438dff2eb..dfedec2a1 100644 --- a/core/js/src/bootstrap/util/index.ts +++ b/core/js/src/bootstrap/util/index.ts @@ -5,6 +5,8 @@ * -------------------------------------------------------------------------- */ +import type { JQueryPluginStatic, JQueryStaticLike } from '../types' + const MAX_UID = 1_000_000 const MILLISECONDS_MULTIPLIER = 1000 const TRANSITION_END = 'transitionend' @@ -156,6 +158,48 @@ const reflow = (element: HTMLElement): void => { const isRTL = (): boolean => document.documentElement.dir === 'rtl' +const getjQuery = (): JQueryStaticLike | null => { + if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { + return window.jQuery + } + + return null +} + +const DOMContentLoadedCallbacks: Array<() => void> = [] + +const onDOMContentLoaded = (callback: () => void): void => { + if (document.readyState === 'loading') { + if (!DOMContentLoadedCallbacks.length) { + document.addEventListener('DOMContentLoaded', () => { + for (const domCallback of DOMContentLoadedCallbacks) { + domCallback() + } + }) + } + + DOMContentLoadedCallbacks.push(callback) + } else { + callback() + } +} + +const defineJQueryPlugin = (plugin: JQueryPluginStatic): void => { + onDOMContentLoaded(() => { + const $ = getjQuery() + if ($) { + const name = plugin.NAME + const JQUERY_NO_CONFLICT = $.fn[name] + $.fn[name] = plugin.jQueryInterface + $.fn[name].Constructor = plugin + $.fn[name].noConflict = () => { + $.fn[name] = JQUERY_NO_CONFLICT + return plugin.jQueryInterface + } + } + }) +} + const execute = (possibleCallback: unknown, args: unknown[] = [], defaultValue: unknown = possibleCallback): unknown => { return typeof possibleCallback === 'function' ? possibleCallback.call(args[0], ...args.slice(1)) : defaultValue } @@ -206,4 +250,4 @@ const getNextActiveElement = (list: T[], activeElement: T, shouldGetNext: boo return list[Math.max(0, Math.min(index, listLength - 1))] } -export { execute, executeAfterTransition, findShadowRoot, getElement, getNextActiveElement, getTransitionDurationFromElement, getUID, isDisabled, isElement, isRTL, isVisible, noop, parseSelector, reflow, triggerTransitionEnd, toType } +export { defineJQueryPlugin, execute, executeAfterTransition, findShadowRoot, getElement, getjQuery, getNextActiveElement, getTransitionDurationFromElement, getUID, isDisabled, isElement, isRTL, isVisible, noop, onDOMContentLoaded, parseSelector, reflow, triggerTransitionEnd, toType } diff --git a/core/js/src/global.d.ts b/core/js/src/global.d.ts index 92375b8f6..3eccab382 100644 --- a/core/js/src/global.d.ts +++ b/core/js/src/global.d.ts @@ -1,9 +1,14 @@ +import type { JQueryStaticLike } from './bootstrap/types' + // Plugins that are loaded separately, not bundled: IMask (https://imask.js.org), // SortableJS (https://sortablejs.github.io/Sortable/) and Vanilla Calendar Pro // (https://vanilla-calendar.pro). The instance types live in the component // modules, so they are part of the published `dist/types`. -interface Window { - VanillaCalendarPro?: typeof import('vanilla-calendar-pro') - IMask?: new (element: HTMLElement, options: { mask: string; lazy?: boolean }) => import('./input-mask').IMaskInstance - Sortable?: new (element: HTMLElement, options?: Record) => import('./sortable').SortableInstance +declare global { + interface Window { + VanillaCalendarPro?: typeof import('vanilla-calendar-pro') + IMask?: new (element: HTMLElement, options: { mask: string; lazy?: boolean }) => import('./input-mask').IMaskInstance + Sortable?: new (element: HTMLElement, options?: Record) => import('./sortable').SortableInstance + jQuery?: JQueryStaticLike + } }