@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.
- package/README.md +472 -0
- package/dist/base-line.css +5 -0
- package/dist/base-line.css.map +1 -0
- package/dist/fonts/GoogleSansCode-Bold.ttf +0 -0
- package/dist/fonts/GoogleSansCode-BoldItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-ExtraBold.ttf +0 -0
- package/dist/fonts/GoogleSansCode-ExtraBoldItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Italic-VariableFont_wght.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Italic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Light.ttf +0 -0
- package/dist/fonts/GoogleSansCode-LightItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Medium.ttf +0 -0
- package/dist/fonts/GoogleSansCode-MediumItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-Regular.ttf +0 -0
- package/dist/fonts/GoogleSansCode-SemiBold.ttf +0 -0
- package/dist/fonts/GoogleSansCode-SemiBoldItalic.ttf +0 -0
- package/dist/fonts/GoogleSansCode-VariableFont_wght.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Black.ttf +0 -0
- package/dist/fonts/RobotoCondensed-BlackItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Bold.ttf +0 -0
- package/dist/fonts/RobotoCondensed-BoldItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraBold.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraBoldItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraLight.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ExtraLightItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Italic-VariableFont_wght.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Italic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Light.ttf +0 -0
- package/dist/fonts/RobotoCondensed-LightItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Medium.ttf +0 -0
- package/dist/fonts/RobotoCondensed-MediumItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Regular.ttf +0 -0
- package/dist/fonts/RobotoCondensed-SemiBold.ttf +0 -0
- package/dist/fonts/RobotoCondensed-SemiBoldItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-Thin.ttf +0 -0
- package/dist/fonts/RobotoCondensed-ThinItalic.ttf +0 -0
- package/dist/fonts/RobotoCondensed-VariableFont_wght.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Black.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-BlackItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Bold.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-BoldItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraBold.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraBoldItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraLight.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-ExtraLightItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Italic-VariableFont_wght.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Italic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Light.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-LightItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Medium.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-MediumItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-Regular.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-SemiBold.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-SemiBoldItalic.ttf +0 -0
- package/dist/fonts/ZalandoSansExpanded-VariableFont_wght.ttf +0 -0
- package/dist/fonts/baseline-icons.woff +0 -0
- package/dist/fonts/baseline-icons.woff2 +0 -0
- package/dist/js/accordion.js +103 -0
- package/dist/js/alert.js +91 -0
- package/dist/js/base.js +146 -0
- package/dist/js/button.js +80 -0
- package/dist/js/carousel.js +427 -0
- package/dist/js/collapse.js +233 -0
- package/dist/js/color-modes.js +70 -0
- package/dist/js/component.js +114 -0
- package/dist/js/dropdown.js +348 -0
- package/dist/js/index.js +108 -0
- package/dist/js/modal.js +440 -0
- package/dist/js/offcanvas.js +356 -0
- package/dist/js/popover.js +241 -0
- package/dist/js/swipe.js +143 -0
- package/dist/js/tab.js +285 -0
- package/dist/js/toast.js +228 -0
- package/dist/js/tooltip.js +716 -0
- package/dist/js/util/backdrop.js +133 -0
- package/dist/js/util/component-functions.js +111 -0
- package/dist/js/util/focustrap.js +101 -0
- package/dist/js/util/scrollbar.js +111 -0
- package/dist/js/util.js +564 -0
- package/package.json +47 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base-Line Carousel Component
|
|
3
|
+
* Baseline 2.0 compatible carousel with Base-Line naming
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseComponent } from './base.js'
|
|
7
|
+
import { getElement, getElements, on, off, one, executeAfterTransition, reflow, triggerTransitionEnd, onDOMContentLoaded, isVisible, getNextActiveElement, getElementFromSelector } from './util.js'
|
|
8
|
+
import { addClass, removeClass, hasClass } from './component.js'
|
|
9
|
+
import Swipe from './swipe.js'
|
|
10
|
+
|
|
11
|
+
const NAME = 'carousel'
|
|
12
|
+
const DATA_KEY = `c.${NAME}`
|
|
13
|
+
const EVENT_KEY = `.${DATA_KEY}`
|
|
14
|
+
const DATA_API_KEY = '.data-api'
|
|
15
|
+
|
|
16
|
+
const ARROW_LEFT_KEY = 'ArrowLeft'
|
|
17
|
+
const ARROW_RIGHT_KEY = 'ArrowRight'
|
|
18
|
+
const TOUCHEVENT_COMPAT_WAIT = 500
|
|
19
|
+
|
|
20
|
+
const ORDER_NEXT = 'next'
|
|
21
|
+
const ORDER_PREV = 'prev'
|
|
22
|
+
const DIRECTION_LEFT = 'left'
|
|
23
|
+
const DIRECTION_RIGHT = 'right'
|
|
24
|
+
|
|
25
|
+
const EVENT_SLIDE = `slide${EVENT_KEY}`
|
|
26
|
+
const EVENT_SLID = `slid${EVENT_KEY}`
|
|
27
|
+
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
|
|
28
|
+
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`
|
|
29
|
+
const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`
|
|
30
|
+
const EVENT_DRAG_START = `dragstart${EVENT_KEY}`
|
|
31
|
+
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
|
|
32
|
+
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
|
|
33
|
+
|
|
34
|
+
const CLASS_NAME_CAROUSEL = 'c-carousel'
|
|
35
|
+
const CLASS_NAME_ACTIVE = 'is-active'
|
|
36
|
+
const CLASS_NAME_SLIDE = 'c-slide'
|
|
37
|
+
const CLASS_NAME_END = 'c-carousel-item-end'
|
|
38
|
+
const CLASS_NAME_START = 'c-carousel-item-start'
|
|
39
|
+
const CLASS_NAME_NEXT = 'c-carousel-item-next'
|
|
40
|
+
const CLASS_NAME_PREV = 'c-carousel-item-prev'
|
|
41
|
+
|
|
42
|
+
const SELECTOR_ACTIVE = '.is-active'
|
|
43
|
+
const SELECTOR_ITEM = '.c-carousel__item'
|
|
44
|
+
const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM
|
|
45
|
+
const SELECTOR_ITEM_IMG = '.c-carousel__item img'
|
|
46
|
+
const SELECTOR_INDICATORS = '.c-carousel-indicators'
|
|
47
|
+
const SELECTOR_DATA_SLIDE = '[data-c-slide], [data-c-slide-to]'
|
|
48
|
+
const SELECTOR_DATA_RIDE = '[data-c-ride="carousel"]'
|
|
49
|
+
|
|
50
|
+
const KEY_TO_DIRECTION = {
|
|
51
|
+
[ARROW_LEFT_KEY]: DIRECTION_RIGHT,
|
|
52
|
+
[ARROW_RIGHT_KEY]: DIRECTION_LEFT
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const Default = {
|
|
56
|
+
interval: 5000,
|
|
57
|
+
keyboard: true,
|
|
58
|
+
pause: 'hover',
|
|
59
|
+
ride: false,
|
|
60
|
+
touch: true,
|
|
61
|
+
wrap: true
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class Carousel extends BaseComponent {
|
|
65
|
+
constructor(element, config) {
|
|
66
|
+
super(element, config)
|
|
67
|
+
|
|
68
|
+
this._interval = null
|
|
69
|
+
this._activeElement = null
|
|
70
|
+
this._isSliding = false
|
|
71
|
+
this.touchTimeout = null
|
|
72
|
+
this._swipeHelper = null
|
|
73
|
+
|
|
74
|
+
// Read data attributes and merge into config
|
|
75
|
+
const rideAttr = this._element.getAttribute('data-c-ride')
|
|
76
|
+
if (rideAttr && !this._config.ride) {
|
|
77
|
+
this._config.ride = rideAttr === 'carousel' ? CLASS_NAME_CAROUSEL : rideAttr
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Read interval from data attribute
|
|
81
|
+
const intervalAttr = this._element.getAttribute('data-c-interval')
|
|
82
|
+
if (intervalAttr) {
|
|
83
|
+
this._config.interval = parseInt(intervalAttr, 10)
|
|
84
|
+
this._config.defaultInterval = this._config.interval
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
this._indicatorsElement = getElement(SELECTOR_INDICATORS, this._element)
|
|
88
|
+
this._addEventListeners()
|
|
89
|
+
|
|
90
|
+
if (this._config.ride === CLASS_NAME_CAROUSEL) {
|
|
91
|
+
this.cycle()
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static get NAME() {
|
|
96
|
+
return NAME
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
static get Default() {
|
|
100
|
+
return Default
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
_configAfterMerge(config) {
|
|
104
|
+
config.defaultInterval = config.interval
|
|
105
|
+
return config
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Public
|
|
109
|
+
next() {
|
|
110
|
+
this._slide(ORDER_NEXT)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
nextWhenVisible() {
|
|
114
|
+
if (!document.hidden && isVisible(this._element)) {
|
|
115
|
+
this.next()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
prev() {
|
|
120
|
+
this._slide(ORDER_PREV)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pause() {
|
|
124
|
+
if (this._isSliding) {
|
|
125
|
+
triggerTransitionEnd(this._element)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
this._clearInterval()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
cycle() {
|
|
132
|
+
this._clearInterval()
|
|
133
|
+
this._updateInterval()
|
|
134
|
+
|
|
135
|
+
this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
_maybeEnableCycle() {
|
|
139
|
+
if (!this._config.ride) {
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (this._isSliding) {
|
|
144
|
+
one(this._element, EVENT_SLID, () => this.cycle())
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
this.cycle()
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
to(index) {
|
|
152
|
+
const items = this._getItems()
|
|
153
|
+
if (index > items.length - 1 || index < 0) {
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (this._isSliding) {
|
|
158
|
+
one(this._element, EVENT_SLID, () => this.to(index))
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const activeIndex = this._getItemIndex(this._getActive())
|
|
163
|
+
if (activeIndex === index) {
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV
|
|
168
|
+
|
|
169
|
+
this._slide(order, items[index])
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
dispose() {
|
|
173
|
+
if (this._swipeHelper) {
|
|
174
|
+
this._swipeHelper.dispose()
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
super.dispose()
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Private
|
|
181
|
+
_addEventListeners() {
|
|
182
|
+
if (this._config.keyboard) {
|
|
183
|
+
on(this._element, EVENT_KEYDOWN, (event) => this._keydown(event))
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (this._config.pause === 'hover') {
|
|
187
|
+
on(this._element, EVENT_MOUSEENTER, () => this.pause())
|
|
188
|
+
on(this._element, EVENT_MOUSELEAVE, () => this._maybeEnableCycle())
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (this._config.touch && Swipe.isSupported()) {
|
|
192
|
+
this._addTouchEventListeners()
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
_addTouchEventListeners() {
|
|
197
|
+
for (const img of getElements(SELECTOR_ITEM_IMG, this._element)) {
|
|
198
|
+
on(img, EVENT_DRAG_START, (event) => event.preventDefault())
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const endCallBack = () => {
|
|
202
|
+
if (this._config.pause !== 'hover') {
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.pause()
|
|
207
|
+
if (this.touchTimeout) {
|
|
208
|
+
clearTimeout(this.touchTimeout)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const swipeConfig = {
|
|
215
|
+
leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),
|
|
216
|
+
rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),
|
|
217
|
+
endCallback: endCallBack
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this._swipeHelper = new Swipe(this._element, swipeConfig)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
_keydown(event) {
|
|
224
|
+
if (/input|textarea/i.test(event.target.tagName)) {
|
|
225
|
+
return
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const direction = KEY_TO_DIRECTION[event.key]
|
|
229
|
+
if (direction) {
|
|
230
|
+
event.preventDefault()
|
|
231
|
+
this._slide(this._directionToOrder(direction))
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
_getItemIndex(element) {
|
|
236
|
+
return this._getItems().indexOf(element)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
_setActiveIndicatorElement(index) {
|
|
240
|
+
if (!this._indicatorsElement) {
|
|
241
|
+
return
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const activeIndicator = getElement(SELECTOR_ACTIVE, this._indicatorsElement)
|
|
245
|
+
|
|
246
|
+
if (activeIndicator) {
|
|
247
|
+
removeClass(activeIndicator, CLASS_NAME_ACTIVE)
|
|
248
|
+
activeIndicator.removeAttribute('aria-current')
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const newActiveIndicator = getElement(`[data-c-slide-to="${index}"]`, this._indicatorsElement)
|
|
252
|
+
|
|
253
|
+
if (newActiveIndicator) {
|
|
254
|
+
addClass(newActiveIndicator, CLASS_NAME_ACTIVE)
|
|
255
|
+
newActiveIndicator.setAttribute('aria-current', 'true')
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
_updateInterval() {
|
|
260
|
+
const element = this._activeElement || this._getActive()
|
|
261
|
+
|
|
262
|
+
if (!element) {
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const elementInterval = Number.parseInt(element.getAttribute('data-c-interval'), 10)
|
|
267
|
+
|
|
268
|
+
this._config.interval = elementInterval || this._config.defaultInterval
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
_slide(order, element = null) {
|
|
272
|
+
if (this._isSliding) {
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const activeElement = this._getActive()
|
|
277
|
+
const isNext = order === ORDER_NEXT
|
|
278
|
+
const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap)
|
|
279
|
+
|
|
280
|
+
if (nextElement === activeElement) {
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const nextElementIndex = this._getItemIndex(nextElement)
|
|
285
|
+
|
|
286
|
+
const triggerEvent = (eventName) => {
|
|
287
|
+
const event = new CustomEvent(eventName, {
|
|
288
|
+
bubbles: true,
|
|
289
|
+
cancelable: true,
|
|
290
|
+
detail: {
|
|
291
|
+
relatedTarget: nextElement,
|
|
292
|
+
direction: this._orderToDirection(order),
|
|
293
|
+
from: this._getItemIndex(activeElement),
|
|
294
|
+
to: nextElementIndex
|
|
295
|
+
}
|
|
296
|
+
})
|
|
297
|
+
this._element.dispatchEvent(event)
|
|
298
|
+
return event
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const slideEvent = triggerEvent(EVENT_SLIDE)
|
|
302
|
+
|
|
303
|
+
if (slideEvent.defaultPrevented) {
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (!activeElement || !nextElement) {
|
|
308
|
+
return
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const isCycling = Boolean(this._interval)
|
|
312
|
+
this.pause()
|
|
313
|
+
|
|
314
|
+
this._isSliding = true
|
|
315
|
+
|
|
316
|
+
this._setActiveIndicatorElement(nextElementIndex)
|
|
317
|
+
this._activeElement = nextElement
|
|
318
|
+
|
|
319
|
+
const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END
|
|
320
|
+
const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV
|
|
321
|
+
|
|
322
|
+
addClass(nextElement, orderClassName)
|
|
323
|
+
|
|
324
|
+
reflow(nextElement)
|
|
325
|
+
|
|
326
|
+
addClass(activeElement, directionalClassName)
|
|
327
|
+
addClass(nextElement, directionalClassName)
|
|
328
|
+
|
|
329
|
+
const completeCallBack = () => {
|
|
330
|
+
removeClass(nextElement, directionalClassName)
|
|
331
|
+
removeClass(nextElement, orderClassName)
|
|
332
|
+
addClass(nextElement, CLASS_NAME_ACTIVE)
|
|
333
|
+
|
|
334
|
+
removeClass(activeElement, CLASS_NAME_ACTIVE)
|
|
335
|
+
removeClass(activeElement, orderClassName)
|
|
336
|
+
removeClass(activeElement, directionalClassName)
|
|
337
|
+
|
|
338
|
+
this._isSliding = false
|
|
339
|
+
|
|
340
|
+
triggerEvent(EVENT_SLID)
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
this._queueCallback(completeCallBack, activeElement, this._isAnimated())
|
|
344
|
+
|
|
345
|
+
if (isCycling) {
|
|
346
|
+
this.cycle()
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
_isAnimated() {
|
|
351
|
+
return hasClass(this._element, CLASS_NAME_SLIDE)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
_getActive() {
|
|
355
|
+
return getElement(SELECTOR_ACTIVE_ITEM, this._element)
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
_getItems() {
|
|
359
|
+
return getElements(SELECTOR_ITEM, this._element)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
_clearInterval() {
|
|
363
|
+
if (this._interval) {
|
|
364
|
+
clearInterval(this._interval)
|
|
365
|
+
this._interval = null
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
_directionToOrder(direction) {
|
|
370
|
+
// RTL support would go here, but for now assume LTR
|
|
371
|
+
return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
_orderToDirection(order) {
|
|
375
|
+
// RTL support would go here, but for now assume LTR
|
|
376
|
+
return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
static getOrCreateInstance(element, config = {}) {
|
|
380
|
+
return element._baseLineComponent || new Carousel(element, config)
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Data API implementation
|
|
385
|
+
on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {
|
|
386
|
+
const target = getElementFromSelector(this)
|
|
387
|
+
|
|
388
|
+
if (!target || !hasClass(target, CLASS_NAME_CAROUSEL)) {
|
|
389
|
+
return
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
event.preventDefault()
|
|
393
|
+
|
|
394
|
+
const carousel = Carousel.getOrCreateInstance(target)
|
|
395
|
+
const slideIndex = this.getAttribute('data-c-slide-to')
|
|
396
|
+
|
|
397
|
+
if (slideIndex) {
|
|
398
|
+
carousel.to(parseInt(slideIndex, 10))
|
|
399
|
+
carousel._maybeEnableCycle()
|
|
400
|
+
return
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (this.getAttribute('data-c-slide') === 'next') {
|
|
404
|
+
carousel.next()
|
|
405
|
+
carousel._maybeEnableCycle()
|
|
406
|
+
return
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
carousel.prev()
|
|
410
|
+
carousel._maybeEnableCycle()
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
// Initialize carousels with data-c-ride on page load
|
|
414
|
+
const initCarousels = () => {
|
|
415
|
+
const carousels = getElements(SELECTOR_DATA_RIDE)
|
|
416
|
+
|
|
417
|
+
for (const carousel of carousels) {
|
|
418
|
+
Carousel.getOrCreateInstance(carousel)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
on(window, EVENT_LOAD_DATA_API, initCarousels)
|
|
423
|
+
|
|
424
|
+
// Also initialize on DOMContentLoaded if page is already loaded
|
|
425
|
+
onDOMContentLoaded(initCarousels)
|
|
426
|
+
|
|
427
|
+
export default Carousel
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base-Line Collapse Component
|
|
3
|
+
* Baseline 2.0 compatible collapse with Base-Line naming
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseComponent } from './base.js'
|
|
7
|
+
import { getElement, getElements, on, off, executeAfterTransition, reflow } from './util.js'
|
|
8
|
+
import { addClass, removeClass, hasClass } from './component.js'
|
|
9
|
+
|
|
10
|
+
const NAME = 'collapse'
|
|
11
|
+
const DATA_KEY = `c.${NAME}`
|
|
12
|
+
const EVENT_KEY = `.${DATA_KEY}`
|
|
13
|
+
const DATA_API_KEY = `[data-c-${NAME}]`
|
|
14
|
+
|
|
15
|
+
const EVENT_SHOW = `show${EVENT_KEY}`
|
|
16
|
+
const EVENT_SHOWN = `shown${EVENT_KEY}`
|
|
17
|
+
const EVENT_HIDE = `hide${EVENT_KEY}`
|
|
18
|
+
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
|
|
19
|
+
const EVENT_CLICK = `click${EVENT_KEY}`
|
|
20
|
+
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
|
|
21
|
+
|
|
22
|
+
const CLASS_NAME_COLLAPSE = 'c-collapse'
|
|
23
|
+
const CLASS_NAME_COLLAPSING = 'c-collapsing'
|
|
24
|
+
const CLASS_NAME_COLLAPSED = 'c-collapsed'
|
|
25
|
+
const CLASS_NAME_SHOW = 'is-show'
|
|
26
|
+
|
|
27
|
+
const WIDTH = 'width'
|
|
28
|
+
const HEIGHT = 'height'
|
|
29
|
+
|
|
30
|
+
const SELECTOR_ACTIVES = '.c-collapse.is-show'
|
|
31
|
+
const SELECTOR_DATA_TOGGLE = '[data-c-toggle="collapse"]'
|
|
32
|
+
|
|
33
|
+
class Collapse extends BaseComponent {
|
|
34
|
+
constructor(element, config) {
|
|
35
|
+
super(element, config)
|
|
36
|
+
this._isTransitioning = false
|
|
37
|
+
this._triggerArray = []
|
|
38
|
+
this._init()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
static get NAME() {
|
|
42
|
+
return NAME
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static get Default() {
|
|
46
|
+
return {
|
|
47
|
+
toggle: true,
|
|
48
|
+
parent: null
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
_init() {
|
|
53
|
+
this._isShown = hasClass(this._element, CLASS_NAME_SHOW)
|
|
54
|
+
|
|
55
|
+
// Get parent from config, data attribute on element, or data attribute on trigger
|
|
56
|
+
if (!this._config.parent) {
|
|
57
|
+
const parentSelector = this._element.getAttribute('data-c-parent')
|
|
58
|
+
if (parentSelector) {
|
|
59
|
+
this._config.parent = getElement(parentSelector)
|
|
60
|
+
} else {
|
|
61
|
+
// Try to find parent from trigger buttons
|
|
62
|
+
const triggers = getElements(SELECTOR_DATA_TOGGLE)
|
|
63
|
+
for (const trigger of triggers) {
|
|
64
|
+
const selector = trigger.getAttribute('data-c-target') || trigger.getAttribute('href')
|
|
65
|
+
const target = getElement(selector)
|
|
66
|
+
if (target === this._element) {
|
|
67
|
+
const parentSelectorFromTrigger = trigger.getAttribute('data-c-parent')
|
|
68
|
+
if (parentSelectorFromTrigger) {
|
|
69
|
+
this._config.parent = getElement(parentSelectorFromTrigger)
|
|
70
|
+
break
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} else if (typeof this._config.parent === 'string') {
|
|
76
|
+
this._config.parent = getElement(this._config.parent)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (this._config.toggle) {
|
|
80
|
+
this.toggle()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this._bindEvents()
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
_bindEvents() {
|
|
87
|
+
const triggers = getElements(SELECTOR_DATA_TOGGLE)
|
|
88
|
+
triggers.forEach(trigger => {
|
|
89
|
+
const selector = trigger.getAttribute('data-c-target') || trigger.getAttribute('href')
|
|
90
|
+
const target = getElement(selector)
|
|
91
|
+
|
|
92
|
+
if (target === this._element) {
|
|
93
|
+
this._triggerArray.push(trigger)
|
|
94
|
+
on(trigger, 'click', (e) => {
|
|
95
|
+
e.preventDefault()
|
|
96
|
+
this.toggle()
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
_getParent() {
|
|
103
|
+
return this._config.parent || null
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
_getSiblings() {
|
|
107
|
+
const parent = this._getParent()
|
|
108
|
+
if (!parent) {
|
|
109
|
+
return []
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return getElements(SELECTOR_ACTIVES, parent).filter(
|
|
113
|
+
element => element !== this._element && hasClass(element, CLASS_NAME_SHOW)
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
show() {
|
|
118
|
+
if (this._isTransitioning || hasClass(this._element, CLASS_NAME_SHOW)) {
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const showEvent = new CustomEvent(EVENT_SHOW, {
|
|
123
|
+
bubbles: true,
|
|
124
|
+
cancelable: true
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
this._element.dispatchEvent(showEvent)
|
|
128
|
+
|
|
129
|
+
if (showEvent.defaultPrevented) {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Close siblings if parent is set
|
|
134
|
+
const siblings = this._getSiblings()
|
|
135
|
+
siblings.forEach(sibling => {
|
|
136
|
+
const siblingInstance = Collapse.getOrCreateInstance(sibling, { toggle: false })
|
|
137
|
+
siblingInstance.hide()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
this._isTransitioning = true
|
|
141
|
+
this._element.style.height = '0'
|
|
142
|
+
addClass(this._element, CLASS_NAME_COLLAPSING)
|
|
143
|
+
removeClass(this._element, CLASS_NAME_COLLAPSE)
|
|
144
|
+
reflow(this._element)
|
|
145
|
+
|
|
146
|
+
const height = this._element.scrollHeight
|
|
147
|
+
this._element.style.height = `${height}px`
|
|
148
|
+
|
|
149
|
+
executeAfterTransition(() => {
|
|
150
|
+
removeClass(this._element, CLASS_NAME_COLLAPSING)
|
|
151
|
+
addClass(this._element, CLASS_NAME_COLLAPSE)
|
|
152
|
+
addClass(this._element, CLASS_NAME_SHOW)
|
|
153
|
+
this._element.style.height = ''
|
|
154
|
+
this._isTransitioning = false
|
|
155
|
+
|
|
156
|
+
this._triggerArray.forEach(trigger => {
|
|
157
|
+
removeClass(trigger, CLASS_NAME_COLLAPSED)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
const shownEvent = new CustomEvent(EVENT_SHOWN, { bubbles: true })
|
|
161
|
+
this._element.dispatchEvent(shownEvent)
|
|
162
|
+
}, this._element)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
hide() {
|
|
166
|
+
if (this._isTransitioning || !hasClass(this._element, CLASS_NAME_SHOW)) {
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const hideEvent = new CustomEvent(EVENT_HIDE, {
|
|
171
|
+
bubbles: true,
|
|
172
|
+
cancelable: true
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
this._element.dispatchEvent(hideEvent)
|
|
176
|
+
|
|
177
|
+
if (hideEvent.defaultPrevented) {
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this._isTransitioning = true
|
|
182
|
+
const height = this._element.getBoundingClientRect().height
|
|
183
|
+
this._element.style.height = `${height}px`
|
|
184
|
+
reflow(this._element)
|
|
185
|
+
|
|
186
|
+
removeClass(this._element, CLASS_NAME_COLLAPSE)
|
|
187
|
+
addClass(this._element, CLASS_NAME_COLLAPSING)
|
|
188
|
+
this._element.style.height = ''
|
|
189
|
+
|
|
190
|
+
executeAfterTransition(() => {
|
|
191
|
+
removeClass(this._element, CLASS_NAME_COLLAPSING)
|
|
192
|
+
addClass(this._element, CLASS_NAME_COLLAPSE)
|
|
193
|
+
removeClass(this._element, CLASS_NAME_SHOW)
|
|
194
|
+
this._isTransitioning = false
|
|
195
|
+
|
|
196
|
+
this._triggerArray.forEach(trigger => {
|
|
197
|
+
addClass(trigger, CLASS_NAME_COLLAPSED)
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
const hiddenEvent = new CustomEvent(EVENT_HIDDEN, { bubbles: true })
|
|
201
|
+
this._element.dispatchEvent(hiddenEvent)
|
|
202
|
+
}, this._element)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
toggle() {
|
|
206
|
+
if (hasClass(this._element, CLASS_NAME_SHOW)) {
|
|
207
|
+
this.hide()
|
|
208
|
+
} else {
|
|
209
|
+
this.show()
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
static getOrCreateInstance(element, config = {}) {
|
|
214
|
+
return element._baseLineComponent || new Collapse(element, config)
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Data API
|
|
219
|
+
on(document, 'click', SELECTOR_DATA_TOGGLE, function (event) {
|
|
220
|
+
event.preventDefault()
|
|
221
|
+
const selector = this.getAttribute('data-c-target') || this.getAttribute('href')
|
|
222
|
+
const target = getElement(selector)
|
|
223
|
+
if (target) {
|
|
224
|
+
// Get parent from trigger button if available
|
|
225
|
+
const parentSelector = this.getAttribute('data-c-parent')
|
|
226
|
+
const config = parentSelector ? { parent: parentSelector } : {}
|
|
227
|
+
const collapse = Collapse.getOrCreateInstance(target, config)
|
|
228
|
+
collapse.toggle()
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
export default Collapse
|
|
233
|
+
|