diff --git a/js/index.esm.js b/js/index.esm.js index 0c03cb2ec..0470ec260 100644 --- a/js/index.esm.js +++ b/js/index.esm.js @@ -16,4 +16,5 @@ export { default as Popover } from './src/popover' export { default as ScrollSpy } from './src/scrollspy' export { default as Tab } from './src/tab' export { default as Toast } from './src/toast' +export { default as Toggler } from './src/toggler' export { default as Tooltip } from './src/tooltip' diff --git a/js/index.umd.js b/js/index.umd.js index d4a5ee338..c03a7e1f4 100644 --- a/js/index.umd.js +++ b/js/index.umd.js @@ -16,6 +16,7 @@ import Popover from './src/popover' import ScrollSpy from './src/scrollspy' import Tab from './src/tab' import Toast from './src/toast' +import Toggler from './src/toggler' import Tooltip from './src/tooltip' export default { @@ -30,5 +31,6 @@ export default { ScrollSpy, Tab, Toast, + Toggler, Tooltip } diff --git a/js/src/toggler.js b/js/src/toggler.js new file mode 100644 index 000000000..d778bfaa6 --- /dev/null +++ b/js/src/toggler.js @@ -0,0 +1,104 @@ +/** + * -------------------------------------------------------------------------- + * Bootstrap (v5.1.3): toggler.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + +import { defineJQueryPlugin } from './util/index' +import EventHandler from './dom/event-handler' +import BaseComponent from './base-component' +import { eventActionOnPlugin } from './util/component-functions' + +/** + * Constants + */ + +const NAME = 'toggler' +const DATA_KEY = 'bs.toggle' +const EVENT_KEY = `.${DATA_KEY}` + +const EVENT_TOGGLE = `toggle${EVENT_KEY}` +const EVENT_TOGGLED = `toggled${EVENT_KEY}` +const EVENT_CLICK = 'click' + +const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="toggler"]' + +const DefaultType = { + attribute: 'string', + value: '(string|number|boolean)' +} + +const Default = { + attribute: 'class', + value: null +} + +/** + * Class definition + */ + +class Toggler extends BaseComponent { + // Getters + static get Default() { + return Default + } + + static get DefaultType() { + return DefaultType + } + + static get NAME() { + return NAME + } + + _configAfterMerge(config) { + return config + } + + // Private + toggle() { + const toggleEvent = EventHandler.trigger(this._element, EVENT_TOGGLE) + + if (toggleEvent.defaultPrevented) { + return + } + + this._execute() + + EventHandler.trigger(this._element, EVENT_TOGGLED) + } + + _execute() { + const { attribute, value } = this._config + + if (attribute === 'id') { + return // You have to be kidding + } + + if (attribute === 'class') { + this._element.classList.toggle(value) + return + } + + if (this._element.getAttribute(attribute) === value) { + this._element.removeAttribute(attribute) + return + } + + this._element.setAttribute(attribute, value) + } +} + +/** + * Data API implementation + */ + +eventActionOnPlugin(Toggler, EVENT_CLICK, SELECTOR_DATA_TOGGLE, 'toggle') +/** + * jQuery + */ + +defineJQueryPlugin(Toggler) + +export default Toggler diff --git a/js/src/util/component-functions.js b/js/src/util/component-functions.js index f1f0701c5..496638787 100644 --- a/js/src/util/component-functions.js +++ b/js/src/util/component-functions.js @@ -6,7 +6,8 @@ */ import EventHandler from '../dom/event-handler' -import { getElementFromSelector, isDisabled } from './index' +import { getElementFromSelector, getSelectorFromElement, isDisabled } from './index' +import SelectorEngine from '../dom/selector-engine' const enableDismissTrigger = (component, method = 'hide') => { const clickEvent = `click.dismiss${component.EVENT_KEY}` @@ -29,6 +30,34 @@ const enableDismissTrigger = (component, method = 'hide') => { }) } -export { - enableDismissTrigger +const eventActionOnPlugin = (Plugin, onEvent, stringSelector, method, callback = null) => { + eventAction(`${onEvent}.${Plugin.NAME}`, stringSelector, data => { + const instances = data.targets.filter(Boolean).map(element => Plugin.getOrCreateInstance(element)) + if (typeof callback === 'function') { + callback({ ...data, instances }) + } + + for (const instance of instances) { + instance[method]() + } + }) +} + +const eventAction = (onEvent, stringSelector, callback) => { + const selector = `${stringSelector}:not(.disabled):not(:disabled)` + EventHandler.on(document, onEvent, selector, function (event) { + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault() + } + + const selector = getSelectorFromElement(this) + const targets = selector ? SelectorEngine.find(selector) : [this] + + callback({ targets, event }) + }) +} + +export { + enableDismissTrigger, + eventActionOnPlugin } diff --git a/site/content/docs/5.1/components/toggler.md b/site/content/docs/5.1/components/toggler.md new file mode 100644 index 000000000..80dd768eb --- /dev/null +++ b/site/content/docs/5.1/components/toggler.md @@ -0,0 +1,168 @@ +--- +layout: docs +title: Attribute Toggler +description: Toggle attributes or classes, using simple html markup +group: components +toc: true +--- + +## How it works + +Attribute Toggler is a simple component, that preferable can be used to avoid writing small JavaScript snippets handle attributes toggling, during `click` events. +Manipulation id done on the same element or on a targeted element + +**Heads up!** Toggler may handle all attributes, except `id's` + + +## Examples + +### Toggle class + +Below are some examples of class manipulation + +{{< example >}} +