Skip to content

Commit 7e6f44a

Browse files
committed
Refactor Toast as BaseComponent class with native CSS autohide
1 parent d38c1db commit 7e6f44a

6 files changed

Lines changed: 448 additions & 725 deletions

File tree

js/src/toast.js

Lines changed: 12 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import BaseComponent from './base-component.js'
99
import EventHandler from './dom/event-handler.js'
1010
import { enableDismissTrigger } from './util/component-functions.js'
11-
import { reflow } from './util/index.js'
1211

1312
/**
1413
* Constants
@@ -18,57 +17,28 @@ const NAME = 'toast'
1817
const DATA_KEY = 'bs.toast'
1918
const EVENT_KEY = `.${DATA_KEY}`
2019

21-
const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`
22-
const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`
23-
const EVENT_FOCUSIN = `focusin${EVENT_KEY}`
24-
const EVENT_FOCUSOUT = `focusout${EVENT_KEY}`
2520
const EVENT_HIDE = `hide${EVENT_KEY}`
2621
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
2722
const EVENT_SHOW = `show${EVENT_KEY}`
2823
const EVENT_SHOWN = `shown${EVENT_KEY}`
2924

30-
const CLASS_NAME_FADE = 'fade'
31-
const CLASS_NAME_HIDE = 'hide' // @deprecated - kept here only for backwards compatibility
3225
const CLASS_NAME_SHOW = 'show'
33-
const CLASS_NAME_SHOWING = 'showing'
34-
35-
const DefaultType = {
36-
animation: 'boolean',
37-
autohide: 'boolean',
38-
delay: 'number'
39-
}
40-
41-
const Default = {
42-
animation: true,
43-
autohide: true,
44-
delay: 5000
45-
}
26+
const ANIMATION_NAME_AUTOHIDE = 'toast-autohide'
4627

4728
/**
4829
* Class definition
4930
*/
5031

5132
class Toast extends BaseComponent {
52-
constructor(element, config) {
53-
super(element, config)
54-
55-
this._timeout = null
56-
this._hasMouseInteraction = false
57-
this._hasKeyboardInteraction = false
58-
this._setListeners()
59-
}
60-
6133
// Getters
62-
static get Default() {
63-
return Default
34+
static get NAME() {
35+
return NAME
6436
}
6537

66-
static get DefaultType() {
67-
return DefaultType
68-
}
38+
constructor(element, config) {
39+
super(element, config)
6940

70-
static get NAME() {
71-
return NAME
41+
EventHandler.on(this._element, 'animationend', event => this._onAnimationEnd(event))
7242
}
7343

7444
// Public
@@ -79,118 +49,26 @@ class Toast extends BaseComponent {
7949
return
8050
}
8151

82-
this._clearTimeout()
83-
84-
if (this._config.animation) {
85-
this._element.classList.add(CLASS_NAME_FADE)
86-
}
87-
88-
const complete = () => {
89-
this._element.classList.remove(CLASS_NAME_SHOWING)
90-
EventHandler.trigger(this._element, EVENT_SHOWN)
91-
92-
this._maybeScheduleHide()
93-
}
94-
95-
this._element.classList.remove(CLASS_NAME_HIDE) // @deprecated
96-
reflow(this._element)
97-
this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING)
98-
99-
this._queueCallback(complete, this._element, this._config.animation)
52+
this._element.classList.add(CLASS_NAME_SHOW)
53+
EventHandler.trigger(this._element, EVENT_SHOWN)
10054
}
10155

10256
hide() {
103-
if (!this.isShown()) {
104-
return
105-
}
106-
10757
const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)
10858

10959
if (hideEvent.defaultPrevented) {
11060
return
11161
}
11262

113-
const complete = () => {
114-
this._element.classList.add(CLASS_NAME_HIDE) // @deprecated
115-
this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW)
116-
EventHandler.trigger(this._element, EVENT_HIDDEN)
117-
}
118-
119-
this._element.classList.add(CLASS_NAME_SHOWING)
120-
this._queueCallback(complete, this._element, this._config.animation)
121-
}
122-
123-
dispose() {
124-
this._clearTimeout()
125-
126-
if (this.isShown()) {
127-
this._element.classList.remove(CLASS_NAME_SHOW)
128-
}
129-
130-
super.dispose()
131-
}
132-
133-
isShown() {
134-
return this._element.classList.contains(CLASS_NAME_SHOW)
63+
this._element.classList.remove(CLASS_NAME_SHOW)
64+
EventHandler.trigger(this._element, EVENT_HIDDEN)
13565
}
13666

13767
// Private
138-
_maybeScheduleHide() {
139-
if (!this._config.autohide) {
140-
return
141-
}
142-
143-
if (this._hasMouseInteraction || this._hasKeyboardInteraction) {
144-
return
145-
}
146-
147-
this._timeout = setTimeout(() => {
68+
_onAnimationEnd(event) {
69+
if (event.animationName === ANIMATION_NAME_AUTOHIDE) {
14870
this.hide()
149-
}, this._config.delay)
150-
}
151-
152-
_onInteraction(event, isInteracting) {
153-
switch (event.type) {
154-
case 'mouseover':
155-
case 'mouseout': {
156-
this._hasMouseInteraction = isInteracting
157-
break
158-
}
159-
160-
case 'focusin':
161-
case 'focusout': {
162-
this._hasKeyboardInteraction = isInteracting
163-
break
164-
}
165-
166-
default: {
167-
break
168-
}
16971
}
170-
171-
if (isInteracting) {
172-
this._clearTimeout()
173-
return
174-
}
175-
176-
const nextElement = event.relatedTarget
177-
if (this._element === nextElement || this._element.contains(nextElement)) {
178-
return
179-
}
180-
181-
this._maybeScheduleHide()
182-
}
183-
184-
_setListeners() {
185-
EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true))
186-
EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false))
187-
EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true))
188-
EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false))
189-
}
190-
191-
_clearTimeout() {
192-
clearTimeout(this._timeout)
193-
this._timeout = null
19472
}
19573
}
19674

0 commit comments

Comments
 (0)