@rhavenside/baseline 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +472 -0
  2. package/dist/base-line.css +5 -0
  3. package/dist/base-line.css.map +1 -0
  4. package/dist/fonts/GoogleSansCode-Bold.ttf +0 -0
  5. package/dist/fonts/GoogleSansCode-BoldItalic.ttf +0 -0
  6. package/dist/fonts/GoogleSansCode-ExtraBold.ttf +0 -0
  7. package/dist/fonts/GoogleSansCode-ExtraBoldItalic.ttf +0 -0
  8. package/dist/fonts/GoogleSansCode-Italic-VariableFont_wght.ttf +0 -0
  9. package/dist/fonts/GoogleSansCode-Italic.ttf +0 -0
  10. package/dist/fonts/GoogleSansCode-Light.ttf +0 -0
  11. package/dist/fonts/GoogleSansCode-LightItalic.ttf +0 -0
  12. package/dist/fonts/GoogleSansCode-Medium.ttf +0 -0
  13. package/dist/fonts/GoogleSansCode-MediumItalic.ttf +0 -0
  14. package/dist/fonts/GoogleSansCode-Regular.ttf +0 -0
  15. package/dist/fonts/GoogleSansCode-SemiBold.ttf +0 -0
  16. package/dist/fonts/GoogleSansCode-SemiBoldItalic.ttf +0 -0
  17. package/dist/fonts/GoogleSansCode-VariableFont_wght.ttf +0 -0
  18. package/dist/fonts/RobotoCondensed-Black.ttf +0 -0
  19. package/dist/fonts/RobotoCondensed-BlackItalic.ttf +0 -0
  20. package/dist/fonts/RobotoCondensed-Bold.ttf +0 -0
  21. package/dist/fonts/RobotoCondensed-BoldItalic.ttf +0 -0
  22. package/dist/fonts/RobotoCondensed-ExtraBold.ttf +0 -0
  23. package/dist/fonts/RobotoCondensed-ExtraBoldItalic.ttf +0 -0
  24. package/dist/fonts/RobotoCondensed-ExtraLight.ttf +0 -0
  25. package/dist/fonts/RobotoCondensed-ExtraLightItalic.ttf +0 -0
  26. package/dist/fonts/RobotoCondensed-Italic-VariableFont_wght.ttf +0 -0
  27. package/dist/fonts/RobotoCondensed-Italic.ttf +0 -0
  28. package/dist/fonts/RobotoCondensed-Light.ttf +0 -0
  29. package/dist/fonts/RobotoCondensed-LightItalic.ttf +0 -0
  30. package/dist/fonts/RobotoCondensed-Medium.ttf +0 -0
  31. package/dist/fonts/RobotoCondensed-MediumItalic.ttf +0 -0
  32. package/dist/fonts/RobotoCondensed-Regular.ttf +0 -0
  33. package/dist/fonts/RobotoCondensed-SemiBold.ttf +0 -0
  34. package/dist/fonts/RobotoCondensed-SemiBoldItalic.ttf +0 -0
  35. package/dist/fonts/RobotoCondensed-Thin.ttf +0 -0
  36. package/dist/fonts/RobotoCondensed-ThinItalic.ttf +0 -0
  37. package/dist/fonts/RobotoCondensed-VariableFont_wght.ttf +0 -0
  38. package/dist/fonts/ZalandoSansExpanded-Black.ttf +0 -0
  39. package/dist/fonts/ZalandoSansExpanded-BlackItalic.ttf +0 -0
  40. package/dist/fonts/ZalandoSansExpanded-Bold.ttf +0 -0
  41. package/dist/fonts/ZalandoSansExpanded-BoldItalic.ttf +0 -0
  42. package/dist/fonts/ZalandoSansExpanded-ExtraBold.ttf +0 -0
  43. package/dist/fonts/ZalandoSansExpanded-ExtraBoldItalic.ttf +0 -0
  44. package/dist/fonts/ZalandoSansExpanded-ExtraLight.ttf +0 -0
  45. package/dist/fonts/ZalandoSansExpanded-ExtraLightItalic.ttf +0 -0
  46. package/dist/fonts/ZalandoSansExpanded-Italic-VariableFont_wght.ttf +0 -0
  47. package/dist/fonts/ZalandoSansExpanded-Italic.ttf +0 -0
  48. package/dist/fonts/ZalandoSansExpanded-Light.ttf +0 -0
  49. package/dist/fonts/ZalandoSansExpanded-LightItalic.ttf +0 -0
  50. package/dist/fonts/ZalandoSansExpanded-Medium.ttf +0 -0
  51. package/dist/fonts/ZalandoSansExpanded-MediumItalic.ttf +0 -0
  52. package/dist/fonts/ZalandoSansExpanded-Regular.ttf +0 -0
  53. package/dist/fonts/ZalandoSansExpanded-SemiBold.ttf +0 -0
  54. package/dist/fonts/ZalandoSansExpanded-SemiBoldItalic.ttf +0 -0
  55. package/dist/fonts/ZalandoSansExpanded-VariableFont_wght.ttf +0 -0
  56. package/dist/fonts/baseline-icons.woff +0 -0
  57. package/dist/fonts/baseline-icons.woff2 +0 -0
  58. package/dist/js/accordion.js +103 -0
  59. package/dist/js/alert.js +91 -0
  60. package/dist/js/base.js +146 -0
  61. package/dist/js/button.js +80 -0
  62. package/dist/js/carousel.js +427 -0
  63. package/dist/js/collapse.js +233 -0
  64. package/dist/js/color-modes.js +70 -0
  65. package/dist/js/component.js +114 -0
  66. package/dist/js/dropdown.js +348 -0
  67. package/dist/js/index.js +108 -0
  68. package/dist/js/modal.js +440 -0
  69. package/dist/js/offcanvas.js +356 -0
  70. package/dist/js/popover.js +241 -0
  71. package/dist/js/swipe.js +143 -0
  72. package/dist/js/tab.js +285 -0
  73. package/dist/js/toast.js +228 -0
  74. package/dist/js/tooltip.js +716 -0
  75. package/dist/js/util/backdrop.js +133 -0
  76. package/dist/js/util/component-functions.js +111 -0
  77. package/dist/js/util/focustrap.js +101 -0
  78. package/dist/js/util/scrollbar.js +111 -0
  79. package/dist/js/util.js +564 -0
  80. package/package.json +47 -0
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Base-Line Swipe Utility
3
+ * Baseline 2.0 compatible swipe with Base-Line naming
4
+ */
5
+
6
+ import { on, off } from './util.js'
7
+
8
+ const NAME = 'swipe'
9
+ const EVENT_KEY = `.c.${NAME}`
10
+ const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
11
+ const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`
12
+ const EVENT_TOUCHEND = `touchend${EVENT_KEY}`
13
+ const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
14
+ const EVENT_POINTERUP = `pointerup${EVENT_KEY}`
15
+ const POINTER_TYPE_TOUCH = 'touch'
16
+ const POINTER_TYPE_PEN = 'pen'
17
+ const CLASS_NAME_POINTER_EVENT = 'pointer-event'
18
+ const SWIPE_THRESHOLD = 40
19
+
20
+ const Default = {
21
+ endCallback: null,
22
+ leftCallback: null,
23
+ rightCallback: null
24
+ }
25
+
26
+ class Swipe {
27
+ constructor(element, config = {}) {
28
+ this._element = element
29
+ this._config = { ...Default, ...config }
30
+
31
+ if (!element || !Swipe.isSupported()) {
32
+ return
33
+ }
34
+
35
+ this._deltaX = 0
36
+ this._supportPointerEvents = Boolean(window.PointerEvent)
37
+ this._initEvents()
38
+ }
39
+
40
+ static get Default() {
41
+ return Default
42
+ }
43
+
44
+ static get NAME() {
45
+ return NAME
46
+ }
47
+
48
+ static isSupported() {
49
+ return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
50
+ }
51
+
52
+ dispose() {
53
+ if (this._boundStart) {
54
+ if (this._supportPointerEvents) {
55
+ off(this._element, EVENT_POINTERDOWN, this._boundStart)
56
+ off(this._element, EVENT_POINTERUP, this._boundEnd)
57
+ } else {
58
+ off(this._element, EVENT_TOUCHSTART, this._boundStart)
59
+ off(this._element, EVENT_TOUCHMOVE, this._boundMove)
60
+ off(this._element, EVENT_TOUCHEND, this._boundEnd)
61
+ }
62
+ }
63
+ if (this._element && this._element.classList) {
64
+ this._element.classList.remove(CLASS_NAME_POINTER_EVENT)
65
+ }
66
+ }
67
+
68
+ _start(event) {
69
+ if (!this._supportPointerEvents) {
70
+ this._deltaX = event.touches[0].clientX
71
+ return
72
+ }
73
+
74
+ if (this._eventIsPointerPenTouch(event)) {
75
+ this._deltaX = event.clientX
76
+ }
77
+ }
78
+
79
+ _end(event) {
80
+ if (this._eventIsPointerPenTouch(event)) {
81
+ this._deltaX = event.clientX - this._deltaX
82
+ }
83
+
84
+ this._handleSwipe()
85
+ if (this._config.endCallback) {
86
+ this._config.endCallback()
87
+ }
88
+ }
89
+
90
+ _move(event) {
91
+ this._deltaX = event.touches && event.touches.length > 1 ?
92
+ 0 :
93
+ event.touches[0].clientX - this._deltaX
94
+ }
95
+
96
+ _handleSwipe() {
97
+ const absDeltaX = Math.abs(this._deltaX)
98
+
99
+ if (absDeltaX <= SWIPE_THRESHOLD) {
100
+ return
101
+ }
102
+
103
+ const direction = absDeltaX / this._deltaX
104
+
105
+ this._deltaX = 0
106
+
107
+ if (!direction) {
108
+ return
109
+ }
110
+
111
+ if (direction > 0 && this._config.rightCallback) {
112
+ this._config.rightCallback()
113
+ } else if (direction < 0 && this._config.leftCallback) {
114
+ this._config.leftCallback()
115
+ }
116
+ }
117
+
118
+ _initEvents() {
119
+ this._boundStart = (event) => this._start(event)
120
+ this._boundMove = (event) => this._move(event)
121
+ this._boundEnd = (event) => this._end(event)
122
+
123
+ if (this._supportPointerEvents) {
124
+ on(this._element, EVENT_POINTERDOWN, this._boundStart)
125
+ on(this._element, EVENT_POINTERUP, this._boundEnd)
126
+
127
+ if (this._element.classList) {
128
+ this._element.classList.add(CLASS_NAME_POINTER_EVENT)
129
+ }
130
+ } else {
131
+ on(this._element, EVENT_TOUCHSTART, this._boundStart)
132
+ on(this._element, EVENT_TOUCHMOVE, this._boundMove)
133
+ on(this._element, EVENT_TOUCHEND, this._boundEnd)
134
+ }
135
+ }
136
+
137
+ _eventIsPointerPenTouch(event) {
138
+ return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)
139
+ }
140
+ }
141
+
142
+ export default Swipe
143
+
package/dist/js/tab.js ADDED
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Base-Line Tab Component
3
+ * Baseline 2.0 compatible tabs with Base-Line naming
4
+ */
5
+
6
+ import { BaseComponent } from './base.js'
7
+ import { on, getElementFromSelector, getNextActiveElement, isDisabled, SelectorEngine } from './util.js'
8
+
9
+ const NAME = 'tab'
10
+ const DATA_KEY = `c.${NAME}`
11
+ const EVENT_KEY = `.${DATA_KEY}`
12
+
13
+ const EVENT_HIDE = `hide${EVENT_KEY}`
14
+ const EVENT_HIDDEN = `hidden${EVENT_KEY}`
15
+ const EVENT_SHOW = `show${EVENT_KEY}`
16
+ const EVENT_SHOWN = `shown${EVENT_KEY}`
17
+ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}`
18
+ const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
19
+ const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`
20
+
21
+ const ARROW_LEFT_KEY = 'ArrowLeft'
22
+ const ARROW_RIGHT_KEY = 'ArrowRight'
23
+ const ARROW_UP_KEY = 'ArrowUp'
24
+ const ARROW_DOWN_KEY = 'ArrowDown'
25
+ const HOME_KEY = 'Home'
26
+ const END_KEY = 'End'
27
+
28
+ const CLASS_NAME_ACTIVE = 'is-active'
29
+ const CLASS_NAME_FADE = 'c-fade'
30
+ const CLASS_NAME_SHOW = 'is-show'
31
+ const CLASS_DROPDOWN = 'c-dropdown'
32
+
33
+ const SELECTOR_DROPDOWN_TOGGLE = '.c-dropdown-toggle'
34
+ const SELECTOR_DROPDOWN_MENU = '.c-dropdown-menu'
35
+ const NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`
36
+
37
+ const SELECTOR_TAB_PANEL = '.c-list-group, .c-nav, [role="tablist"]'
38
+ const SELECTOR_OUTER = '.c-nav-item, .c-list-group-item'
39
+ const SELECTOR_INNER = `.c-nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .c-list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role="tab"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`
40
+ const SELECTOR_DATA_TOGGLE = '[data-c-toggle="tab"], [data-c-toggle="pill"], [data-bl-toggle="tab"], [data-bl-toggle="pill"], [data-bl-toggle="list"]'
41
+ const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`
42
+
43
+ const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-c-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-c-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bl-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-bl-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bl-toggle="list"]`
44
+
45
+ class Tab extends BaseComponent {
46
+ constructor(element) {
47
+ super(element)
48
+ this._parent = this._element.closest(SELECTOR_TAB_PANEL)
49
+
50
+ if (!this._parent) {
51
+ return
52
+ }
53
+
54
+ // Set up initial aria attributes
55
+ this._setInitialAttributes(this._parent, this._getChildren())
56
+
57
+ on(this._element, EVENT_KEYDOWN, (event) => this._keydown(event))
58
+ }
59
+
60
+ static get NAME() {
61
+ return NAME
62
+ }
63
+
64
+ // Public
65
+ show() {
66
+ const innerElem = this._element
67
+ if (this._elemIsActive(innerElem)) {
68
+ return
69
+ }
70
+
71
+ // Search for active tab on same parent to deactivate it
72
+ const active = this._getActiveElem()
73
+
74
+ const hideEvent = active ?
75
+ this._triggerEvent(active, EVENT_HIDE, { relatedTarget: innerElem }) :
76
+ null
77
+
78
+ const showEvent = this._triggerEvent(innerElem, EVENT_SHOW, { relatedTarget: active })
79
+
80
+ if (showEvent.defaultPrevented || (hideEvent && hideEvent.defaultPrevented)) {
81
+ return
82
+ }
83
+
84
+ this._deactivate(active, innerElem)
85
+ this._activate(innerElem, active)
86
+ }
87
+
88
+ // Private
89
+ _activate(element, relatedElem) {
90
+ if (!element) {
91
+ return
92
+ }
93
+
94
+ element.classList.add(CLASS_NAME_ACTIVE)
95
+
96
+ this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section
97
+
98
+ const complete = () => {
99
+ if (element.getAttribute('role') !== 'tab') {
100
+ element.classList.add(CLASS_NAME_SHOW)
101
+ return
102
+ }
103
+
104
+ element.removeAttribute('tabindex')
105
+ element.setAttribute('aria-selected', true)
106
+ this._toggleDropDown(element, true)
107
+ this._triggerEvent(element, EVENT_SHOWN, {
108
+ relatedTarget: relatedElem
109
+ })
110
+ }
111
+
112
+ this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))
113
+ }
114
+
115
+ _deactivate(element, relatedElem) {
116
+ if (!element) {
117
+ return
118
+ }
119
+
120
+ element.classList.remove(CLASS_NAME_ACTIVE)
121
+ element.blur()
122
+
123
+ this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too
124
+
125
+ const complete = () => {
126
+ if (element.getAttribute('role') !== 'tab') {
127
+ element.classList.remove(CLASS_NAME_SHOW)
128
+ return
129
+ }
130
+
131
+ element.setAttribute('aria-selected', false)
132
+ element.setAttribute('tabindex', '-1')
133
+ this._toggleDropDown(element, false)
134
+ this._triggerEvent(element, EVENT_HIDDEN, { relatedTarget: relatedElem })
135
+ }
136
+
137
+ this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))
138
+ }
139
+
140
+ _keydown(event) {
141
+ if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key))) {
142
+ return
143
+ }
144
+
145
+ event.stopPropagation()
146
+ event.preventDefault()
147
+
148
+ const children = this._getChildren().filter(element => !isDisabled(element))
149
+ let nextActiveElement
150
+
151
+ if ([HOME_KEY, END_KEY].includes(event.key)) {
152
+ nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]
153
+ } else {
154
+ const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)
155
+ nextActiveElement = getNextActiveElement(children, event.target, isNext, true)
156
+ }
157
+
158
+ if (nextActiveElement) {
159
+ nextActiveElement.focus({ preventScroll: true })
160
+ Tab.getOrCreateInstance(nextActiveElement).show()
161
+ }
162
+ }
163
+
164
+ _getChildren() {
165
+ return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent)
166
+ }
167
+
168
+ _getActiveElem() {
169
+ return this._getChildren().find(child => this._elemIsActive(child)) || null
170
+ }
171
+
172
+ _setInitialAttributes(parent, children) {
173
+ this._setAttributeIfNotExists(parent, 'role', 'tablist')
174
+
175
+ for (const child of children) {
176
+ this._setInitialAttributesOnChild(child)
177
+ }
178
+ }
179
+
180
+ _setInitialAttributesOnChild(child) {
181
+ child = this._getInnerElement(child)
182
+ const isActive = this._elemIsActive(child)
183
+ const outerElem = this._getOuterElement(child)
184
+ child.setAttribute('aria-selected', isActive)
185
+
186
+ if (outerElem !== child) {
187
+ this._setAttributeIfNotExists(outerElem, 'role', 'presentation')
188
+ }
189
+
190
+ if (!isActive) {
191
+ child.setAttribute('tabindex', '-1')
192
+ }
193
+
194
+ this._setAttributeIfNotExists(child, 'role', 'tab')
195
+
196
+ // set attributes to the related panel too
197
+ this._setInitialAttributesOnTargetPanel(child)
198
+ }
199
+
200
+ _setInitialAttributesOnTargetPanel(child) {
201
+ const target = SelectorEngine.getElementFromSelector(child)
202
+
203
+ if (!target) {
204
+ return
205
+ }
206
+
207
+ this._setAttributeIfNotExists(target, 'role', 'tabpanel')
208
+
209
+ if (child.id) {
210
+ this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`)
211
+ }
212
+ }
213
+
214
+ _toggleDropDown(element, open) {
215
+ const outerElem = this._getOuterElement(element)
216
+ if (!outerElem.classList.contains(CLASS_DROPDOWN)) {
217
+ return
218
+ }
219
+
220
+ const toggle = (selector, className) => {
221
+ const elem = SelectorEngine.findOne(selector, outerElem)
222
+ if (elem) {
223
+ elem.classList.toggle(className, open)
224
+ }
225
+ }
226
+
227
+ toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE)
228
+ toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW)
229
+ outerElem.setAttribute('aria-expanded', open)
230
+ }
231
+
232
+ _setAttributeIfNotExists(element, attribute, value) {
233
+ if (!element.hasAttribute(attribute)) {
234
+ element.setAttribute(attribute, value)
235
+ }
236
+ }
237
+
238
+ _elemIsActive(elem) {
239
+ return elem.classList.contains(CLASS_NAME_ACTIVE)
240
+ }
241
+
242
+ _getInnerElement(elem) {
243
+ return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem)
244
+ }
245
+
246
+ _getOuterElement(elem) {
247
+ return elem.closest(SELECTOR_OUTER) || elem
248
+ }
249
+
250
+ _triggerEvent(element, eventName, detail = {}) {
251
+ const event = new CustomEvent(eventName, {
252
+ bubbles: true,
253
+ cancelable: true,
254
+ detail
255
+ })
256
+ element.dispatchEvent(event)
257
+ return event
258
+ }
259
+
260
+ static getOrCreateInstance(element) {
261
+ return element._baseLineComponent || new Tab(element)
262
+ }
263
+ }
264
+
265
+ // Data API implementation
266
+ on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
267
+ if (['A', 'AREA'].includes(this.tagName)) {
268
+ event.preventDefault()
269
+ }
270
+
271
+ if (isDisabled(this)) {
272
+ return
273
+ }
274
+
275
+ Tab.getOrCreateInstance(this).show()
276
+ })
277
+
278
+ // Initialize on focus
279
+ on(window, EVENT_LOAD_DATA_API, () => {
280
+ for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {
281
+ Tab.getOrCreateInstance(element)
282
+ }
283
+ })
284
+
285
+ export default Tab
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Base-Line Toast Component
3
+ * Baseline 2.0 compatible toast with Base-Line naming
4
+ * Based on Baseline-main architecture
5
+ */
6
+
7
+ import { BaseComponent } from './base.js'
8
+ import { EventHandler } from './util.js'
9
+ import { enableDismissTrigger } from './util/component-functions.js'
10
+ import { defineJQueryPlugin, reflow } from './util.js'
11
+
12
+ const NAME = 'toast'
13
+ const DATA_KEY = `c.${NAME}`
14
+ const EVENT_KEY = `.${DATA_KEY}`
15
+
16
+ const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`
17
+ const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`
18
+ const EVENT_FOCUSIN = `focusin${EVENT_KEY}`
19
+ const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`
20
+ const EVENT_HIDE = `hide${EVENT_KEY}`
21
+ const EVENT_HIDDEN = `hidden${EVENT_KEY}`
22
+ const EVENT_SHOW = `show${EVENT_KEY}`
23
+ const EVENT_SHOWN = `shown${EVENT_KEY}`
24
+
25
+ const CLASS_NAME_FADE = 'c-fade'
26
+ const CLASS_NAME_HIDE = 'c-hide' // @deprecated - kept here only for backwards compatibility
27
+ const CLASS_NAME_SHOW = 'is-show'
28
+ const CLASS_NAME_SHOWING = 'is-showing'
29
+
30
+ const DefaultType = {
31
+ animation: 'boolean',
32
+ autohide: 'boolean',
33
+ delay: 'number'
34
+ }
35
+
36
+ const Default = {
37
+ animation: true,
38
+ autohide: true,
39
+ delay: 5000
40
+ }
41
+
42
+ /**
43
+ * Class definition
44
+ */
45
+
46
+ class Toast extends BaseComponent {
47
+ constructor(element, config) {
48
+ super(element, config)
49
+
50
+ this._timeout = null
51
+ this._hasMouseInteraction = false
52
+ this._hasKeyboardInteraction = false
53
+ this._setListeners()
54
+ }
55
+
56
+ // Getters
57
+ static get Default() {
58
+ return Default
59
+ }
60
+
61
+ static get DefaultType() {
62
+ return DefaultType
63
+ }
64
+
65
+ static get NAME() {
66
+ return NAME
67
+ }
68
+
69
+ // Public
70
+ show() {
71
+ const showEvent = EventHandler.trigger(this._element, EVENT_SHOW)
72
+
73
+ if (showEvent.defaultPrevented) {
74
+ return
75
+ }
76
+
77
+ this._clearTimeout()
78
+
79
+ if (this._config.animation) {
80
+ this._element.classList.add(CLASS_NAME_FADE)
81
+ }
82
+
83
+ const complete = () => {
84
+ this._element.classList.remove(CLASS_NAME_SHOWING)
85
+ EventHandler.trigger(this._element, EVENT_SHOWN)
86
+
87
+ this._maybeScheduleHide()
88
+ }
89
+
90
+ this._element.classList.remove(CLASS_NAME_HIDE) // @deprecated
91
+ reflow(this._element)
92
+ this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING)
93
+
94
+ this._queueCallback(complete, this._element, this._config.animation)
95
+ }
96
+
97
+ hide() {
98
+ if (!this.isShown()) {
99
+ return
100
+ }
101
+
102
+ const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)
103
+
104
+ if (hideEvent.defaultPrevented) {
105
+ return
106
+ }
107
+
108
+ const complete = () => {
109
+ this._element.classList.add(CLASS_NAME_HIDE) // @deprecated
110
+ this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW)
111
+ EventHandler.trigger(this._element, EVENT_HIDDEN)
112
+ }
113
+
114
+ this._element.classList.add(CLASS_NAME_SHOWING)
115
+ this._queueCallback(complete, this._element, this._config.animation)
116
+ }
117
+
118
+ dispose() {
119
+ this._clearTimeout()
120
+
121
+ if (this.isShown()) {
122
+ this._element.classList.remove(CLASS_NAME_SHOW)
123
+ }
124
+
125
+ super.dispose()
126
+ }
127
+
128
+ isShown() {
129
+ return this._element.classList.contains(CLASS_NAME_SHOW)
130
+ }
131
+
132
+ // Private
133
+ _maybeScheduleHide() {
134
+ if (!this._config.autohide) {
135
+ return
136
+ }
137
+
138
+ if (this._hasMouseInteraction || this._hasKeyboardInteraction) {
139
+ return
140
+ }
141
+
142
+ this._timeout = setTimeout(() => {
143
+ this.hide()
144
+ }, this._config.delay)
145
+ }
146
+
147
+ _onInteraction(event, isInteracting) {
148
+ switch (event.type) {
149
+ case 'mouseover':
150
+ case 'mouseout': {
151
+ this._hasMouseInteraction = isInteracting
152
+ break
153
+ }
154
+
155
+ case 'focusin':
156
+ case 'focusout': {
157
+ this._hasKeyboardInteraction = isInteracting
158
+ break
159
+ }
160
+
161
+ default: {
162
+ break
163
+ }
164
+ }
165
+
166
+ if (isInteracting) {
167
+ this._clearTimeout()
168
+ return
169
+ }
170
+
171
+ const nextElement = event.relatedTarget
172
+ if (this._element === nextElement || this._element.contains(nextElement)) {
173
+ return
174
+ }
175
+
176
+ this._maybeScheduleHide()
177
+ }
178
+
179
+ _setListeners() {
180
+ EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true))
181
+ EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false))
182
+ EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true))
183
+ EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false))
184
+ }
185
+
186
+ _clearTimeout() {
187
+ clearTimeout(this._timeout)
188
+ this._timeout = null
189
+ }
190
+
191
+ // Static
192
+ static getOrCreateInstance(element, config = {}) {
193
+ return element._baseLineComponent || new Toast(element, config)
194
+ }
195
+
196
+ static getInstance(element) {
197
+ return element._baseLineComponent || null
198
+ }
199
+
200
+ static jQueryInterface(config) {
201
+ return this.each(function () {
202
+ const data = Toast.getOrCreateInstance(this, config)
203
+
204
+ if (typeof config === 'string') {
205
+ if (typeof data[config] === 'undefined') {
206
+ throw new TypeError(`No method named "${config}"`)
207
+ }
208
+
209
+ data[config]()
210
+ }
211
+ })
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Data API implementation
217
+ */
218
+
219
+ enableDismissTrigger(Toast)
220
+
221
+ /**
222
+ * jQuery
223
+ */
224
+
225
+ defineJQueryPlugin(Toast)
226
+
227
+ export default Toast
228
+