Skip to content

Commit fe10f28

Browse files
Merge branch 'main' into patrickhlauke-navs-tabs
2 parents 6346c0e + e79c8f3 commit fe10f28

38 files changed

+831
-550
lines changed

.eslintrc.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,23 @@
3636
"error",
3737
"always"
3838
],
39-
"prefer-named-capture-group": "off",
4039
"semi": [
4140
"error",
4241
"never"
4342
],
4443
"unicorn/consistent-function-scoping": "off",
4544
"unicorn/explicit-length-check": "off",
4645
"unicorn/no-array-callback-reference": "off",
46+
"unicorn/no-array-for-each": "off",
4747
"unicorn/no-for-loop": "off",
4848
"unicorn/no-null": "off",
4949
"unicorn/no-unused-properties": "error",
5050
"unicorn/no-useless-undefined": "off",
5151
"unicorn/prefer-dom-node-append": "off",
5252
"unicorn/prefer-dom-node-dataset": "off",
5353
"unicorn/prefer-dom-node-remove": "off",
54-
"unicorn/prefer-optional-catch-binding": "off",
5554
"unicorn/prefer-query-selector": "off",
55+
"unicorn/prefer-spread": "off",
5656
"unicorn/prevent-abbreviations": "off"
5757
}
5858
}

.github/workflows/js.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
run: npm run js-test
4848

4949
- name: Run Coveralls
50-
uses: coverallsapp/github-action@master
50+
uses: coverallsapp/github-action@v1.1.2
5151
if: matrix.node == 14
5252
with:
5353
github-token: "${{ secrets.GITHUB_TOKEN }}"

build/change-version.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ function main(args) {
9292
'.git',
9393
'_gh_pages',
9494
'node_modules',
95-
'vendor'
95+
'resources'
9696
])
9797
const INCLUDED_EXTENSIONS = new Set([
9898
// This extension allowlist is how we avoid modifying binary files

js/src/carousel.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ const SELECTOR_ITEM = '.carousel-item'
9090
const SELECTOR_ITEM_IMG = '.carousel-item img'
9191
const SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'
9292
const SELECTOR_INDICATORS = '.carousel-indicators'
93+
const SELECTOR_INDICATOR = '[data-bs-target]'
9394
const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'
9495
const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]'
9596

@@ -405,18 +406,19 @@ class Carousel extends BaseComponent {
405406

406407
_setActiveIndicatorElement(element) {
407408
if (this._indicatorsElement) {
408-
const indicators = SelectorEngine.find(SELECTOR_ACTIVE, this._indicatorsElement)
409+
const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)
409410

410-
for (let i = 0; i < indicators.length; i++) {
411-
indicators[i].classList.remove(CLASS_NAME_ACTIVE)
412-
}
411+
activeIndicator.classList.remove(CLASS_NAME_ACTIVE)
412+
activeIndicator.removeAttribute('aria-current')
413413

414-
const nextIndicator = this._indicatorsElement.children[
415-
this._getItemIndex(element)
416-
]
414+
const indicators = SelectorEngine.find(SELECTOR_INDICATOR, this._indicatorsElement)
417415

418-
if (nextIndicator) {
419-
nextIndicator.classList.add(CLASS_NAME_ACTIVE)
416+
for (let i = 0; i < indicators.length; i++) {
417+
if (Number.parseInt(indicators[i].getAttribute('data-bs-slide-to'), 10) === this._getItemIndex(element)) {
418+
indicators[i].classList.add(CLASS_NAME_ACTIVE)
419+
indicators[i].setAttribute('aria-current', 'true')
420+
break
421+
}
420422
}
421423
}
422424
}

js/src/dropdown.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ const PLACEMENT_RIGHT = isRTL ? 'left-start' : 'right-start'
7272
const PLACEMENT_LEFT = isRTL ? 'right-start' : 'left-start'
7373

7474
const Default = {
75-
offset: 0,
75+
offset: [0, 0],
7676
flip: true,
7777
boundary: 'clippingParents',
7878
reference: 'toggle',
@@ -81,7 +81,7 @@ const Default = {
8181
}
8282

8383
const DefaultType = {
84-
offset: '(number|string|function)',
84+
offset: '(array|string|function)',
8585
flip: 'boolean',
8686
boundary: '(string|element)',
8787
reference: '(string|element|object)',
@@ -298,6 +298,20 @@ class Dropdown extends BaseComponent {
298298
return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null
299299
}
300300

301+
_getOffset() {
302+
const { offset } = this._config
303+
304+
if (typeof offset === 'string') {
305+
return offset.split(',').map(val => Number.parseInt(val, 10))
306+
}
307+
308+
if (typeof offset === 'function') {
309+
return popperData => offset(popperData, this._element)
310+
}
311+
312+
return offset
313+
}
314+
301315
_getPopperConfig() {
302316
const popperConfig = {
303317
placement: this._getPlacement(),
@@ -313,6 +327,12 @@ class Dropdown extends BaseComponent {
313327
options: {
314328
fallbackPlacements: ['top', 'right', 'bottom', 'left']
315329
}
330+
},
331+
{
332+
name: 'offset',
333+
options: {
334+
offset: this._getOffset()
335+
}
316336
}]
317337
}
318338

js/src/tooltip.js

Lines changed: 96 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const DefaultType = {
5050
html: 'boolean',
5151
selector: '(string|boolean)',
5252
placement: '(string|function)',
53+
offset: '(array|string|function)',
5354
container: '(string|element|boolean)',
5455
fallbackPlacements: 'array',
5556
boundary: '(string|element)',
@@ -80,6 +81,7 @@ const Default = {
8081
html: false,
8182
selector: false,
8283
placement: 'top',
84+
offset: [0, 0],
8385
container: false,
8486
fallbackPlacements: ['top', 'right', 'bottom', 'left'],
8587
boundary: 'clippingParents',
@@ -191,13 +193,7 @@ class Tooltip extends BaseComponent {
191193
}
192194

193195
if (event) {
194-
const dataKey = this.constructor.DATA_KEY
195-
let context = Data.getData(event.delegateTarget, dataKey)
196-
197-
if (!context) {
198-
context = new this.constructor(event.delegateTarget, this._getDelegateConfig())
199-
Data.setData(event.delegateTarget, dataKey, context)
200-
}
196+
const context = this._initializeOnDelegatedTarget(event)
201197

202198
context._activeTrigger.click = !context._activeTrigger.click
203199

@@ -245,83 +241,85 @@ class Tooltip extends BaseComponent {
245241
throw new Error('Please use show on visible elements')
246242
}
247243

248-
if (this.isWithContent() && this._isEnabled) {
249-
const showEvent = EventHandler.trigger(this._element, this.constructor.Event.SHOW)
250-
const shadowRoot = findShadowRoot(this._element)
251-
const isInTheDom = shadowRoot === null ?
252-
this._element.ownerDocument.documentElement.contains(this._element) :
253-
shadowRoot.contains(this._element)
244+
if (!(this.isWithContent() && this._isEnabled)) {
245+
return
246+
}
254247

255-
if (showEvent.defaultPrevented || !isInTheDom) {
256-
return
257-
}
248+
const showEvent = EventHandler.trigger(this._element, this.constructor.Event.SHOW)
249+
const shadowRoot = findShadowRoot(this._element)
250+
const isInTheDom = shadowRoot === null ?
251+
this._element.ownerDocument.documentElement.contains(this._element) :
252+
shadowRoot.contains(this._element)
258253

259-
const tip = this.getTipElement()
260-
const tipId = getUID(this.constructor.NAME)
254+
if (showEvent.defaultPrevented || !isInTheDom) {
255+
return
256+
}
261257

262-
tip.setAttribute('id', tipId)
263-
this._element.setAttribute('aria-describedby', tipId)
258+
const tip = this.getTipElement()
259+
const tipId = getUID(this.constructor.NAME)
264260

265-
this.setContent()
261+
tip.setAttribute('id', tipId)
262+
this._element.setAttribute('aria-describedby', tipId)
266263

267-
if (this.config.animation) {
268-
tip.classList.add(CLASS_NAME_FADE)
269-
}
264+
this.setContent()
270265

271-
const placement = typeof this.config.placement === 'function' ?
272-
this.config.placement.call(this, tip, this._element) :
273-
this.config.placement
266+
if (this.config.animation) {
267+
tip.classList.add(CLASS_NAME_FADE)
268+
}
274269

275-
const attachment = this._getAttachment(placement)
276-
this._addAttachmentClass(attachment)
270+
const placement = typeof this.config.placement === 'function' ?
271+
this.config.placement.call(this, tip, this._element) :
272+
this.config.placement
277273

278-
const container = this._getContainer()
279-
Data.setData(tip, this.constructor.DATA_KEY, this)
274+
const attachment = this._getAttachment(placement)
275+
this._addAttachmentClass(attachment)
280276

281-
if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
282-
container.appendChild(tip)
283-
}
277+
const container = this._getContainer()
278+
Data.setData(tip, this.constructor.DATA_KEY, this)
284279

285-
EventHandler.trigger(this._element, this.constructor.Event.INSERTED)
280+
if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
281+
container.appendChild(tip)
282+
}
286283

287-
this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
284+
EventHandler.trigger(this._element, this.constructor.Event.INSERTED)
288285

289-
tip.classList.add(CLASS_NAME_SHOW)
286+
this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
290287

291-
const customClass = typeof this.config.customClass === 'function' ? this.config.customClass() : this.config.customClass
292-
if (customClass) {
293-
tip.classList.add(...customClass.split(' '))
294-
}
288+
tip.classList.add(CLASS_NAME_SHOW)
295289

296-
// If this is a touch-enabled device we add extra
297-
// empty mouseover listeners to the body's immediate children;
298-
// only needed because of broken event delegation on iOS
299-
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
300-
if ('ontouchstart' in document.documentElement) {
301-
[].concat(...document.body.children).forEach(element => {
302-
EventHandler.on(element, 'mouseover', noop())
303-
})
304-
}
290+
const customClass = typeof this.config.customClass === 'function' ? this.config.customClass() : this.config.customClass
291+
if (customClass) {
292+
tip.classList.add(...customClass.split(' '))
293+
}
305294

306-
const complete = () => {
307-
const prevHoverState = this._hoverState
295+
// If this is a touch-enabled device we add extra
296+
// empty mouseover listeners to the body's immediate children;
297+
// only needed because of broken event delegation on iOS
298+
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
299+
if ('ontouchstart' in document.documentElement) {
300+
[].concat(...document.body.children).forEach(element => {
301+
EventHandler.on(element, 'mouseover', noop())
302+
})
303+
}
308304

309-
this._hoverState = null
310-
EventHandler.trigger(this._element, this.constructor.Event.SHOWN)
305+
const complete = () => {
306+
const prevHoverState = this._hoverState
311307

312-
if (prevHoverState === HOVER_STATE_OUT) {
313-
this._leave(null, this)
314-
}
315-
}
308+
this._hoverState = null
309+
EventHandler.trigger(this._element, this.constructor.Event.SHOWN)
316310

317-
if (this.tip.classList.contains(CLASS_NAME_FADE)) {
318-
const transitionDuration = getTransitionDurationFromElement(this.tip)
319-
EventHandler.one(this.tip, 'transitionend', complete)
320-
emulateTransitionEnd(this.tip, transitionDuration)
321-
} else {
322-
complete()
311+
if (prevHoverState === HOVER_STATE_OUT) {
312+
this._leave(null, this)
323313
}
324314
}
315+
316+
if (this.tip.classList.contains(CLASS_NAME_FADE)) {
317+
const transitionDuration = getTransitionDurationFromElement(this.tip)
318+
EventHandler.one(this.tip, 'transitionend', complete)
319+
emulateTransitionEnd(this.tip, transitionDuration)
320+
} else {
321+
complete()
322+
}
325323
}
326324

327325
hide() {
@@ -465,6 +463,32 @@ class Tooltip extends BaseComponent {
465463

466464
// Private
467465

466+
_initializeOnDelegatedTarget(event, context) {
467+
const dataKey = this.constructor.DATA_KEY
468+
context = context || Data.getData(event.delegateTarget, dataKey)
469+
470+
if (!context) {
471+
context = new this.constructor(event.delegateTarget, this._getDelegateConfig())
472+
Data.setData(event.delegateTarget, dataKey, context)
473+
}
474+
475+
return context
476+
}
477+
478+
_getOffset() {
479+
const { offset } = this.config
480+
481+
if (typeof offset === 'string') {
482+
return offset.split(',').map(val => Number.parseInt(val, 10))
483+
}
484+
485+
if (typeof offset === 'function') {
486+
return popperData => offset(popperData, this._element)
487+
}
488+
489+
return offset
490+
}
491+
468492
_getPopperConfig(attachment) {
469493
const defaultBsConfig = {
470494
placement: attachment,
@@ -476,6 +500,12 @@ class Tooltip extends BaseComponent {
476500
fallbackPlacements: this.config.fallbackPlacements
477501
}
478502
},
503+
{
504+
name: 'offset',
505+
options: {
506+
offset: this._getOffset()
507+
}
508+
},
479509
{
480510
name: 'preventOverflow',
481511
options: {
@@ -582,16 +612,7 @@ class Tooltip extends BaseComponent {
582612
}
583613

584614
_enter(event, context) {
585-
const dataKey = this.constructor.DATA_KEY
586-
context = context || Data.getData(event.delegateTarget, dataKey)
587-
588-
if (!context) {
589-
context = new this.constructor(
590-
event.delegateTarget,
591-
this._getDelegateConfig()
592-
)
593-
Data.setData(event.delegateTarget, dataKey, context)
594-
}
615+
context = this._initializeOnDelegatedTarget(event, context)
595616

596617
if (event) {
597618
context._activeTrigger[
@@ -621,16 +642,7 @@ class Tooltip extends BaseComponent {
621642
}
622643

623644
_leave(event, context) {
624-
const dataKey = this.constructor.DATA_KEY
625-
context = context || Data.getData(event.delegateTarget, dataKey)
626-
627-
if (!context) {
628-
context = new this.constructor(
629-
event.delegateTarget,
630-
this._getDelegateConfig()
631-
)
632-
Data.setData(event.delegateTarget, dataKey, context)
633-
}
645+
context = this._initializeOnDelegatedTarget(event, context)
634646

635647
if (event) {
636648
context._activeTrigger[

0 commit comments

Comments
 (0)