# 1 Giao diện sử dụng thuộc tính data-coreui-toggle="dropdown" (ok)

Source code



{% file src="https://1655165672-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LsqgAqbSGjkO0e50FMT%2Fuploads%2Fmsgbfbt63jPZMJ3gZznX%2Fcoreui-main.zip?alt=media&token=b1336b3c-fb17-4df2-8173-412a9131bf19" %}

<figure><img src="https://1655165672-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LsqgAqbSGjkO0e50FMT%2Fuploads%2FsGn2o8lVjKLrRpCVHIYW%2Fimage.png?alt=media&#x26;token=5810a485-1f74-4486-a417-55cfe36aa955" alt=""><figcaption></figcaption></figure>

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" />
  <link rel="stylesheet" href="//code.jquery.com/jquery-3.7.1.min.js">
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.2/css/bootstrap.min.css"/>
  <script src="dist/js/coreui.bundle.js"></script>
</head>
<body>
  <div class="dropdown btn-group">
    <button class="btn btn-secondary dropdown-toggle" type="button" data-coreui-toggle="dropdown" aria-expanded="false">
      <i class="fas fa-cog"></i>
    </button>
    <ul class="dropdown-menu">
      <li>
        <a class="dropdown-item" href='@'>
          <i class="fas fa-eye-slash"></i> View trash
        </a>
      </li>
    </ul>
  </div>
</body>
</html>
```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\index.umd.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): index.esm.js
 * Licensed under MIT (https://coreui.io/license)
 * --------------------------------------------------------------------------
 */
import Dropdown from './src/dropdown'
export default {
  Dropdown
}
```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dropdown.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dropdown.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's dropdown.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */
import * as Popper from '@popperjs/core'
import {
  defineJQueryPlugin,
  getElement,
  getElementFromSelector,
  getNextActiveElement,
  isDisabled,
  isElement,
  isRTL,
  isVisible,
  noop,
  typeCheckConfig
} from './util/index'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selector-engine'
import BaseComponent from './base-component'
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
const NAME = 'dropdown'
const DATA_KEY = 'coreui.dropdown'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const ESCAPE_KEY = 'Escape'
const SPACE_KEY = 'Space'
const TAB_KEY = 'Tab'
const ARROW_UP_KEY = 'ArrowUp'
const ARROW_DOWN_KEY = 'ArrowDown'
const RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`)
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`
const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_DROPUP = 'dropup'
const CLASS_NAME_DROPEND = 'dropend'
const CLASS_NAME_DROPSTART = 'dropstart'
const CLASS_NAME_NAVBAR = 'navbar'
const SELECTOR_DATA_TOGGLE = '[data-coreui-toggle="dropdown"]'
const SELECTOR_MENU = '.dropdown-menu'
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'
const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'
const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'
const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'
const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'
const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'
const Default = {
  offset: [0, 2],
  boundary: 'clippingParents',
  reference: 'toggle',
  display: 'dynamic',
  popperConfig: null,
  autoClose: true
}
const DefaultType = {
  offset: '(array|string|function)',
  boundary: '(string|element)',
  reference: '(string|element|object)',
  display: 'string',
  popperConfig: '(null|object|function)',
  autoClose: '(boolean|string)'
}
/**
 * ------------------------------------------------------------------------
 * Class Definition
 * ------------------------------------------------------------------------
 */
class Dropdown extends BaseComponent {
  constructor(element, config) {
    super(element)
    this._popper = null
    this._config = this._getConfig(config)
    this._menu = this._getMenuElement()
    this._inNavbar = this._detectNavbar()
  }
  // Getters
  static get Default() {
    return Default
  }
  static get DefaultType() {
    return DefaultType
  }
  static get NAME() {
    return NAME
  }
  // Public
  toggle() {
    return this._isShown() ? this.hide() : this.show()
  }
  show() {
    if (isDisabled(this._element) || this._isShown(this._menu)) {
      return
    }
    const relatedTarget = {
      relatedTarget: this._element
    }
    const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)
    if (showEvent.defaultPrevented) {
      return
    }
    const parent = Dropdown.getParentFromElement(this._element)
    // Totally disable Popper for Dropdowns in Navbar
    if (this._inNavbar) {
      Manipulator.setDataAttribute(this._menu, 'popper', 'none')
    } else {
      this._createPopper(parent)
    }
    // If this is a touch-enabled device we add extra
    // empty mouseover listeners to the body's immediate children;
    // only needed because of broken event delegation on iOS
    // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
    if ('ontouchstart' in document.documentElement &&
      !parent.closest(SELECTOR_NAVBAR_NAV)) {
      [].concat(...document.body.children)
        .forEach(elem => EventHandler.on(elem, 'mouseover', noop))
    }
    this._element.focus()
    this._element.setAttribute('aria-expanded', true)
    this._menu.classList.add(CLASS_NAME_SHOW)
    this._element.classList.add(CLASS_NAME_SHOW)
    EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)
  }
  hide() {
    if (isDisabled(this._element) || !this._isShown(this._menu)) {
      return
    }
    const relatedTarget = {
      relatedTarget: this._element
    }
    this._completeHide(relatedTarget)
  }
  dispose() {
    if (this._popper) {
      this._popper.destroy()
    }
    super.dispose()
  }
  update() {
    this._inNavbar = this._detectNavbar()
    if (this._popper) {
      this._popper.update()
    }
  }
  // Private
  _completeHide(relatedTarget) {
    const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)
    if (hideEvent.defaultPrevented) {
      return
    }
    // If this is a touch-enabled device we remove the extra
    // empty mouseover listeners we added for iOS support
    if ('ontouchstart' in document.documentElement) {
      [].concat(...document.body.children)
        .forEach(elem => EventHandler.off(elem, 'mouseover', noop))
    }
    if (this._popper) {
      this._popper.destroy()
    }
    this._menu.classList.remove(CLASS_NAME_SHOW)
    this._element.classList.remove(CLASS_NAME_SHOW)
    this._element.setAttribute('aria-expanded', 'false')
    Manipulator.removeDataAttribute(this._menu, 'popper')
    EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)
  }
  _getConfig(config) {
    config = {
      ...this.constructor.Default,
      ...Manipulator.getDataAttributes(this._element),
      ...config
    }
    typeCheckConfig(NAME, config, this.constructor.DefaultType)
    if (typeof config.reference === 'object' && !isElement(config.reference) &&
      typeof config.reference.getBoundingClientRect !== 'function'
    ) {
      // Popper virtual elements require a getBoundingClientRect method
      throw new TypeError(`${NAME.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`)
    }
    return config
  }
  _createPopper(parent) {
    if (typeof Popper === 'undefined') {
      throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)')
    }
    let referenceElement = this._element
    if (this._config.reference === 'parent') {
      referenceElement = parent
    } else if (isElement(this._config.reference)) {
      referenceElement = getElement(this._config.reference)
    } else if (typeof this._config.reference === 'object') {
      referenceElement = this._config.reference
    }
    const popperConfig = this._getPopperConfig()
    const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false)
    this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)
    if (isDisplayStatic) {
      Manipulator.setDataAttribute(this._menu, 'popper', 'static')
    }
  }
  _isShown(element = this._element) {
    return element.classList.contains(CLASS_NAME_SHOW)
  }
  _getMenuElement() {
    return SelectorEngine.next(this._element, SELECTOR_MENU)[0]
  }
  _getPlacement() {
    const parentDropdown = this._element.parentNode
    if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {
      return PLACEMENT_RIGHT
    }
    if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {
      return PLACEMENT_LEFT
    }
    // We need to trim the value because custom properties can also include spaces
    const isEnd = getComputedStyle(this._menu).getPropertyValue('--cui-position').trim() === 'end'
    if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {
      return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP
    }
    return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM
  }
  _detectNavbar() {
    return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null
  }
  _getOffset() {
    const { offset } = this._config
    if (typeof offset === 'string') {
      return offset.split(',').map(val => Number.parseInt(val, 10))
    }
    if (typeof offset === 'function') {
      return popperData => offset(popperData, this._element)
    }
    return offset
  }
  _getPopperConfig() {
    const defaultBsPopperConfig = {
      placement: this._getPlacement(),
      modifiers: [{
        name: 'preventOverflow',
        options: {
          boundary: this._config.boundary
        }
      },
      {
        name: 'offset',
        options: {
          offset: this._getOffset()
        }
      }]
    }
    // Disable Popper if we have a static display
    if (this._config.display === 'static') {
      defaultBsPopperConfig.modifiers = [{
        name: 'applyStyles',
        enabled: false
      }]
    }
    return {
      ...defaultBsPopperConfig,
      ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
    }
  }
  _selectMenuItem({ key, target }) {
    const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)
    if (!items.length) {
      return
    }
    // if target isn't included in items (e.g. when expanding the dropdown)
    // allow cycling to get the last item in case key equals ARROW_UP_KEY
    getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()
  }
  // Static
  static jQueryInterface(config) {
    return this.each(function () {
      const data = Dropdown.getOrCreateInstance(this, config)
      if (typeof config !== 'string') {
        return
      }
      if (typeof data[config] === 'undefined') {
        throw new TypeError(`No method named "${config}"`)
      }
      data[config]()
    })
  }
  static clearMenus(event) {
    if (event && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) {
      return
    }
    const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
    for (let i = 0, len = toggles.length; i < len; i++) {
      const context = Dropdown.getInstance(toggles[i])
      if (!context || context._config.autoClose === false) {
        continue
      }
      if (!context._isShown()) {
        continue
      }
      const relatedTarget = {
        relatedTarget: context._element
      }
      if (event) {
        const composedPath = event.composedPath()
        const isMenuTarget = composedPath.includes(context._menu)
        if (
          composedPath.includes(context._element) ||
          (context._config.autoClose === 'inside' && !isMenuTarget) ||
          (context._config.autoClose === 'outside' && isMenuTarget)
        ) {
          continue
        }
        // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
        if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {
          continue
        }
        if (event.type === 'click') {
          relatedTarget.clickEvent = event
        }
      }
      context._completeHide(relatedTarget)
    }
  }
  static getParentFromElement(element) {
    return getElementFromSelector(element) || element.parentNode
  }
  static dataApiKeydownHandler(event) {
    // If not input/textarea:
    //  - And not a key in REGEXP_KEYDOWN => not a dropdown command
    // If input/textarea:
    //  - If space key => not a dropdown command
    //  - If key is other than escape
    //    - If key is not up or down => not a dropdown command
    //    - If trigger inside the menu => not a dropdown command
    if (/input|textarea/i.test(event.target.tagName) ?
      event.key === SPACE_KEY || (event.key !== ESCAPE_KEY &&
      ((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) ||
        event.target.closest(SELECTOR_MENU))) :
      !REGEXP_KEYDOWN.test(event.key)) {
      return
    }
    const isActive = this.classList.contains(CLASS_NAME_SHOW)
    if (!isActive && event.key === ESCAPE_KEY) {
      return
    }
    event.preventDefault()
    event.stopPropagation()
    if (isDisabled(this)) {
      return
    }
    const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
    const instance = Dropdown.getOrCreateInstance(getToggleButton)
    if (event.key === ESCAPE_KEY) {
      instance.hide()
      return
    }
    if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {
      if (!isActive) {
        instance.show()
      }
      instance._selectMenuItem(event)
      return
    }
    if (!isActive || event.key === SPACE_KEY) {
      Dropdown.clearMenus()
    }
  }
}
/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */
EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)
EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)
EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)
EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  event.preventDefault()
  Dropdown.getOrCreateInstance(this).toggle()
})
/**
 * ------------------------------------------------------------------------
 * jQuery
 * ------------------------------------------------------------------------
 * add .Dropdown to jQuery only if jQuery is present
 */
defineJQueryPlugin(Dropdown)
export default Dropdown

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\base-component.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): alert.js
 * Licensed under MIT (https://coreui.io/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)
 * --------------------------------------------------------------------------
 */
import Data from './dom/data'
import {
  executeAfterTransition,
  getElement
} from './util/index'
import EventHandler from './dom/event-handler'
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
const VERSION = '4.1.3'
class BaseComponent {
  constructor(element) {
    element = getElement(element)
    if (!element) {
      return
    }
    this._element = element
    Data.set(this._element, this.constructor.DATA_KEY, this)
  }
  dispose() {
    Data.remove(this._element, this.constructor.DATA_KEY)
    EventHandler.off(this._element, this.constructor.EVENT_KEY)
    Object.getOwnPropertyNames(this).forEach(propertyName => {
      this[propertyName] = null
    })
  }
  _queueCallback(callback, element, isAnimated = true) {
    executeAfterTransition(callback, element, isAnimated)
  }
  /** 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 NAME() {
    throw new Error('You have to implement the static method "NAME", for each component!')
  }
  static get DATA_KEY() {
    return `coreui.${this.NAME}`
  }
  static get EVENT_KEY() {
    return `.${this.DATA_KEY}`
  }
}
export default BaseComponent

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\util\index.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): alert.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component 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 = 1_000_000
const MILLISECONDS_MULTIPLIER = 1000
const TRANSITION_END = 'transitionend'
// Shoutout AngusCroll (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 getSelector = element => {
  let selector = element.getAttribute('data-coreui-target')
  if (!selector || selector === '#') {
    let hrefAttr = 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 (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {
      return null
    }
    // Just in case some CMS puts out a full URL with the anchor appended
    if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
      hrefAttr = `#${hrefAttr.split('#')[1]}`
    }
    selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null
  }
  return selector
}
const getSelectorFromElement = element => {
  const selector = getSelector(element)
  if (selector) {
    return document.querySelector(selector) ? selector : null
  }
  return null
}
const getElementFromSelector = element => {
  const selector = getSelector(element)
  return selector ? document.querySelector(selector) : null
}
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 = obj => {
  if (!obj || typeof obj !== 'object') {
    return false
  }
  if (typeof obj.jquery !== 'undefined') {
    obj = obj[0]
  }
  return typeof obj.nodeType !== 'undefined'
}
const getElement = obj => {
  if (isElement(obj)) { // it's a jQuery object or a node element
    return obj.jquery ? obj[0] : obj
  }
  if (typeof obj === 'string' && obj.length > 0) {
    return document.querySelector(obj)
  }
  return null
}
const typeCheckConfig = (componentName, config, configTypes) => {
  Object.keys(configTypes).forEach(property => {
    const expectedTypes = configTypes[property]
    const value = config[property]
    const valueType = value && isElement(value) ? 'element' : toType(value)
    if (!new RegExp(expectedTypes).test(valueType)) {
      throw new TypeError(
        `${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`
      )
    }
  })
}
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 => {
  // eslint-disable-next-line no-unused-expressions
  element.offsetHeight
}
const getjQuery = () => {
  const { jQuery } = window
  if (jQuery && !document.body.hasAttribute('data-coreui-no-jquery')) {
    return 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', () => {
        DOMContentLoadedCallbacks.forEach(callback => 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 = callback => {
  if (typeof callback === 'function') {
    callback()
  }
}
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) => {
  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 list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]
  }
  const listLength = list.length
  index += shouldGetNext ? 1 : -1
  if (isCycleAllowed) {
    index = (index + listLength) % listLength
  }
  return list[Math.max(0, Math.min(index, listLength - 1))]
}
export {
  getElement,
  getUID,
  getSelectorFromElement,
  getElementFromSelector,
  getTransitionDurationFromElement,
  triggerTransitionEnd,
  isElement,
  typeCheckConfig,
  isVisible,
  isDisabled,
  findShadowRoot,
  noop,
  getNextActiveElement,
  reflow,
  getjQuery,
  onDOMContentLoaded,
  isRTL,
  defineJQueryPlugin,
  execute,
  executeAfterTransition
}

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dom\data.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dom/data.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component 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()
export default {
  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)
    }
  }
}

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dom\event-handler.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dom/event-handler.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's  dom/event-handler.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */
import { getjQuery } from '../util/index'
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
const namespaceRegex = /[^.]*(?=\..*)\.|.*/
const stripNameRegex = /\..*/
const stripUidRegex = /::\d+$/
const eventRegistry = {} // Events storage
let uidEvent = 1
const customEvents = {
  mouseenter: 'mouseover',
  mouseleave: 'mouseout'
}
const customEventsRegex = /^(mouseenter|mouseleave)/i
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 getUidEvent(element, uid) {
  return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++
}
function getEvent(element) {
  const uid = getUidEvent(element)
  element.uidEvent = uid
  eventRegistry[uid] = eventRegistry[uid] || {}
  return eventRegistry[uid]
}
function bootstrapHandler(element, fn) {
  return function handler(event) {
    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 (let i = domElements.length; i--;) {
        if (domElements[i] === target) {
          event.delegateTarget = target
          if (handler.oneOff) {
            EventHandler.off(element, event.type, selector, fn)
          }
          return fn.apply(target, [event])
        }
      }
    }
    // To please ESLint
    return null
  }
}
function findHandler(events, handler, delegationSelector = null) {
  const uidEventList = Object.keys(events)
  for (let i = 0, len = uidEventList.length; i < len; i++) {
    const event = events[uidEventList[i]]
    if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {
      return event
    }
  }
  return null
}
function normalizeParams(originalTypeEvent, handler, delegationFn) {
  const delegation = typeof handler === 'string'
  const originalHandler = delegation ? delegationFn : handler
  let typeEvent = getTypeEvent(originalTypeEvent)
  const isNative = nativeEvents.has(typeEvent)
  if (!isNative) {
    typeEvent = originalTypeEvent
  }
  return [delegation, originalHandler, typeEvent]
}
function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
  if (typeof originalTypeEvent !== 'string' || !element) {
    return
  }
  if (!handler) {
    handler = delegationFn
    delegationFn = null
  }
  // 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 (customEventsRegex.test(originalTypeEvent)) {
    const wrapFn = fn => {
      return function (event) {
        if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {
          return fn.call(this, event)
        }
      }
    }
    if (delegationFn) {
      delegationFn = wrapFn(delegationFn)
    } else {
      handler = wrapFn(handler)
    }
  }
  const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
  const events = getEvent(element)
  const handlers = events[typeEvent] || (events[typeEvent] = {})
  const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)
  if (previousFn) {
    previousFn.oneOff = previousFn.oneOff && oneOff
    return
  }
  const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))
  const fn = delegation ?
    bootstrapDelegationHandler(element, handler, delegationFn) :
    bootstrapHandler(element, handler)
  fn.delegationSelector = delegation ? handler : null
  fn.originalHandler = originalHandler
  fn.oneOff = oneOff
  fn.uidEvent = uid
  handlers[uid] = fn
  element.addEventListener(typeEvent, fn, delegation)
}
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] || {}
  Object.keys(storeElementEvent).forEach(handlerKey => {
    if (handlerKey.includes(namespace)) {
      const event = storeElementEvent[handlerKey]
      removeHandler(element, events, typeEvent, event.originalHandler, 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, delegationFn) {
    addHandler(element, event, handler, delegationFn, false)
  },
  one(element, event, handler, delegationFn) {
    addHandler(element, event, handler, delegationFn, true)
  },
  off(element, originalTypeEvent, handler, delegationFn) {
    if (typeof originalTypeEvent !== 'string' || !element) {
      return
    }
    const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
    const inNamespace = typeEvent !== originalTypeEvent
    const events = getEvent(element)
    const isNamespace = originalTypeEvent.startsWith('.')
    if (typeof originalHandler !== 'undefined') {
      // Simplest case: handler is passed, remove that listener ONLY.
      if (!events || !events[typeEvent]) {
        return
      }
      removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null)
      return
    }
    if (isNamespace) {
      Object.keys(events).forEach(elementEvent => {
        removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))
      })
    }
    const storeElementEvent = events[typeEvent] || {}
    Object.keys(storeElementEvent).forEach(keyHandlers => {
      const handlerKey = keyHandlers.replace(stripUidRegex, '')
      if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
        const event = storeElementEvent[keyHandlers]
        removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
      }
    })
  },
  trigger(element, event, args) {
    if (typeof event !== 'string' || !element) {
      return null
    }
    const $ = getjQuery()
    const typeEvent = getTypeEvent(event)
    const inNamespace = event !== typeEvent
    const isNative = nativeEvents.has(typeEvent)
    let jQueryEvent
    let bubbles = true
    let nativeDispatch = true
    let defaultPrevented = false
    let evt = null
    if (inNamespace && $) {
      jQueryEvent = $.Event(event, args)
      $(element).trigger(jQueryEvent)
      bubbles = !jQueryEvent.isPropagationStopped()
      nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()
      defaultPrevented = jQueryEvent.isDefaultPrevented()
    }
    if (isNative) {
      evt = document.createEvent('HTMLEvents')
      evt.initEvent(typeEvent, bubbles, true)
    } else {
      evt = new CustomEvent(event, {
        bubbles,
        cancelable: true
      })
    }
    // merge custom information in our event
    if (typeof args !== 'undefined') {
      Object.keys(args).forEach(key => {
        Object.defineProperty(evt, key, {
          get() {
            return args[key]
          }
        })
      })
    }
    if (defaultPrevented) {
      evt.preventDefault()
    }
    if (nativeDispatch) {
      element.dispatchEvent(evt)
    }
    if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {
      jQueryEvent.preventDefault()
    }
    return evt
  }
}
export default EventHandler

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dom\manipulator.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dom/manipulator.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's  dom/manipulator.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */

function normalizeData(val) {
  if (val === 'true') {
    return true
  }

  if (val === 'false') {
    return false
  }

  if (val === Number(val).toString()) {
    return Number(val)
  }

  if (val === '' || val === 'null') {
    return null
  }

  return val
}

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 = {}

    Object.keys(element.dataset)
      .filter(key => key.startsWith('coreui'))
      .forEach(key => {
        let pureKey = key.replace(/^coreui/, '')
        pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)
        attributes[pureKey] = normalizeData(element.dataset[key])
      })

    return attributes
  },

  getDataAttribute(element, key) {
    return normalizeData(element.getAttribute(`data-coreui-${normalizeDataKey(key)}`))
  },

  offset(element) {
    const rect = element.getBoundingClientRect()

    return {
      top: rect.top + window.pageYOffset,
      left: rect.left + window.pageXOffset
    }
  },

  position(element) {
    return {
      top: element.offsetTop,
      left: element.offsetLeft
    }
  }
}

export default Manipulator

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\src\dom\selector-engine.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): dom/selector-engine.js
 * Licensed under MIT (https://coreui.io/license)
 *
 * This component is a modified version of the Bootstrap's  dom/selector-engine.js
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
 * --------------------------------------------------------------------------
 */

/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */

import { isDisabled, isVisible } from '../util/index'

const NODE_TEXT = 3

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

    while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {
      if (ancestor.matches(selector)) {
        parents.push(ancestor)
      }

      ancestor = ancestor.parentNode
    }

    return parents
  },

  prev(element, selector) {
    let previous = element.previousElementSibling

    while (previous) {
      if (previous.matches(selector)) {
        return [previous]
      }

      previous = previous.previousElementSibling
    }

    return []
  },

  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))
  }
}

export default SelectorEngine

```

2 file này phải có để cấu hình

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\index.esm.js\
index.esm.js có thể trống cũng ok

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\js\index.umd.js

```javascript
/**
 * --------------------------------------------------------------------------
 * CoreUI (v4.1.3): index.esm.js
 * Licensed under MIT (https://coreui.io/license)
 * --------------------------------------------------------------------------
 */
import Dropdown from './src/dropdown'
export default {
  Dropdown
}

```

C:\Users\Administrator\Desktop\Practice Coreui\coreui-main\package.json

```json
{
  "name": "@coreui/coreui",
  "description": "The most popular front-end framework for developing responsive, mobile-first projects on the web rewritten and maintain by the CoreUI Team",
  "version": "4.1.3",
  "config": {
    "version_short": "4.1"
  },
  "keywords": [
    "css",
    "sass",
    "mobile-first",
    "responsive",
    "front-end",
    "framework",
    "web"
  ],
  "homepage": "https://coreui.io",
  "author": "The CoreUI Team (https://github.com/orgs/coreui/people)",
  "scripts": {
    "start": "npm-run-all --parallel watch",
    "js": "npm-run-all js-compile js-minify",
    "js-compile": "npm-run-all --aggregate-output --parallel js-compile-*",
    "js-compile-standalone": "rollup --environment BUNDLE:false --config build/rollup.config.js --sourcemap",
    "js-compile-standalone-esm": "rollup --environment ESM:true,BUNDLE:false --config build/rollup.config.js --sourcemap",
    "js-compile-bundle": "rollup --environment BUNDLE:true --config build/rollup.config.js --sourcemap",
    "watch": "npm-run-all --parallel watch-*",
    "watch-js-main": "nodemon --watch js/src/ --ext js --exec \"npm-run-all js-compile\""
  },
  "style": "dist/css/coreui.css",
  "sass": "scss/coreui.scss",
  "main": "dist/js/coreui.js",
  "module": "dist/js/coreui.esm.js",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/coreui/coreui.git"
  },
  "bugs": {
    "url": "https://github.com/coreui/coreui/issues"
  },
  "license": "MIT",
  "peerDependencies": {
    "@popperjs/core": "^2.11.2"
  },
  "devDependencies": {
    "@babel/cli": "^7.17.6",
    "@babel/core": "^7.17.5",
    "@babel/preset-env": "^7.16.11",
    "@popperjs/core": "^2.11.2",
    "@rollup/plugin-babel": "^5.3.1",
    "@rollup/plugin-commonjs": "^21.0.2",
    "@rollup/plugin-node-resolve": "^13.1.3",
    "@rollup/plugin-replace": "^4.0.0",
    "auto-changelog": "^2.4.0",
    "autoprefixer": "^10.4.2",
    "bundlewatch": "^0.3.3",
    "clean-css-cli": "^5.5.2",
    "cross-env": "^7.0.3",
    "eslint": "^8.10.0",
    "eslint-config-xo": "^0.40.0",
    "eslint-plugin-import": "^2.25.4",
    "eslint-plugin-unicorn": "^41.0.0",
    "find-unused-sass-variables": "^4.0.3",
    "glob": "^7.2.0",
    "globby": "^11.0.4",
    "hammer-simulator": "0.0.1",
    "hugo-bin": "^0.81.1",
    "ip": "^1.1.5",
    "jquery": "^3.6.0",
    "linkinator": "^3.0.3",
    "lockfile-lint": "^4.6.2",
    "nodemon": "^2.0.15",
    "npm-run-all": "^4.1.5",
    "postcss": "^8.4.7",
    "postcss-cli": "^9.1.0",
    "postcss-combine-duplicated-selectors": "^10.0.3",
    "rollup": "^2.69.0",
    "rollup-plugin-istanbul": "^3.0.0",
    "rtlcss": "^3.5.0",
    "sass": "^1.49.9",
    "shelljs": "^0.8.5",
    "stylelint": "^14.5.3",
    "stylelint-config-twbs-bootstrap": "^3.0.1",
    "terser": "^5.12.0",
    "vnu-jar": "21.10.12"
  },
  "files": [
    "dist/{css,js}/*.{css,js,map}",
    "js/{src,dist}/**/*.{js,map}",
    "scss/**/*.scss"
  ],
  "hugo-bin": {
    "buildTags": "extended"
  },
  "jspm": {
    "registry": "npm",
    "main": "js/coreui",
    "directories": {
      "lib": "dist"
    },
    "shim": {
      "js/coreui": {
        "deps": [
          "@popperjs/core"
        ]
      }
    },
    "dependencies": {},
    "peerDependencies": {
      "@popperjs/core": "^2.11.2"
    }
  },
  "dependencies": {
    "@coreui/coreui": "^4.1.3"
  }
}

```
