@openeuropa/bcl-bootstrap 0.25.1 → 0.26.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/bootstrap-icons.svg +1 -1
- package/icons/apple.svg +2 -2
- package/icons/boombox-fill.svg +2 -2
- package/icons/cup-fill.svg +1 -1
- package/icons/cup.svg +1 -1
- package/js/dist/alert.js +3 -12
- package/js/dist/alert.js.map +1 -1
- package/js/dist/base-component.js +32 -18
- package/js/dist/base-component.js.map +1 -1
- package/js/dist/button.js +3 -12
- package/js/dist/button.js.map +1 -1
- package/js/dist/carousel.js +207 -307
- package/js/dist/carousel.js.map +1 -1
- package/js/dist/collapse.js +57 -88
- package/js/dist/collapse.js.map +1 -1
- package/js/dist/dom/data.js +1 -3
- package/js/dist/dom/data.js.map +1 -1
- package/js/dist/dom/event-handler.js +87 -106
- package/js/dist/dom/event-handler.js.map +1 -1
- package/js/dist/dom/manipulator.js +21 -25
- package/js/dist/dom/manipulator.js.map +1 -1
- package/js/dist/dom/selector-engine.js +11 -10
- package/js/dist/dom/selector-engine.js.map +1 -1
- package/js/dist/dropdown.js +83 -115
- package/js/dist/dropdown.js.map +1 -1
- package/js/dist/modal.js +95 -152
- package/js/dist/modal.js.map +1 -1
- package/js/dist/offcanvas.js +75 -58
- package/js/dist/offcanvas.js.map +1 -1
- package/js/dist/popover.js +29 -56
- package/js/dist/popover.js.map +1 -1
- package/js/dist/scrollspy.js +176 -125
- package/js/dist/scrollspy.js.map +1 -1
- package/js/dist/tab.js +207 -92
- package/js/dist/tab.js.map +1 -1
- package/js/dist/toast.js +23 -37
- package/js/dist/toast.js.map +1 -1
- package/js/dist/tooltip.js +259 -348
- package/js/dist/tooltip.js.map +1 -1
- package/js/dist/util/backdrop.js +62 -39
- package/js/dist/util/backdrop.js.map +1 -1
- package/js/dist/util/component-functions.js +1 -1
- package/js/dist/util/component-functions.js.map +1 -1
- package/js/dist/util/config.js +75 -0
- package/js/dist/util/config.js.map +1 -0
- package/js/dist/util/focustrap.js +41 -34
- package/js/dist/util/focustrap.js.map +1 -1
- package/js/dist/util/index.js +56 -52
- package/js/dist/util/index.js.map +1 -1
- package/js/dist/util/sanitizer.js +12 -19
- package/js/dist/util/sanitizer.js.map +1 -1
- package/js/dist/util/scrollbar.js +49 -34
- package/js/dist/util/scrollbar.js.map +1 -1
- package/js/dist/util/swipe.js +151 -0
- package/js/dist/util/swipe.js.map +1 -0
- package/js/dist/util/template-factory.js +173 -0
- package/js/dist/util/template-factory.js.map +1 -0
- package/js/src/alert.js +3 -15
- package/js/src/base-component.js +28 -18
- package/js/src/button.js +3 -17
- package/js/src/carousel.js +203 -319
- package/js/src/collapse.js +61 -94
- package/js/src/dom/data.js +1 -3
- package/js/src/dom/event-handler.js +80 -108
- package/js/src/dom/manipulator.js +22 -31
- package/js/src/dom/selector-engine.js +10 -19
- package/js/src/dropdown.js +84 -138
- package/js/src/modal.js +94 -158
- package/js/src/offcanvas.js +72 -61
- package/js/src/popover.js +31 -62
- package/js/src/scrollspy.js +166 -171
- package/js/src/tab.js +193 -110
- package/js/src/toast.js +19 -41
- package/js/src/tooltip.js +259 -371
- package/js/src/util/backdrop.js +55 -36
- package/js/src/util/component-functions.js +1 -1
- package/js/src/util/config.js +66 -0
- package/js/src/util/focustrap.js +38 -28
- package/js/src/util/index.js +67 -64
- package/js/src/util/sanitizer.js +11 -19
- package/js/src/util/scrollbar.js +47 -30
- package/js/src/util/swipe.js +146 -0
- package/js/src/util/template-factory.js +160 -0
- package/package.json +4 -4
- package/scss/_accordion.scss +52 -24
- package/scss/_alert.scss +18 -4
- package/scss/_badge.scss +14 -5
- package/scss/_breadcrumb.scss +22 -10
- package/scss/_button-group.scss +3 -0
- package/scss/_buttons.scss +97 -22
- package/scss/_card.scss +55 -37
- package/scss/_close.scss +1 -1
- package/scss/_containers.scss +1 -1
- package/scss/_dropdown.scss +83 -75
- package/scss/_functions.scss +7 -7
- package/scss/_grid.scss +3 -3
- package/scss/_helpers.scss +1 -0
- package/scss/_list-group.scss +44 -27
- package/scss/_maps.scss +54 -0
- package/scss/_modal.scss +71 -43
- package/scss/_nav.scss +53 -20
- package/scss/_navbar.scss +91 -150
- package/scss/_offcanvas.scss +119 -59
- package/scss/_pagination.scss +66 -21
- package/scss/_placeholders.scss +1 -1
- package/scss/_popover.scss +90 -52
- package/scss/_progress.scss +20 -9
- package/scss/_reboot.scss +25 -40
- package/scss/_root.scss +40 -21
- package/scss/_spinners.scss +38 -22
- package/scss/_tables.scss +32 -23
- package/scss/_toasts.scss +35 -16
- package/scss/_tooltip.scss +61 -56
- package/scss/_type.scss +2 -0
- package/scss/_utilities.scss +43 -26
- package/scss/_variables.scss +113 -121
- package/scss/bootstrap-grid.scss +3 -6
- package/scss/bootstrap-reboot.scss +3 -7
- package/scss/bootstrap-utilities.scss +3 -6
- package/scss/bootstrap.scss +4 -6
- package/scss/forms/_floating-labels.scss +14 -3
- package/scss/forms/_form-check.scss +28 -5
- package/scss/forms/_form-control.scss +12 -37
- package/scss/forms/_form-select.scss +0 -1
- package/scss/forms/_input-group.scss +15 -7
- package/scss/helpers/_color-bg.scss +10 -0
- package/scss/helpers/_colored-links.scss +2 -2
- package/scss/helpers/_position.scss +7 -1
- package/scss/helpers/_ratio.scss +2 -2
- package/scss/helpers/_vr.scss +1 -1
- package/scss/mixins/_alert.scss +7 -3
- package/scss/mixins/_banner.scss +9 -0
- package/scss/mixins/_breakpoints.scss +8 -8
- package/scss/mixins/_buttons.scss +32 -95
- package/scss/mixins/_container.scss +4 -2
- package/scss/mixins/_forms.scss +8 -0
- package/scss/mixins/_gradients.scss +1 -1
- package/scss/mixins/_grid.scss +12 -12
- package/scss/mixins/_pagination.scss +4 -25
- package/scss/mixins/_reset-text.scss +1 -1
- package/scss/mixins/_table-variants.scss +12 -9
- package/scss/mixins/_utilities.scss +12 -4
package/js/src/util/backdrop.js
CHANGED
|
@@ -1,41 +1,65 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* --------------------------------------------------------------------------
|
|
3
|
-
* Bootstrap (v5.
|
|
3
|
+
* Bootstrap (v5.2.0): util/backdrop.js
|
|
4
4
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
5
5
|
* --------------------------------------------------------------------------
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import EventHandler from '../dom/event-handler'
|
|
9
|
-
import { execute, executeAfterTransition, getElement, reflow
|
|
9
|
+
import { execute, executeAfterTransition, getElement, reflow } from './index'
|
|
10
|
+
import Config from './config'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Constants
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const NAME = 'backdrop'
|
|
17
|
+
const CLASS_NAME_FADE = 'fade'
|
|
18
|
+
const CLASS_NAME_SHOW = 'show'
|
|
19
|
+
const EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`
|
|
10
20
|
|
|
11
21
|
const Default = {
|
|
12
22
|
className: 'modal-backdrop',
|
|
13
|
-
|
|
23
|
+
clickCallback: null,
|
|
14
24
|
isAnimated: false,
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
isVisible: true, // if false, we use the backdrop helper without adding any element to the dom
|
|
26
|
+
rootElement: 'body' // give the choice to place backdrop under different elements
|
|
17
27
|
}
|
|
18
28
|
|
|
19
29
|
const DefaultType = {
|
|
20
30
|
className: 'string',
|
|
21
|
-
|
|
31
|
+
clickCallback: '(function|null)',
|
|
22
32
|
isAnimated: 'boolean',
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
isVisible: 'boolean',
|
|
34
|
+
rootElement: '(element|string)'
|
|
25
35
|
}
|
|
26
|
-
const NAME = 'backdrop'
|
|
27
|
-
const CLASS_NAME_FADE = 'fade'
|
|
28
|
-
const CLASS_NAME_SHOW = 'show'
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Class definition
|
|
39
|
+
*/
|
|
31
40
|
|
|
32
|
-
class Backdrop {
|
|
41
|
+
class Backdrop extends Config {
|
|
33
42
|
constructor(config) {
|
|
43
|
+
super()
|
|
34
44
|
this._config = this._getConfig(config)
|
|
35
45
|
this._isAppended = false
|
|
36
46
|
this._element = null
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
// Getters
|
|
50
|
+
static get Default() {
|
|
51
|
+
return Default
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static get DefaultType() {
|
|
55
|
+
return DefaultType
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static get NAME() {
|
|
59
|
+
return NAME
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Public
|
|
39
63
|
show(callback) {
|
|
40
64
|
if (!this._config.isVisible) {
|
|
41
65
|
execute(callback)
|
|
@@ -44,11 +68,12 @@ class Backdrop {
|
|
|
44
68
|
|
|
45
69
|
this._append()
|
|
46
70
|
|
|
71
|
+
const element = this._getElement()
|
|
47
72
|
if (this._config.isAnimated) {
|
|
48
|
-
reflow(
|
|
73
|
+
reflow(element)
|
|
49
74
|
}
|
|
50
75
|
|
|
51
|
-
|
|
76
|
+
element.classList.add(CLASS_NAME_SHOW)
|
|
52
77
|
|
|
53
78
|
this._emulateAnimation(() => {
|
|
54
79
|
execute(callback)
|
|
@@ -69,8 +94,18 @@ class Backdrop {
|
|
|
69
94
|
})
|
|
70
95
|
}
|
|
71
96
|
|
|
72
|
-
|
|
97
|
+
dispose() {
|
|
98
|
+
if (!this._isAppended) {
|
|
99
|
+
return
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
EventHandler.off(this._element, EVENT_MOUSEDOWN)
|
|
73
103
|
|
|
104
|
+
this._element.remove()
|
|
105
|
+
this._isAppended = false
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Private
|
|
74
109
|
_getElement() {
|
|
75
110
|
if (!this._element) {
|
|
76
111
|
const backdrop = document.createElement('div')
|
|
@@ -85,15 +120,9 @@ class Backdrop {
|
|
|
85
120
|
return this._element
|
|
86
121
|
}
|
|
87
122
|
|
|
88
|
-
|
|
89
|
-
config = {
|
|
90
|
-
...Default,
|
|
91
|
-
...(typeof config === 'object' ? config : {})
|
|
92
|
-
}
|
|
93
|
-
|
|
123
|
+
_configAfterMerge(config) {
|
|
94
124
|
// use getElement() with the default "body" to get a fresh Element on each instantiation
|
|
95
125
|
config.rootElement = getElement(config.rootElement)
|
|
96
|
-
typeCheckConfig(NAME, config, DefaultType)
|
|
97
126
|
return config
|
|
98
127
|
}
|
|
99
128
|
|
|
@@ -102,26 +131,16 @@ class Backdrop {
|
|
|
102
131
|
return
|
|
103
132
|
}
|
|
104
133
|
|
|
105
|
-
this.
|
|
134
|
+
const element = this._getElement()
|
|
135
|
+
this._config.rootElement.append(element)
|
|
106
136
|
|
|
107
|
-
EventHandler.on(
|
|
137
|
+
EventHandler.on(element, EVENT_MOUSEDOWN, () => {
|
|
108
138
|
execute(this._config.clickCallback)
|
|
109
139
|
})
|
|
110
140
|
|
|
111
141
|
this._isAppended = true
|
|
112
142
|
}
|
|
113
143
|
|
|
114
|
-
dispose() {
|
|
115
|
-
if (!this._isAppended) {
|
|
116
|
-
return
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
EventHandler.off(this._element, EVENT_MOUSEDOWN)
|
|
120
|
-
|
|
121
|
-
this._element.remove()
|
|
122
|
-
this._isAppended = false
|
|
123
|
-
}
|
|
124
|
-
|
|
125
144
|
_emulateAnimation(callback) {
|
|
126
145
|
executeAfterTransition(callback, this._getElement(), this._config.isAnimated)
|
|
127
146
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* --------------------------------------------------------------------------
|
|
3
|
-
* Bootstrap (v5.
|
|
3
|
+
* Bootstrap (v5.2.0): util/component-functions.js
|
|
4
4
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
5
5
|
* --------------------------------------------------------------------------
|
|
6
6
|
*/
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* --------------------------------------------------------------------------
|
|
3
|
+
* Bootstrap (v5.2.0): util/config.js
|
|
4
|
+
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
5
|
+
* --------------------------------------------------------------------------
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { isElement, toType } from './index'
|
|
9
|
+
import Manipulator from '../dom/manipulator'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Class definition
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
class Config {
|
|
16
|
+
// Getters
|
|
17
|
+
static get Default() {
|
|
18
|
+
return {}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static get DefaultType() {
|
|
22
|
+
return {}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static get NAME() {
|
|
26
|
+
throw new Error('You have to implement the static method "NAME", for each component!')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_getConfig(config) {
|
|
30
|
+
config = this._mergeConfigObj(config)
|
|
31
|
+
config = this._configAfterMerge(config)
|
|
32
|
+
this._typeCheckConfig(config)
|
|
33
|
+
return config
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
_configAfterMerge(config) {
|
|
37
|
+
return config
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_mergeConfigObj(config, element) {
|
|
41
|
+
const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {} // try to parse
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
...this.constructor.Default,
|
|
45
|
+
...(typeof jsonConfig === 'object' ? jsonConfig : {}),
|
|
46
|
+
...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),
|
|
47
|
+
...(typeof config === 'object' ? config : {})
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_typeCheckConfig(config, configTypes = this.constructor.DefaultType) {
|
|
52
|
+
for (const property of Object.keys(configTypes)) {
|
|
53
|
+
const expectedTypes = configTypes[property]
|
|
54
|
+
const value = config[property]
|
|
55
|
+
const valueType = isElement(value) ? 'element' : toType(value)
|
|
56
|
+
|
|
57
|
+
if (!new RegExp(expectedTypes).test(valueType)) {
|
|
58
|
+
throw new TypeError(
|
|
59
|
+
`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default Config
|
package/js/src/util/focustrap.js
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* --------------------------------------------------------------------------
|
|
3
|
-
* Bootstrap (v5.
|
|
3
|
+
* Bootstrap (v5.2.0): util/focustrap.js
|
|
4
4
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
5
5
|
* --------------------------------------------------------------------------
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import EventHandler from '../dom/event-handler'
|
|
9
9
|
import SelectorEngine from '../dom/selector-engine'
|
|
10
|
-
import
|
|
10
|
+
import Config from './config'
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const DefaultType = {
|
|
18
|
-
trapElement: 'element',
|
|
19
|
-
autofocus: 'boolean'
|
|
20
|
-
}
|
|
12
|
+
/**
|
|
13
|
+
* Constants
|
|
14
|
+
*/
|
|
21
15
|
|
|
22
16
|
const NAME = 'focustrap'
|
|
23
17
|
const DATA_KEY = 'bs.focustrap'
|
|
@@ -29,22 +23,49 @@ const TAB_KEY = 'Tab'
|
|
|
29
23
|
const TAB_NAV_FORWARD = 'forward'
|
|
30
24
|
const TAB_NAV_BACKWARD = 'backward'
|
|
31
25
|
|
|
32
|
-
|
|
26
|
+
const Default = {
|
|
27
|
+
autofocus: true,
|
|
28
|
+
trapElement: null // The element to trap focus inside of
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const DefaultType = {
|
|
32
|
+
autofocus: 'boolean',
|
|
33
|
+
trapElement: 'element'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Class definition
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
class FocusTrap extends Config {
|
|
33
41
|
constructor(config) {
|
|
42
|
+
super()
|
|
34
43
|
this._config = this._getConfig(config)
|
|
35
44
|
this._isActive = false
|
|
36
45
|
this._lastTabNavDirection = null
|
|
37
46
|
}
|
|
38
47
|
|
|
39
|
-
|
|
40
|
-
|
|
48
|
+
// Getters
|
|
49
|
+
static get Default() {
|
|
50
|
+
return Default
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static get DefaultType() {
|
|
54
|
+
return DefaultType
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static get NAME() {
|
|
58
|
+
return NAME
|
|
59
|
+
}
|
|
41
60
|
|
|
61
|
+
// Public
|
|
62
|
+
activate() {
|
|
42
63
|
if (this._isActive) {
|
|
43
64
|
return
|
|
44
65
|
}
|
|
45
66
|
|
|
46
|
-
if (autofocus) {
|
|
47
|
-
trapElement.focus()
|
|
67
|
+
if (this._config.autofocus) {
|
|
68
|
+
this._config.trapElement.focus()
|
|
48
69
|
}
|
|
49
70
|
|
|
50
71
|
EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop
|
|
@@ -64,12 +85,10 @@ class FocusTrap {
|
|
|
64
85
|
}
|
|
65
86
|
|
|
66
87
|
// Private
|
|
67
|
-
|
|
68
88
|
_handleFocusin(event) {
|
|
69
|
-
const { target } = event
|
|
70
89
|
const { trapElement } = this._config
|
|
71
90
|
|
|
72
|
-
if (target === document || target === trapElement || trapElement.contains(target)) {
|
|
91
|
+
if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {
|
|
73
92
|
return
|
|
74
93
|
}
|
|
75
94
|
|
|
@@ -91,15 +110,6 @@ class FocusTrap {
|
|
|
91
110
|
|
|
92
111
|
this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD
|
|
93
112
|
}
|
|
94
|
-
|
|
95
|
-
_getConfig(config) {
|
|
96
|
-
config = {
|
|
97
|
-
...Default,
|
|
98
|
-
...(typeof config === 'object' ? config : {})
|
|
99
|
-
}
|
|
100
|
-
typeCheckConfig(NAME, config, DefaultType)
|
|
101
|
-
return config
|
|
102
|
-
}
|
|
103
113
|
}
|
|
104
114
|
|
|
105
115
|
export default FocusTrap
|
package/js/src/util/index.js
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* --------------------------------------------------------------------------
|
|
3
|
-
* Bootstrap (v5.
|
|
3
|
+
* Bootstrap (v5.2.0): util/index.js
|
|
4
4
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
5
5
|
* --------------------------------------------------------------------------
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
const MAX_UID =
|
|
8
|
+
const MAX_UID = 1_000_000
|
|
9
9
|
const MILLISECONDS_MULTIPLIER = 1000
|
|
10
10
|
const TRANSITION_END = 'transitionend'
|
|
11
11
|
|
|
12
|
-
//
|
|
13
|
-
const toType =
|
|
14
|
-
if (
|
|
15
|
-
return `${
|
|
12
|
+
// Shout-out Angus Croll (https://goo.gl/pxwQGp)
|
|
13
|
+
const toType = object => {
|
|
14
|
+
if (object === null || object === undefined) {
|
|
15
|
+
return `${object}`
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
return
|
|
18
|
+
return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase()
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
23
|
-
* Public Util Api
|
|
24
|
-
* --------------------------------------------------------------------------
|
|
22
|
+
* Public Util API
|
|
25
23
|
*/
|
|
26
24
|
|
|
27
25
|
const getUID = prefix => {
|
|
@@ -36,22 +34,22 @@ const getSelector = element => {
|
|
|
36
34
|
let selector = element.getAttribute('data-bs-target')
|
|
37
35
|
|
|
38
36
|
if (!selector || selector === '#') {
|
|
39
|
-
let
|
|
37
|
+
let hrefAttribute = element.getAttribute('href')
|
|
40
38
|
|
|
41
39
|
// The only valid content that could double as a selector are IDs or classes,
|
|
42
40
|
// so everything starting with `#` or `.`. If a "real" URL is used as the selector,
|
|
43
41
|
// `document.querySelector` will rightfully complain it is invalid.
|
|
44
42
|
// See https://github.com/twbs/bootstrap/issues/32273
|
|
45
|
-
if (!
|
|
43
|
+
if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {
|
|
46
44
|
return null
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
// Just in case some CMS puts out a full URL with the anchor appended
|
|
50
|
-
if (
|
|
51
|
-
|
|
48
|
+
if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
|
|
49
|
+
hrefAttribute = `#${hrefAttribute.split('#')[1]}`
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
selector =
|
|
52
|
+
selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
return selector
|
|
@@ -100,50 +98,56 @@ const triggerTransitionEnd = element => {
|
|
|
100
98
|
element.dispatchEvent(new Event(TRANSITION_END))
|
|
101
99
|
}
|
|
102
100
|
|
|
103
|
-
const isElement =
|
|
104
|
-
if (!
|
|
101
|
+
const isElement = object => {
|
|
102
|
+
if (!object || typeof object !== 'object') {
|
|
105
103
|
return false
|
|
106
104
|
}
|
|
107
105
|
|
|
108
|
-
if (typeof
|
|
109
|
-
|
|
106
|
+
if (typeof object.jquery !== 'undefined') {
|
|
107
|
+
object = object[0]
|
|
110
108
|
}
|
|
111
109
|
|
|
112
|
-
return typeof
|
|
110
|
+
return typeof object.nodeType !== 'undefined'
|
|
113
111
|
}
|
|
114
112
|
|
|
115
|
-
const getElement =
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
const getElement = object => {
|
|
114
|
+
// it's a jQuery object or a node element
|
|
115
|
+
if (isElement(object)) {
|
|
116
|
+
return object.jquery ? object[0] : object
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
if (typeof
|
|
121
|
-
return document.querySelector(
|
|
119
|
+
if (typeof object === 'string' && object.length > 0) {
|
|
120
|
+
return document.querySelector(object)
|
|
122
121
|
}
|
|
123
122
|
|
|
124
123
|
return null
|
|
125
124
|
}
|
|
126
125
|
|
|
127
|
-
const typeCheckConfig = (componentName, config, configTypes) => {
|
|
128
|
-
Object.keys(configTypes).forEach(property => {
|
|
129
|
-
const expectedTypes = configTypes[property]
|
|
130
|
-
const value = config[property]
|
|
131
|
-
const valueType = value && isElement(value) ? 'element' : toType(value)
|
|
132
|
-
|
|
133
|
-
if (!new RegExp(expectedTypes).test(valueType)) {
|
|
134
|
-
throw new TypeError(
|
|
135
|
-
`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
}
|
|
140
|
-
|
|
141
126
|
const isVisible = element => {
|
|
142
127
|
if (!isElement(element) || element.getClientRects().length === 0) {
|
|
143
128
|
return false
|
|
144
129
|
}
|
|
145
130
|
|
|
146
|
-
|
|
131
|
+
const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'
|
|
132
|
+
// Handle `details` element as its content may falsie appear visible when it is closed
|
|
133
|
+
const closedDetails = element.closest('details:not([open])')
|
|
134
|
+
|
|
135
|
+
if (!closedDetails) {
|
|
136
|
+
return elementIsVisible
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (closedDetails !== element) {
|
|
140
|
+
const summary = element.closest('summary')
|
|
141
|
+
if (summary && summary.parentNode !== closedDetails) {
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (summary === null) {
|
|
146
|
+
return false
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return elementIsVisible
|
|
147
151
|
}
|
|
148
152
|
|
|
149
153
|
const isDisabled = element => {
|
|
@@ -196,15 +200,12 @@ const noop = () => {}
|
|
|
196
200
|
* @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
|
|
197
201
|
*/
|
|
198
202
|
const reflow = element => {
|
|
199
|
-
// eslint-disable-
|
|
200
|
-
element.offsetHeight
|
|
203
|
+
element.offsetHeight // eslint-disable-line no-unused-expressions
|
|
201
204
|
}
|
|
202
205
|
|
|
203
206
|
const getjQuery = () => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
|
|
207
|
-
return jQuery
|
|
207
|
+
if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
|
|
208
|
+
return window.jQuery
|
|
208
209
|
}
|
|
209
210
|
|
|
210
211
|
return null
|
|
@@ -217,7 +218,9 @@ const onDOMContentLoaded = callback => {
|
|
|
217
218
|
// add listener on the first call when the document is in loading state
|
|
218
219
|
if (!DOMContentLoadedCallbacks.length) {
|
|
219
220
|
document.addEventListener('DOMContentLoaded', () => {
|
|
220
|
-
|
|
221
|
+
for (const callback of DOMContentLoadedCallbacks) {
|
|
222
|
+
callback()
|
|
223
|
+
}
|
|
221
224
|
})
|
|
222
225
|
}
|
|
223
226
|
|
|
@@ -291,15 +294,15 @@ const executeAfterTransition = (callback, transitionElement, waitForTransition =
|
|
|
291
294
|
* @return {Element|elem} The proper element
|
|
292
295
|
*/
|
|
293
296
|
const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
|
|
297
|
+
const listLength = list.length
|
|
294
298
|
let index = list.indexOf(activeElement)
|
|
295
299
|
|
|
296
|
-
// if the element does not exist in the list return an element
|
|
300
|
+
// if the element does not exist in the list return an element
|
|
301
|
+
// depending on the direction and if cycle is allowed
|
|
297
302
|
if (index === -1) {
|
|
298
|
-
return
|
|
303
|
+
return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]
|
|
299
304
|
}
|
|
300
305
|
|
|
301
|
-
const listLength = list.length
|
|
302
|
-
|
|
303
306
|
index += shouldGetNext ? 1 : -1
|
|
304
307
|
|
|
305
308
|
if (isCycleAllowed) {
|
|
@@ -310,24 +313,24 @@ const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed
|
|
|
310
313
|
}
|
|
311
314
|
|
|
312
315
|
export {
|
|
316
|
+
defineJQueryPlugin,
|
|
317
|
+
execute,
|
|
318
|
+
executeAfterTransition,
|
|
319
|
+
findShadowRoot,
|
|
313
320
|
getElement,
|
|
314
|
-
getUID,
|
|
315
|
-
getSelectorFromElement,
|
|
316
321
|
getElementFromSelector,
|
|
322
|
+
getjQuery,
|
|
323
|
+
getNextActiveElement,
|
|
324
|
+
getSelectorFromElement,
|
|
317
325
|
getTransitionDurationFromElement,
|
|
318
|
-
|
|
326
|
+
getUID,
|
|
327
|
+
isDisabled,
|
|
319
328
|
isElement,
|
|
320
|
-
|
|
329
|
+
isRTL,
|
|
321
330
|
isVisible,
|
|
322
|
-
isDisabled,
|
|
323
|
-
findShadowRoot,
|
|
324
331
|
noop,
|
|
325
|
-
getNextActiveElement,
|
|
326
|
-
reflow,
|
|
327
|
-
getjQuery,
|
|
328
332
|
onDOMContentLoaded,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
executeAfterTransition
|
|
333
|
+
reflow,
|
|
334
|
+
triggerTransitionEnd,
|
|
335
|
+
toType
|
|
333
336
|
}
|
package/js/src/util/sanitizer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* --------------------------------------------------------------------------
|
|
3
|
-
* Bootstrap (v5.
|
|
3
|
+
* Bootstrap (v5.2.0): util/sanitizer.js
|
|
4
4
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
|
|
5
5
|
* --------------------------------------------------------------------------
|
|
6
6
|
*/
|
|
@@ -21,14 +21,14 @@ const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
|
|
|
21
21
|
/**
|
|
22
22
|
* A pattern that recognizes a commonly useful subset of URLs that are safe.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
24
|
+
* Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
|
|
25
25
|
*/
|
|
26
26
|
const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* A pattern that matches safe data URLs. Only matches image, video and audio types.
|
|
30
30
|
*
|
|
31
|
-
*
|
|
31
|
+
* Shout-out to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
|
|
32
32
|
*/
|
|
33
33
|
const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i
|
|
34
34
|
|
|
@@ -43,16 +43,9 @@ const allowedAttribute = (attribute, allowedAttributeList) => {
|
|
|
43
43
|
return true
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const regExp = allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)
|
|
47
|
-
|
|
48
46
|
// Check if a regular expression validates the attribute.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
return true
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
return false
|
|
47
|
+
return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)
|
|
48
|
+
.some(regex => regex.test(attributeName))
|
|
56
49
|
}
|
|
57
50
|
|
|
58
51
|
export const DefaultAllowlist = {
|
|
@@ -89,21 +82,20 @@ export const DefaultAllowlist = {
|
|
|
89
82
|
ul: []
|
|
90
83
|
}
|
|
91
84
|
|
|
92
|
-
export function sanitizeHtml(unsafeHtml, allowList,
|
|
85
|
+
export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
|
|
93
86
|
if (!unsafeHtml.length) {
|
|
94
87
|
return unsafeHtml
|
|
95
88
|
}
|
|
96
89
|
|
|
97
|
-
if (
|
|
98
|
-
return
|
|
90
|
+
if (sanitizeFunction && typeof sanitizeFunction === 'function') {
|
|
91
|
+
return sanitizeFunction(unsafeHtml)
|
|
99
92
|
}
|
|
100
93
|
|
|
101
94
|
const domParser = new window.DOMParser()
|
|
102
95
|
const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')
|
|
103
96
|
const elements = [].concat(...createdDocument.body.querySelectorAll('*'))
|
|
104
97
|
|
|
105
|
-
for (
|
|
106
|
-
const element = elements[i]
|
|
98
|
+
for (const element of elements) {
|
|
107
99
|
const elementName = element.nodeName.toLowerCase()
|
|
108
100
|
|
|
109
101
|
if (!Object.keys(allowList).includes(elementName)) {
|
|
@@ -115,11 +107,11 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {
|
|
|
115
107
|
const attributeList = [].concat(...element.attributes)
|
|
116
108
|
const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])
|
|
117
109
|
|
|
118
|
-
|
|
110
|
+
for (const attribute of attributeList) {
|
|
119
111
|
if (!allowedAttribute(attribute, allowedAttributes)) {
|
|
120
112
|
element.removeAttribute(attribute.nodeName)
|
|
121
113
|
}
|
|
122
|
-
}
|
|
114
|
+
}
|
|
123
115
|
}
|
|
124
116
|
|
|
125
117
|
return createdDocument.body.innerHTML
|