UNPKG

@coreui/coreui

Version:

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

340 lines (322 loc) 11.2 kB
/*! * CoreUI chip.js v5.9.0 (https://coreui.io) * Copyright 2026 The CoreUI Team (https://github.com/orgs/coreui/people) * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/sanitizer.js'), require('./util/index.js')) : typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/sanitizer', './util/index'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Chip = factory(global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Sanitizer, global.Index)); })(this, (function (BaseComponent, EventHandler, Manipulator, SelectorEngine, sanitizer_js, index_js) { 'use strict'; /** * -------------------------------------------------------------------------- * CoreUI chip.js * Licensed under MIT (https://github.com/coreui/coreui/blob/main/LICENSE) * -------------------------------------------------------------------------- */ /** * Constants */ const NAME = 'chip'; const DATA_KEY = 'coreui.chip'; const EVENT_KEY = `.${DATA_KEY}`; const DATA_API_KEY = '.data-api'; const EVENT_REMOVE = `remove${EVENT_KEY}`; const EVENT_REMOVED = `removed${EVENT_KEY}`; const EVENT_SELECT = `select${EVENT_KEY}`; const EVENT_SELECTED = `selected${EVENT_KEY}`; const EVENT_DESELECT = `deselect${EVENT_KEY}`; const EVENT_DESELECTED = `deselected${EVENT_KEY}`; const SELECTOR_CHIP_CHECK = '.chip-check'; const SELECTOR_CHIP_REMOVE = '.chip-remove'; const SELECTOR_DATA_CHIP = '[data-coreui-chip]'; const CLASS_NAME_CHIP_CHECK = 'chip-check'; const CLASS_NAME_CHIP_CLICKABLE = 'chip-clickable'; const CLASS_NAME_CHIP_REMOVE = 'chip-remove'; const CLASS_NAME_ACTIVE = 'active'; const CLASS_NAME_DISABLED = 'disabled'; const DEFAULT_REMOVE_ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="4" y1="4" x2="12" y2="12"/><line x1="12" y1="4" x2="4" y2="12"/></svg>'; const DEFAULT_SELECTED_ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 512 512" fill="currentColor"><path d="M425.373 89.373 196 318.745 86.627 209.373l-45.254 45.254L196 409.255l274.627-274.628z"/></svg>'; const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']); const Default = { allowList: sanitizer_js.SVGAllowlist, ariaRemoveLabel: 'Remove', disabled: false, filter: false, removable: false, removeIcon: DEFAULT_REMOVE_ICON, sanitize: true, sanitizeFn: null, selectable: false, selected: false, selectedIcon: DEFAULT_SELECTED_ICON }; const DefaultType = { allowList: 'object', ariaRemoveLabel: 'string', disabled: 'boolean', filter: 'boolean', removable: 'boolean', removeIcon: 'string', sanitize: 'boolean', sanitizeFn: '(null|function)', selectable: 'boolean', selected: 'boolean', selectedIcon: 'string' }; /** * Class definition */ class Chip extends BaseComponent { constructor(element, config) { super(element, config); this._disabled = this._config.disabled || this._element.classList.contains(CLASS_NAME_DISABLED); this._selected = this._config.selected || this._element.classList.contains(CLASS_NAME_ACTIVE); this._ensureRemoveButton(); this._applyState(); if (this._config.selectable || this._config.removable) { this._makeFocusable(); } this._addEventListeners(); } // Getters static get Default() { return Default; } static get DefaultType() { return DefaultType; } static get NAME() { return NAME; } // Public remove() { const removeEvent = EventHandler.trigger(this._element, EVENT_REMOVE); if (removeEvent.defaultPrevented) { return; } this._destroyElement(); } toggle() { if (!this._config.selectable) { return; } if (this._selected) { this.deselect(); return; } this.select(); } select() { if (!this._config.selectable) { return; } if (this._selected) { return; } const selectEvent = EventHandler.trigger(this._element, EVENT_SELECT); if (selectEvent.defaultPrevented) { return; } this._selected = true; this._applyState(); EventHandler.trigger(this._element, EVENT_SELECTED); } deselect() { if (!this._config.selectable) { return; } if (!this._selected) { return; } const deselectEvent = EventHandler.trigger(this._element, EVENT_DESELECT); if (deselectEvent.defaultPrevented) { return; } this._selected = false; this._applyState(); EventHandler.trigger(this._element, EVENT_DESELECTED); } // Private _configAfterMerge(config) { // A filter chip is selectable by definition. if (config.filter) { config.selectable = true; } return config; } _addEventListeners() { EventHandler.on(this._element, 'keydown', event => this._handleKeydown(event)); EventHandler.on(this._element, 'click', event => { if (this._disabled) { return; } if (event.target.closest(SELECTOR_CHIP_REMOVE)) { return; } this.toggle(); }); EventHandler.on(this._element, 'click', SELECTOR_CHIP_REMOVE, event => { event.stopPropagation(); this.remove(); }); } _applyState() { if (!this._disabled && (this._config.clickable || this._config.selectable)) { this._element.classList.add(CLASS_NAME_CHIP_CLICKABLE); } if (this._disabled) { this._element.classList.add(CLASS_NAME_DISABLED); this._element.setAttribute('aria-disabled', 'true'); } else { this._element.classList.remove(CLASS_NAME_DISABLED); if (this._element.getAttribute('aria-disabled') === 'true') { this._element.setAttribute('aria-disabled', 'false'); } } if (this._config.selectable) { this._element.classList.toggle(CLASS_NAME_ACTIVE, this._selected); this._element.setAttribute('aria-selected', this._selected ? 'true' : 'false'); if (this._config.filter) { if (this._selected) { this._ensureCheckIcon(); } else { var _SelectorEngine$findO; (_SelectorEngine$findO = SelectorEngine.findOne(SELECTOR_CHIP_CHECK, this._element)) == null || _SelectorEngine$findO.remove(); } } } else { this._element.classList.remove(CLASS_NAME_ACTIVE); if (this._element.getAttribute('aria-selected') === 'true') { this._element.setAttribute('aria-selected', 'false'); } } } _ensureCheckIcon() { if (SelectorEngine.findOne(SELECTOR_CHIP_CHECK, this._element)) { return; } const check = document.createElement('span'); check.className = CLASS_NAME_CHIP_CHECK; check.setAttribute('aria-hidden', 'true'); check.innerHTML = this._sanitizeIcon(this._config.selectedIcon); this._element.prepend(check); } _createRemoveButton() { const button = document.createElement('button'); button.type = 'button'; button.className = CLASS_NAME_CHIP_REMOVE; button.setAttribute('aria-label', this._config.ariaRemoveLabel); button.setAttribute('tabindex', '-1'); // Not in tab order, chips handle keyboard button.innerHTML = this._sanitizeIcon(this._config.removeIcon); return button; } _ensureRemoveButton() { // A disabled chip is not interactive, so it never shows a remove button. if (!this._config.removable || this._disabled) { return; } if (SelectorEngine.findOne(SELECTOR_CHIP_REMOVE, this._element)) { return; } this._element.append(this._createRemoveButton()); } _makeFocusable() { if (this._element.hasAttribute('tabindex') || this._disabled) { return; } this._element.setAttribute('tabindex', '0'); } _handleKeydown(event) { const { key } = event; if (this._disabled) { return; } switch (key) { case 'Enter': case ' ': case 'Spacebar': { if (!this._config.selectable) { return; } event.preventDefault(); this.toggle(); break; } case 'Backspace': case 'Delete': { if (this._config.removable) { event.preventDefault(); this.remove(); } break; } // No default } } _destroyElement() { EventHandler.trigger(this._element, EVENT_REMOVED); this._element.remove(); this.dispose(); } _sanitizeIcon(icon) { return this._config.sanitize ? sanitizer_js.sanitizeHtml(icon, this._config.allowList, this._config.sanitizeFn) : icon; } _getConfig(config) { const dataAttributes = Manipulator.getDataAttributes(this._element); for (const dataAttribute of Object.keys(dataAttributes)) { if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) { delete dataAttributes[dataAttribute]; } } config = { ...dataAttributes, ...(typeof config === 'object' && config ? config : {}) }; config = this._mergeConfigObj(config); config = this._configAfterMerge(config); this._typeCheckConfig(config); return config; } // Static static chipInterface(element, config) { const data = Chip.getOrCreateInstance(element, config); if (typeof config === 'string') { if (typeof data[config] === 'undefined') { throw new TypeError(`No method named "${config}"`); } data[config](); } } static jQueryInterface(config) { return this.each(function () { const data = Chip.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 */ EventHandler.on(document, `DOMContentLoaded${EVENT_KEY}${DATA_API_KEY}`, () => { for (const element of SelectorEngine.find(SELECTOR_DATA_CHIP)) { Chip.chipInterface(element); } }); /** * jQuery */ index_js.defineJQueryPlugin(Chip); return Chip; })); //# sourceMappingURL=chip.js.map