UNPKG

@coreui/coreui-pro

Version:

The most popular front-end framework for developing responsive, mobile-first projects on the web rewritten by the CoreUI Team

1,559 lines (1,452 loc) 357 kB
/*! * CoreUI v5.14.2 (https://coreui.io) * Copyright 2025 The CoreUI Team (https://github.com/orgs/coreui/people) * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) */ import * as Popper from '@popperjs/core'; /** * -------------------------------------------------------------------------- * CoreUI dom/data.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This is a modified version of the Bootstrap's dom/data.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const elementMap = new Map(); const Data = { set(element, key, instance) { if (!elementMap.has(element)) { elementMap.set(element, new Map()); } const instanceMap = elementMap.get(element); // make it clear we only want one instance per element // can be removed later when multiple key/instances are fine to be used if (!instanceMap.has(key) && instanceMap.size !== 0) { // eslint-disable-next-line no-console console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`); return; } instanceMap.set(key, instance); }, get(element, key) { if (elementMap.has(element)) { return elementMap.get(element).get(key) || null; } return null; }, remove(element, key) { if (!elementMap.has(element)) { return; } const instanceMap = elementMap.get(element); instanceMap.delete(key); // free up element references if there are no instances left for an element if (instanceMap.size === 0) { elementMap.delete(element); } } }; /** * -------------------------------------------------------------------------- * CoreUI util/index.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This is a modified version of the Bootstrap's util/index.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ const MAX_UID = 1000000; const MILLISECONDS_MULTIPLIER = 1000; const TRANSITION_END = 'transitionend'; /** * Properly escape IDs selectors to handle weird IDs * @param {string} selector * @returns {string} */ const parseSelector = selector => { if (selector && window.CSS && window.CSS.escape) { // document.querySelector needs escaping to handle IDs (html5+) containing for instance / selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`); } return selector; }; // Shout-out Angus Croll (https://goo.gl/pxwQGp) const toType = object => { if (object === null || object === undefined) { return `${object}`; } return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase(); }; /** * Public Util API */ const getUID = prefix => { do { prefix += Math.floor(Math.random() * MAX_UID); } while (document.getElementById(prefix)); return prefix; }; const getTransitionDurationFromElement = element => { if (!element) { return 0; } // Get transition-duration of the element let { transitionDuration, transitionDelay } = window.getComputedStyle(element); const floatTransitionDuration = Number.parseFloat(transitionDuration); const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found if (!floatTransitionDuration && !floatTransitionDelay) { return 0; } // If multiple durations are defined, take the first transitionDuration = transitionDuration.split(',')[0]; transitionDelay = transitionDelay.split(',')[0]; return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; }; const triggerTransitionEnd = element => { element.dispatchEvent(new Event(TRANSITION_END)); }; const isElement = object => { if (!object || typeof object !== 'object') { return false; } if (typeof object.jquery !== 'undefined') { object = object[0]; } return typeof object.nodeType !== 'undefined'; }; const getElement = object => { // it's a jQuery object or a node element if (isElement(object)) { return object.jquery ? object[0] : object; } if (typeof object === 'string' && object.length > 0) { return document.querySelector(parseSelector(object)); } return null; }; const isVisible = element => { if (!isElement(element) || element.getClientRects().length === 0) { return false; } const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; // Handle `details` element as its content may falsie appear visible when it is closed const closedDetails = element.closest('details:not([open])'); if (!closedDetails) { return elementIsVisible; } if (closedDetails !== element) { const summary = element.closest('summary'); if (summary && summary.parentNode !== closedDetails) { return false; } if (summary === null) { return false; } } return elementIsVisible; }; const isDisabled = element => { if (!element || element.nodeType !== Node.ELEMENT_NODE) { return true; } if (element.classList.contains('disabled')) { return true; } if (typeof element.disabled !== 'undefined') { return element.disabled; } return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; }; const findShadowRoot = element => { if (!document.documentElement.attachShadow) { return null; } // Can find the shadow root otherwise it'll return the document if (typeof element.getRootNode === 'function') { const root = element.getRootNode(); return root instanceof ShadowRoot ? root : null; } if (element instanceof ShadowRoot) { return element; } // when we don't find a shadow root if (!element.parentNode) { return null; } return findShadowRoot(element.parentNode); }; const noop = () => {}; /** * Trick to restart an element's animation * * @param {HTMLElement} element * @return void * * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation */ const reflow = element => { element.offsetHeight; // eslint-disable-line no-unused-expressions }; const getjQuery = () => { if (window.jQuery && !document.body.hasAttribute('data-coreui-no-jquery')) { return window.jQuery; } return null; }; const DOMContentLoadedCallbacks = []; const onDOMContentLoaded = callback => { if (document.readyState === 'loading') { // add listener on the first call when the document is in loading state if (!DOMContentLoadedCallbacks.length) { document.addEventListener('DOMContentLoaded', () => { for (const callback of DOMContentLoadedCallbacks) { callback(); } }); } DOMContentLoadedCallbacks.push(callback); } else { callback(); } }; const isRTL = () => document.documentElement.dir === 'rtl'; const defineJQueryPlugin = plugin => { onDOMContentLoaded(() => { const $ = getjQuery(); /* istanbul ignore if */ 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, args = [], defaultValue = possibleCallback) => { return typeof possibleCallback === 'function' ? possibleCallback.call(...args) : defaultValue; }; const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => { if (!waitForTransition) { execute(callback); return; } const durationPadding = 5; const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding; let called = false; const handler = ({ target }) => { if (target !== transitionElement) { return; } called = true; transitionElement.removeEventListener(TRANSITION_END, handler); execute(callback); }; transitionElement.addEventListener(TRANSITION_END, handler); setTimeout(() => { if (!called) { triggerTransitionEnd(transitionElement); } }, emulatedDuration); }; /** * Return the previous/next element of a list. * * @param {array} list The list of elements * @param activeElement The active element * @param shouldGetNext Choose to get next or previous element * @param isCycleAllowed * @return {Element|elem} The proper element */ const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => { const listLength = list.length; let index = list.indexOf(activeElement); // if the element does not exist in the list return an element // depending on the direction and if cycle is allowed if (index === -1) { return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]; } index += shouldGetNext ? 1 : -1; if (isCycleAllowed) { index = (index + listLength) % listLength; } return list[Math.max(0, Math.min(index, listLength - 1))]; }; /** * -------------------------------------------------------------------------- * CoreUI dom/event-handler.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This is a modified version of the Bootstrap's dom/event-handler.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const namespaceRegex = /[^.]*(?=\..*)\.|.*/; const stripNameRegex = /\..*/; const stripUidRegex = /::\d+$/; const eventRegistry = {}; // Events storage let uidEvent = 1; const customEvents = { mouseenter: 'mouseover', mouseleave: 'mouseout' }; const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']); /** * Private methods */ function makeEventUid(element, uid) { return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++; } function getElementEvents(element) { const uid = makeEventUid(element); element.uidEvent = uid; eventRegistry[uid] = eventRegistry[uid] || {}; return eventRegistry[uid]; } function bootstrapHandler(element, fn) { return function handler(event) { hydrateObj(event, { delegateTarget: element }); if (handler.oneOff) { EventHandler.off(element, event.type, fn); } return fn.apply(element, [event]); }; } function bootstrapDelegationHandler(element, selector, fn) { return function handler(event) { const domElements = element.querySelectorAll(selector); for (let { target } = event; target && target !== this; target = target.parentNode) { for (const domElement of domElements) { if (domElement !== target) { continue; } hydrateObj(event, { delegateTarget: target }); if (handler.oneOff) { EventHandler.off(element, event.type, selector, fn); } return fn.apply(target, [event]); } } }; } function findHandler(events, callable, delegationSelector = null) { return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector); } function normalizeParameters(originalTypeEvent, handler, delegationFunction) { const isDelegated = typeof handler === 'string'; // TODO: tooltip passes `false` instead of selector, so we need to check const callable = isDelegated ? delegationFunction : handler || delegationFunction; let typeEvent = getTypeEvent(originalTypeEvent); if (!nativeEvents.has(typeEvent)) { typeEvent = originalTypeEvent; } return [isDelegated, callable, typeEvent]; } function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { if (typeof originalTypeEvent !== 'string' || !element) { return; } let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position // this prevents the handler from being dispatched the same way as mouseover or mouseout does if (originalTypeEvent in customEvents) { const wrapFunction = fn => { return function (event) { if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) { return fn.call(this, event); } }; }; callable = wrapFunction(callable); } const events = getElementEvents(element); const handlers = events[typeEvent] || (events[typeEvent] = {}); const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null); if (previousFunction) { previousFunction.oneOff = previousFunction.oneOff && oneOff; return; } const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, '')); const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable); fn.delegationSelector = isDelegated ? handler : null; fn.callable = callable; fn.oneOff = oneOff; fn.uidEvent = uid; handlers[uid] = fn; element.addEventListener(typeEvent, fn, isDelegated); } function removeHandler(element, events, typeEvent, handler, delegationSelector) { const fn = findHandler(events[typeEvent], handler, delegationSelector); if (!fn) { return; } element.removeEventListener(typeEvent, fn, Boolean(delegationSelector)); delete events[typeEvent][fn.uidEvent]; } function removeNamespacedHandlers(element, events, typeEvent, namespace) { const storeElementEvent = events[typeEvent] || {}; for (const [handlerKey, event] of Object.entries(storeElementEvent)) { if (handlerKey.includes(namespace)) { removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); } } } function getTypeEvent(event) { // allow to get the native events from namespaced events ('click.coreui.button' --> 'click') event = event.replace(stripNameRegex, ''); return customEvents[event] || event; } const EventHandler = { on(element, event, handler, delegationFunction) { addHandler(element, event, handler, delegationFunction, false); }, one(element, event, handler, delegationFunction) { addHandler(element, event, handler, delegationFunction, true); }, off(element, originalTypeEvent, handler, delegationFunction) { if (typeof originalTypeEvent !== 'string' || !element) { return; } const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); const inNamespace = typeEvent !== originalTypeEvent; const events = getElementEvents(element); const storeElementEvent = events[typeEvent] || {}; const isNamespace = originalTypeEvent.startsWith('.'); if (typeof callable !== 'undefined') { // Simplest case: handler is passed, remove that listener ONLY. if (!Object.keys(storeElementEvent).length) { return; } removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null); return; } if (isNamespace) { for (const elementEvent of Object.keys(events)) { removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1)); } } for (const [keyHandlers, event] of Object.entries(storeElementEvent)) { const handlerKey = keyHandlers.replace(stripUidRegex, ''); if (!inNamespace || originalTypeEvent.includes(handlerKey)) { removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); } } }, trigger(element, event, args) { if (typeof event !== 'string' || !element) { return null; } const $ = getjQuery(); const typeEvent = getTypeEvent(event); const inNamespace = event !== typeEvent; let jQueryEvent = 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; } }; function hydrateObj(obj, meta = {}) { for (const [key, value] of Object.entries(meta)) { try { obj[key] = value; } catch (_unused) { Object.defineProperty(obj, key, { configurable: true, get() { return value; } }); } } return obj; } /** * -------------------------------------------------------------------------- * CoreUI dom/manipulator.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This is a modified version of the Bootstrap's dom/manipulator.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ function normalizeData(value) { if (value === 'true') { return true; } if (value === 'false') { return false; } if (value === Number(value).toString()) { return Number(value); } if (value === '' || value === 'null') { return null; } if (typeof value !== 'string') { return value; } try { return JSON.parse(decodeURIComponent(value)); } catch (_unused) { return value; } } function normalizeDataKey(key) { return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`); } const Manipulator = { setDataAttribute(element, key, value) { element.setAttribute(`data-coreui-${normalizeDataKey(key)}`, value); }, removeDataAttribute(element, key) { element.removeAttribute(`data-coreui-${normalizeDataKey(key)}`); }, getDataAttributes(element) { if (!element) { return {}; } const attributes = {}; const coreuiKeys = Object.keys(element.dataset).filter(key => key.startsWith('coreui') && !key.startsWith('coreuiConfig')); for (const key of coreuiKeys) { let pureKey = key.replace(/^coreui/, ''); pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1); attributes[pureKey] = normalizeData(element.dataset[key]); } return attributes; }, getDataAttribute(element, key) { return normalizeData(element.getAttribute(`data-coreui-${normalizeDataKey(key)}`)); } }; /** * -------------------------------------------------------------------------- * CoreUI util/config.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This is a modified version of the Bootstrap's util/config.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Class definition */ class Config { // Getters static get Default() { return {}; } static get DefaultType() { return {}; } static get NAME() { throw new Error('You have to implement the static method "NAME", for each component!'); } _getConfig(config) { config = this._mergeConfigObj(config); config = this._configAfterMerge(config); this._typeCheckConfig(config); return config; } _configAfterMerge(config) { return config; } _mergeConfigObj(config, element) { const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse return { ...this.constructor.Default, ...(typeof jsonConfig === 'object' ? jsonConfig : {}), ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}), ...(typeof config === 'object' ? config : {}) }; } _typeCheckConfig(config, configTypes = this.constructor.DefaultType) { for (const [property, expectedTypes] of Object.entries(configTypes)) { const value = config[property]; const valueType = isElement(value) ? 'element' : toType(value); if (!new RegExp(expectedTypes).test(valueType)) { throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`); } } } } /** * -------------------------------------------------------------------------- * CoreUI base-component.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This component is a modified version of the Bootstrap's base-component.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const VERSION = '5.14.2'; /** * Class definition */ class BaseComponent extends Config { constructor(element, config) { super(); element = getElement(element); if (!element) { return; } this._element = element; this._config = this._getConfig(config); Data.set(this._element, this.constructor.DATA_KEY, this); } // Public dispose() { Data.remove(this._element, this.constructor.DATA_KEY); EventHandler.off(this._element, this.constructor.EVENT_KEY); for (const propertyName of Object.getOwnPropertyNames(this)) { this[propertyName] = null; } } // Private _queueCallback(callback, element, isAnimated = true) { executeAfterTransition(callback, element, isAnimated); } _getConfig(config) { config = this._mergeConfigObj(config, this._element); config = this._configAfterMerge(config); this._typeCheckConfig(config); return config; } // Static static getInstance(element) { return Data.get(getElement(element), this.DATA_KEY); } static getOrCreateInstance(element, config = {}) { return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null); } static get VERSION() { return VERSION; } static get DATA_KEY() { return `coreui.${this.NAME}`; } static get EVENT_KEY() { return `.${this.DATA_KEY}`; } static eventName(name) { return `${name}${this.EVENT_KEY}`; } } /** * -------------------------------------------------------------------------- * CoreUI dom/selector-engine.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This is a modified version of the Bootstrap's dom/selector-engine.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ const getSelector = element => { let selector = element.getAttribute('data-coreui-target'); if (!selector || selector === '#') { let hrefAttribute = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes, // so everything starting with `#` or `.`. If a "real" URL is used as the selector, // `document.querySelector` will rightfully complain it is invalid. // See https://github.com/twbs/bootstrap/issues/32273 if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) { return null; } // Just in case some CMS puts out a full URL with the anchor appended if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) { hrefAttribute = `#${hrefAttribute.split('#')[1]}`; } selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null; } return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null; }; const SelectorEngine = { find(selector, element = document.documentElement) { return [].concat(...Element.prototype.querySelectorAll.call(element, selector)); }, findOne(selector, element = document.documentElement) { return Element.prototype.querySelector.call(element, selector); }, children(element, selector) { return [].concat(...element.children).filter(child => child.matches(selector)); }, parents(element, selector) { const parents = []; let ancestor = element.parentNode.closest(selector); while (ancestor) { parents.push(ancestor); ancestor = ancestor.parentNode.closest(selector); } return parents; }, prev(element, selector) { let previous = element.previousElementSibling; while (previous) { if (previous.matches(selector)) { return [previous]; } previous = previous.previousElementSibling; } return []; }, // TODO: this is now unused; remove later along with prev() next(element, selector) { let next = element.nextElementSibling; while (next) { if (next.matches(selector)) { return [next]; } next = next.nextElementSibling; } return []; }, focusableChildren(element) { const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(','); return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el)); }, getSelectorFromElement(element) { const selector = getSelector(element); if (selector) { return SelectorEngine.findOne(selector) ? selector : null; } return null; }, getElementFromSelector(element) { const selector = getSelector(element); return selector ? SelectorEngine.findOne(selector) : null; }, getMultipleElementsFromSelector(element) { const selector = getSelector(element); return selector ? SelectorEngine.find(selector) : []; } }; /** * -------------------------------------------------------------------------- * CoreUI util/component-functions.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This is a modified version of the Bootstrap's util/component-functions.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ const enableDismissTrigger = (component, method = 'hide') => { const clickEvent = `click.dismiss${component.EVENT_KEY}`; const name = component.NAME; EventHandler.on(document, clickEvent, `[data-coreui-dismiss="${name}"]`, function (event) { if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault(); } if (isDisabled(this)) { return; } const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`); const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method instance[method](); }); }; /** * -------------------------------------------------------------------------- * CoreUI alert.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This component is a modified version of the Bootstrap's alert.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$r = 'alert'; const DATA_KEY$m = 'coreui.alert'; const EVENT_KEY$n = `.${DATA_KEY$m}`; const EVENT_CLOSE = `close${EVENT_KEY$n}`; const EVENT_CLOSED = `closed${EVENT_KEY$n}`; const CLASS_NAME_FADE$5 = 'fade'; const CLASS_NAME_SHOW$f = 'show'; /** * Class definition */ class Alert extends BaseComponent { // Getters static get NAME() { return NAME$r; } // Public close() { const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE); if (closeEvent.defaultPrevented) { return; } this._element.classList.remove(CLASS_NAME_SHOW$f); const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5); this._queueCallback(() => this._destroyElement(), this._element, isAnimated); } // Private _destroyElement() { this._element.remove(); EventHandler.trigger(this._element, EVENT_CLOSED); this.dispose(); } // Static static jQueryInterface(config) { return this.each(function () { const data = Alert.getOrCreateInstance(this); if (typeof config !== 'string') { return; } if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { throw new TypeError(`No method named "${config}"`); } data[config](this); }); } } /** * Data API implementation */ enableDismissTrigger(Alert, 'close'); /** * jQuery */ defineJQueryPlugin(Alert); /** * -------------------------------------------------------------------------- * CoreUI button.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * * This component is a modified version of the Bootstrap's button.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$q = 'button'; const DATA_KEY$l = 'coreui.button'; const EVENT_KEY$m = `.${DATA_KEY$l}`; const DATA_API_KEY$h = '.data-api'; const CLASS_NAME_ACTIVE$6 = 'active'; const SELECTOR_DATA_TOGGLE$f = '[data-coreui-toggle="button"]'; const EVENT_CLICK_DATA_API$g = `click${EVENT_KEY$m}${DATA_API_KEY$h}`; /** * Class definition */ class Button extends BaseComponent { // Getters static get NAME() { return NAME$q; } // Public toggle() { // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$6)); } // Static static jQueryInterface(config) { return this.each(function () { const data = Button.getOrCreateInstance(this); if (config === 'toggle') { data[config](); } }); } } /** * Data API implementation */ EventHandler.on(document, EVENT_CLICK_DATA_API$g, SELECTOR_DATA_TOGGLE$f, event => { event.preventDefault(); const button = event.target.closest(SELECTOR_DATA_TOGGLE$f); const data = Button.getOrCreateInstance(button); data.toggle(); }); /** * jQuery */ defineJQueryPlugin(Button); /** * Converts an ISO week string to a Date object representing the Monday of that week. * @param isoWeek - The ISO week string (e.g., "2023W05" or "2023w05"). * @returns The Date object for the Monday of the specified week, or null if invalid. */ const convertIsoWeekToDate = isoWeek => { const [year, week] = isoWeek.split(/[Ww]/); const date = new Date(Number(year), 0, 4); // 4th Jan is always in week 1 date.setDate(date.getDate() - (date.getDay() || 7) + 1 + (Number(week) - 1) * 7); return date; }; /** * Converts a date string or Date object to a Date object based on selection type. * @param date - The date to convert. * @param selectionType - The type of selection ('day', 'week', 'month', 'year'). * @returns The corresponding Date object or null if invalid. */ const convertToDateObject = (date, selectionType) => { if (date === null) { return null; } if (date instanceof Date) { return date; } if (selectionType === 'week') { return convertIsoWeekToDate(date); } if (selectionType === 'month' || selectionType === 'year') { const _date = new Date(Date.parse(date)); const userTimezoneOffset = _date.getTimezoneOffset() * 60000; return new Date(_date.getTime() + userTimezoneOffset); } return new Date(Date.parse(date)); }; /** * Creates groups from an array. * @param arr - The array to group. * @param numberOfGroups - Number of groups to create. * @returns An array of grouped arrays. */ const createGroupsInArray = (arr, numberOfGroups) => { const perGroup = Math.ceil(arr.length / numberOfGroups); return Array.from({ length: numberOfGroups }).fill('').map((_, i) => arr.slice(i * perGroup, (i + 1) * perGroup)); }; /** * Adjusts the calendar date based on order and view type. * @param calendarDate - The current calendar date. * @param order - The order to adjust by. * @param view - The current view type. * @returns The adjusted Date object. */ const getCalendarDate = (calendarDate, order, view) => { if (order !== 0 && view === 'days') { return new Date(calendarDate.getFullYear(), calendarDate.getMonth() + order, 1); } if (order !== 0 && view === 'months') { return new Date(calendarDate.getFullYear() + order, calendarDate.getMonth(), 1); } if (order !== 0 && view === 'years') { return new Date(calendarDate.getFullYear() + 12 * order, calendarDate.getMonth(), 1); } return calendarDate; }; /** * Formats a date based on the selection type. * @param date - The date to format. * @param selectionType - The type of selection ('day', 'week', 'month', 'year'). * @returns A formatted date string or the original Date object. */ const getDateBySelectionType = (date, selectionType) => { if (date === null) { return null; } if (selectionType === 'week') { return `${date.getFullYear()}W${getWeekNumber(date)}`; } if (selectionType === 'month') { const monthNumber = `0${date.getMonth() + 1}`.slice(-2); return `${date.getFullYear()}-${monthNumber}`; } if (selectionType === 'year') { return `${date.getFullYear()}`; } return date; }; /** * Retrieves an array of month names based on locale and format. * @param locale - The locale string (e.g., 'en-US'). * @param format - The format of the month names ('short' or 'long'). * @returns An array of month names. */ const getMonthsNames = (locale, format = 'short') => { return Array.from({ length: 12 }, (_, i) => { return new Date(2000, i, 1).toLocaleString(locale, { month: format }); }); }; /** * Generates an array of years centered around a given year. * @param year - The central year. * @param range - The number of years before and after the central year. * @returns An array of years. */ const getYears = (year, range = 6) => { return Array.from({ length: range * 2 }, (_, i) => year - range + i); }; /** * Retrieves leading days (from the previous month) for a calendar view. * @param year - The year. * @param month - The month (0-11). * @param firstDayOfWeek - The first day of the week (0-6, where 0 is Sunday). * @returns An array of leading day objects. */ const getLeadingDays = (year, month, firstDayOfWeek) => { // 0: sunday // 1: monday const dates = []; const d = new Date(year, month); const y = d.getFullYear(); const m = d.getMonth(); const firstWeekday = new Date(y, m, 1).getDay(); let leadingDays = 6 - (6 - firstWeekday) - firstDayOfWeek; if (firstDayOfWeek) { leadingDays = leadingDays < 0 ? 7 + leadingDays : leadingDays; } for (let i = leadingDays * -1; i < 0; i++) { dates.push({ date: new Date(y, m, i + 1), month: 'previous' }); } return dates; }; /** * Retrieves all days within a specific month. * @param year - The year. * @param month - The month (0-11). * @returns An array of day objects. */ const getMonthDays = (year, month) => { const dates = []; const lastDay = new Date(year, month + 1, 0).getDate(); for (let i = 1; i <= lastDay; i++) { dates.push({ date: new Date(year, month, i), month: 'current' }); } return dates; }; /** * Retrieves trailing days (from the next month) for a calendar view. * @param year - The year. * @param month - The month (0-11). * @param leadingDays - Array of leading day objects. * @param monthDays - Array of current month day objects. * @returns An array of trailing day objects. */ const getTrailingDays = (year, month, leadingDays, monthDays) => { const dates = []; const days = 42 - (leadingDays.length + monthDays.length); for (let i = 1; i <= days; i++) { dates.push({ date: new Date(year, month + 1, i), month: 'next' }); } return dates; }; /** * Calculates the ISO week number for a given date. * @param date - The date to calculate the week number for. * @returns The ISO week number. */ const getWeekNumber = date => { const tempDate = new Date(date); tempDate.setHours(0, 0, 0, 0); // Thursday in current week decides the year tempDate.setDate(tempDate.getDate() + 3 - (tempDate.getDay() + 6) % 7); const week1 = new Date(tempDate.getFullYear(), 0, 4); // Calculate full weeks to the date const weekNumber = 1 + Math.round((tempDate.getTime() - week1.getTime()) / 86400000 / 7); return weekNumber; }; /** * Retrieves detailed information about each week in a month for calendar rendering. * @param year - The year. * @param month - The month (0-11). * @param firstDayOfWeek - The first day of the week (0-6, where 0 is Sunday). * @returns An array of week objects containing week numbers and day details. */ const getMonthDetails = (year, month, firstDayOfWeek) => { const daysPrevMonth = getLeadingDays(year, month, firstDayOfWeek); const daysThisMonth = getMonthDays(year, month); const daysNextMonth = getTrailingDays(year, month, daysPrevMonth, daysThisMonth); const days = [...daysPrevMonth, ...daysThisMonth, ...daysNextMonth]; const weeks = []; for (const [index, day] of days.entries()) { if (index % 7 === 0 || weeks.length === 0) { weeks.push({ days: [] }); } if ((index + 1) % 7 === 0) { weeks[weeks.length - 1].weekNumber = getWeekNumber(day.date); } weeks[weeks.length - 1].days.push(day); } return weeks; }; /** * Checks if a date is disabled based on the 'date' period type. * @param date - The date to check. * @param min - Minimum allowed date. * @param max - Maximum allowed date. * @param disabledDates - Criteria for disabled dates. * @returns True if the date is disabled, false otherwise. */ const isDateDisabled = (date, min, max, disabledDates) => { if (min && date < min) { return true; } if (max && date > max) { return true; } if (disabledDates === undefined) { return false; } if (typeof disabledDates === 'function') { return disabledDates(date); } if (disabledDates instanceof Date && isSameDateAs(date, disabledDates)) { return true; } if (Array.isArray(disabledDates) && disabledDates) { for (const _date of disabledDates) { if (typeof _date === 'function' && _date(date)) { return true; } if (Array.isArray(_date) && isDateInRange(date, _date[0], _date[1])) { return true; } if (_date instanceof Date && isSameDateAs(date, _date)) { return true; } } } return false; }; /** * Checks if a date is within a specified range. * @param date - The date to check. * @param start - Start date of the range. * @param end - End date of the range. * @returns True if the date is within the range, false otherwise. */ const isDateInRange = (date, start, end) => { const _date = removeTimeFromDate(date); const _start = start ? removeTimeFromDate(start) : null; const _end = end ? removeTimeFromDate(end) : null; return Boolean(_start && _end && _start <= _date && _date <= _end); }; /** * Checks if a date is selected based on start and end dates. * @param date - The date to check. * @param start - Start date. * @param end - End date. * @returns True if the date is selected, false otherwise. */ const isDateSelected = (date, start, end) => { if (start !== null && isSameDateAs(start, date)) { return true; } if (end !== null && isSameDateAs(end, date)) { return true; } return false; }; /** * Determines if any date within a range is disabled. * @param startDate - Start date of the range. * @param endDate - End date of the range. * @param disabledDates - Criteria for disabled dates. * @returns True if any date in the range is disabled, false otherwise. */ const isDisableDateInRange = (startDate, endDate, disabledDates) => { if (startDate && endDate) { const date = new Date(startDate); let disabled = false; // eslint-disable-next-line no-unmodified-loop-condition while (date < endDate) { date.setDate(date.getDate() + 1); if (isDateDisabled(date, null, null, disabledDates)) { disabled = true; break; } } return disabled; } return false; }; /** * Checks if a month is disabled based on the 'month' period type. * @param date - The date representing the month to check. * @param min - Minimum allowed date. * @param max - Maximum allowed date. * @param disabledDates - Criteria for disabled dates. * @returns True if the month is disabled, false otherwise. */ const isMonthDisabled = (date, min, max, disabledDates) => { const current = date.getFullYear() * 12 + date.getMonth(); const _min = min ? min.getFullYear() * 12 + min.getMonth() : null; const _max = max ? max.getFullYear() * 12 + max.getMonth() : null; if (_min && current < _min) { return true; } if (_max && current > _max) { return true; } if (disabledDates === undefined) { return false; } const start = min ? Math.max(date.getTime(), min.getTime()) : date; const end = max ? Math.min(date.getTime(), max.getTime()) : new Date(new Date().getFullYear(), 11, 31); for (const currentDate = new Date(start); // eslint-disable-next-line no-unmodified-loop-condition currentDate <= end; currentDate.setDate(currentDate.getDate() + 1)) { if (!isDateDisabled(currentDate, min, max, disabledDates)) { return false; } } return false; }; /** * Checks if a month is selected based on start and end dates. * @param date - The date representing the month. * @param start - Start date. * @param end - End date. * @returns True if the month is selected, false otherwise. */ const isMonthSelected = (date, start, end) => { const year = date.getFullYear(); const month = date.getMonth(); if (start !== null && year === start.getFullYear() && month === start.getMonth()) { return true; } if (end !== null && year === end.getFullYear() && month === end.getMonth()) { return true; } return false; }; /** * Checks if a month is within a specified range. * @param date - The date representing the month. * @param start - Start date. * @param end - End date. * @returns True if the month is within the range, false otherwise. */ const isMonthInRange = (date, start, end) => { const year = date.getFullYear(); const month = date.getMonth(); const _start = start ? start.getFullYear() * 12 + start.getMonth() : null; const _end = end ? end.getFullYear() * 12 + end.getMonth() : null; const _date = year * 12 + month; return Boolean(_start && _end && _start <= _date && _date <= _end); }; /** * Checks if two dates are the same calendar date. * @param date - First date. * @param date2 - Second date. * @returns True if both dates are the same, false otherwise. */ const isSameDateAs = (date, date2) => { if (date instanceof Date && date2 instanceof Date) { return date.getDate() === date2.getDate() && date.getMonth() === date2.getMonth() && date.getFullYear() === date2.getFullYear(); } if (date === null && date2 === null) { return true; } return false; }; /** * Checks if a date is today. * @param date - The date to check. * @returns True if the date is today, false otherwise. */ const isToday = date => { const today = new Date(); return isSameDateAs(date, today); }; /** * Checks if a year is disabled based on the 'year' period type. * @param date - The date representing the year to check. * @param min - Minimum allowed date. * @param max - Maximum allowed date. * @param disabledDates - Criteria for disabled dates. * @returns True if the year is disabled, false otherwise. */ const isYearDisabled = (date, min, max, disabledDates) => { const year = date.getFullYear(); const minYear = min ? min.getFullYear() : null; const maxYear = max ? max.getFullYear() : null; if (minYear && year < minYear) { return true; } if (maxYear && year > maxYear) { return true; } if (disabledDates === undefined) { return false; } const start = min ? Math.max(date.getTime(), min.getTime()) : date; const end = max ? Math.min(date.getTime(), max.getTime()) : new Date(new Date().getFullYear(), 11, 31); for (const currentDate = new Date(start); // eslint-disable-next-line no-unmodified-loop-condition currentDate <= end; currentDate.setDate(currentDate.getDate() + 1)) { if (!isDateDisabled(currentDate, min, max, disabledDates)) { return false; } } return false; }; /** * Checks if a year is selected based on start and end dates. * @param date - The date representing the year. * @param start - Start date. * @param end - End date. * @returns True if the year matches the start's or end's year, false otherwise. */ const isYearSelected = (date, start, end) => { const year = date.getFullYear(); if (start !== null && year === start.getFullYear()) { return true; } if (end !== null && year === end.getFullYear()) { return true; } return false; }; /** * Checks if a year is within a specified range. * @param date - The date representing the year. * @param start - Start date. * @param end - End date. * @returns True if the year's value lies between start's year and end's year, false otherwise. */ const isYearInRange = (date, start, end) => { const year = date.getFullYear(); const _start = start ? start.getFullYear() : null; const _end = end ? end.getFullYear() : null; return Boolean(_start && _end && _start <= year && year <= _end); }; /** * Removes the time component from a Date object. * @param date - The original date. * @returns A new Date object with the time set to 00:00:00. */ const removeTimeFromDate = date => { const clearedDate = new Date(date); clearedDate.setHours(0, 0, 0, 0); return clearedDate; }; /** * Copies the time (hours, minutes, seconds, milliseconds) from one Date to another. * * @param {Date} target - The date whose time will be updated. * @param {Date} source - The date to copy the time from. * @returns {Date} A new Date instance with the date from `target` and time from `source`. */ const setTimeFromDate = (target, source) => { if (target === null) { return null; } if (!(source instanceof Date)) { return target; } const result = new Date(target); // create a copy to avoid mutation result.setHours(source.getHours(), source.getMinutes(), source.getSeconds(), source.getMilliseconds()); return result; }; /* eslint-disable complexity, indent, multiline-ternary, @stylistic/multiline-ternary */ /** * -------------------------------------------------------------------------- * CoreUI PRO calendar.js * License (https://coreui.io/pro/license/) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME$p = 'calendar'; const DATA_KEY$k = 'coreui.calendar'; const EVENT_KEY$l = `.${DATA_KEY$k}`; const DATA_API_KEY$g = '.data-api'; const ARROW_UP_KEY$4 = 'ArrowUp'; const ARROW_RIGHT_KEY$3 = 'ArrowRight'; const ARROW_DOWN_KEY$4 = 'ArrowDown'; const ARROW_LEFT_KEY$3 = 'ArrowLeft'; const ENTER_KEY$3 = 'Enter'; const SPACE_KEY$1 = 'Space'; const EVENT_BLUR = `blur${EVENT_KEY$l}`; const EVENT_CALENDAR_DATE_CHANGE = `calendarDateChange${EVENT_KEY$l}`; const EVENT_CALENDAR_MOUSE_LEAVE = `calendarMouseleave${EVENT_KEY$l}`; const EVENT_CELL_HOVER = `cellHover${EVENT_KEY$l}`; const EVENT_END_DATE_CHANGE$1 = `endDateChange${EVENT_KEY$l}`; const EVENT_FOCUS = `focus${EVENT_KEY$l}`; const EVENT_KEYDOWN$6 = `keydown${EVENT_KEY$l}`; const EVENT_SELECT_END_CHANGE = `selectEndChange${EVENT_KEY$l}`; const EVENT_START_DATE_CHANGE$1 = `startDateChange${EVENT_KEY$l}`; const EVENT_MOUSEENTER$3 = `mouseenter${EVENT_KEY$l}`; const EVENT_MOUSELEAVE$3 = `mouseleave${EVENT_KEY$l}`; const EVENT_LOAD_DATA_API$d = `load${EVENT_KEY$l}${DATA_API_KEY$g}`; const EVENT_CLICK_DATA_API$f = `click${EVENT_KEY$l}${DATA_API_KEY$g}`; const CLASS_NAME_CALENDAR_CELL = 'calendar-cell'; const CLASS_NAME_CALENDAR_CELL_INNER = 'calendar-cell-inner'; const CLASS_NAME_CALENDAR_ROW = 'calendar-row'; const CLASS_NAME_CALENDARS$1 = 'calendars'; const CLASS_NAME_SHOW_WEEK_NUMBERS = 'show-week-numbers'; const SELECTOR_BTN_DOUBLE_NEXT = '.btn-double-next'; const SELECTOR_BTN_DOUBLE_PREV = '.btn-double-prev'; const SELECTOR_BTN_MONTH = '.btn-month'; const SELECTOR_BTN_NEXT = '.btn-next'; const SELECTOR_BTN_PREV = '.btn-prev'; const SELECTOR_BTN_YEAR = '.btn-year'; const SELECTOR_CALENDAR$1 = '.calendar'; const SELECTOR_CALENDAR_CELL = '.calendar-cell'; const SELECTOR_CALENDAR_CELL_CLICKABLE = `${SELECTOR_CALENDAR_CELL}[tabindex="0"]`; const SELECTOR_CALENDAR_ROW = '.calendar-row'; const SELECTOR_CALENDAR_ROW_CLICKABLE = `${SELECTOR_CALENDAR_ROW}[tabindex="0"]`; const SELECTOR_DATA_TOGGLE$e = '[data-coreui-toggle="calendar"]'; const Default$n = { ariaNavNextMonthLabel: 'Next month', ariaNavNextY