bootstrap-italia 2.13.4 → 2.15.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 (93) hide show
  1. package/dist/_virtual/_commonjsHelpers.js +6 -0
  2. package/dist/_virtual/_commonjsHelpers.js.map +1 -0
  3. package/dist/bootstrap-italia.esm.js +1 -2
  4. package/dist/bootstrap-italia.esm.js.map +1 -1
  5. package/dist/css/bootstrap-italia.min.css +1 -1
  6. package/dist/css/bootstrap-italia.min.css.map +1 -1
  7. package/dist/js/bootstrap-italia.bundle.min.js +15 -9
  8. package/dist/js/bootstrap-italia.min.js +4 -4
  9. package/dist/plugins/back-to-top.js +0 -6
  10. package/dist/plugins/back-to-top.js.map +1 -1
  11. package/dist/plugins/button.js +3 -3
  12. package/dist/plugins/button.js.map +1 -1
  13. package/dist/plugins/carousel.js +183 -402
  14. package/dist/plugins/carousel.js.map +1 -1
  15. package/dist/plugins/forward.js.map +1 -1
  16. package/dist/plugins/header-sticky.js +1 -7
  17. package/dist/plugins/header-sticky.js.map +1 -1
  18. package/dist/plugins/input-label.js +5 -1
  19. package/dist/plugins/input-label.js.map +1 -1
  20. package/dist/plugins/input-number.js +5 -7
  21. package/dist/plugins/input-number.js.map +1 -1
  22. package/dist/plugins/masonry.js +8 -1
  23. package/dist/plugins/masonry.js.map +1 -1
  24. package/dist/plugins/navbar-collapsible.js +120 -139
  25. package/dist/plugins/navbar-collapsible.js.map +1 -1
  26. package/dist/plugins/navscroll.js +27 -3
  27. package/dist/plugins/navscroll.js.map +1 -1
  28. package/dist/plugins/select-autocomplete.js +66 -17
  29. package/dist/plugins/select-autocomplete.js.map +1 -1
  30. package/dist/plugins/tooltip.js +10 -0
  31. package/dist/plugins/tooltip.js.map +1 -1
  32. package/dist/plugins/track-focus.js +0 -6
  33. package/dist/plugins/track-focus.js.map +1 -1
  34. package/dist/plugins/util/focustrap.js +23 -3
  35. package/dist/plugins/util/focustrap.js.map +1 -1
  36. package/dist/plugins/vendor/accessible-autocomplete.js +9 -0
  37. package/dist/plugins/vendor/accessible-autocomplete.js.map +1 -0
  38. package/dist/svg/sprites.svg +1 -1
  39. package/dist/version.js +1 -1
  40. package/dist/version.js.map +1 -1
  41. package/package.json +2 -3
  42. package/src/js/bootstrap-italia.entry.js +0 -2
  43. package/src/js/bootstrap-italia.esm.js +1 -2
  44. package/src/js/icons.js +9 -0
  45. package/src/js/plugins/back-to-top.js +0 -6
  46. package/src/js/plugins/button.js +3 -3
  47. package/src/js/plugins/carousel.js +184 -402
  48. package/src/js/plugins/forward.js +0 -1
  49. package/src/js/plugins/header-sticky.js +1 -7
  50. package/src/js/plugins/input-label.js +5 -1
  51. package/src/js/plugins/input-number.js +5 -7
  52. package/src/js/plugins/masonry.js +8 -1
  53. package/src/js/plugins/navbar-collapsible.js +120 -141
  54. package/src/js/plugins/navscroll.js +27 -3
  55. package/src/js/plugins/select-autocomplete.js +67 -17
  56. package/src/js/plugins/tooltip.js +10 -0
  57. package/src/js/plugins/track-focus.js +0 -6
  58. package/src/js/plugins/util/focustrap.js +23 -3
  59. package/src/js/plugins/util/pageScroll.js +1 -1
  60. package/src/js/plugins/vendor/accessible-autocomplete.js +1 -0
  61. package/src/js/version.js +1 -1
  62. package/src/scss/base/_variables.scss +1 -1
  63. package/src/scss/base/_version.scss +1 -1
  64. package/src/scss/components/_buttons.scss +1 -1
  65. package/src/scss/components/_carousel.scss +7 -235
  66. package/src/scss/components/_navigation.scss +22 -7
  67. package/src/scss/components/_tab.scss +4 -4
  68. package/src/scss/forms/_accessible-autocomplete.scss +0 -19
  69. package/src/scss/forms/_form-input-number.scss +1 -1
  70. package/src/scss/forms/_form-input-upload.scss +1 -1
  71. package/src/scss/forms/_form-toggles.scss +1 -1
  72. package/src/scss/forms/_forms.scss +2 -1
  73. package/src/scss/forms/_just-validate.scss +4 -4
  74. package/src/scss/utilities/focus.scss +26 -16
  75. package/src/svg/it-bluesky.svg +4 -0
  76. package/src/svg/it-car.svg +4 -0
  77. package/src/svg/it-cart.svg +5 -0
  78. package/src/svg/it-file-docx.svg +12 -0
  79. package/src/svg/it-file-image.svg +5 -0
  80. package/src/svg/it-file-signed.svg +16 -0
  81. package/src/svg/it-file-xlsx.svg +11 -0
  82. package/src/svg/it-sign.svg +8 -0
  83. package/src/svg/it-spotify.svg +4 -0
  84. package/types/index.d.ts +0 -2
  85. package/types/plugins/carousel.d.ts +42 -2
  86. package/dist/plugins/carousel-bi.js +0 -226
  87. package/dist/plugins/carousel-bi.js.map +0 -1
  88. package/dist/plugins/util/pageScroll.js +0 -45
  89. package/dist/plugins/util/pageScroll.js.map +0 -1
  90. package/dist/plugins/util/swipe.js +0 -150
  91. package/dist/plugins/util/swipe.js.map +0 -1
  92. package/src/js/plugins/carousel-bi.js +0 -225
  93. package/types/plugins/carousel-bi.d.ts +0 -57
@@ -33,11 +33,8 @@ const SELECTOR_BTN = 'button[class^="input-number-"]'
33
33
  class InputNumber extends BaseComponent {
34
34
  constructor(element) {
35
35
  super(element)
36
-
37
36
  this._wrapperElement = this._element.closest(SELECTOR_WRAPPER)
38
-
39
37
  this._label = new InputLabel(element)
40
-
41
38
  this._init()
42
39
  this._bindEvents()
43
40
  }
@@ -48,8 +45,6 @@ class InputNumber extends BaseComponent {
48
45
  return NAME
49
46
  }
50
47
 
51
- // Public
52
-
53
48
  // Private
54
49
  _init() {
55
50
  if (this._wrapperElement) {
@@ -83,7 +78,10 @@ class InputNumber extends BaseComponent {
83
78
  }
84
79
 
85
80
  _incrDecr(isDecr) {
86
- var inputVal = 0
81
+ let inputVal = 0
82
+ if (this._element.disabled || this._element.readonly) {
83
+ return
84
+ }
87
85
  if (this._element.value !== '') inputVal = parseFloat(this._element.value)
88
86
 
89
87
  if (!isNaN(inputVal)) {
@@ -137,7 +135,7 @@ const createInput = (element) => {
137
135
 
138
136
  if (typeof window !== 'undefined' && typeof document !== 'undefined') {
139
137
  document.addEventListener('DOMContentLoaded', function () {
140
- var frmel = document.querySelectorAll(SELECTOR_INPUT + ', label')
138
+ let frmel = document.querySelectorAll(SELECTOR_INPUT + ', label')
141
139
  frmel.forEach(function (item) {
142
140
  const target = InputLabel.getInputFromLabel(item) || item
143
141
  createInput(target)
@@ -54,6 +54,13 @@ class Masonry extends BaseComponent {
54
54
  super.dispose()
55
55
  }
56
56
 
57
+ // Public
58
+ layout() {
59
+ if (this._masonry) {
60
+ this._masonry.layout()
61
+ }
62
+ }
63
+
57
64
  // Private
58
65
  _getConfig(config) {
59
66
  config = {
@@ -86,7 +93,7 @@ class Masonry extends BaseComponent {
86
93
  _initMasonry() {
87
94
  const config = this._config
88
95
  config.container = this._element
89
- this._masonry = new MiniMasonry(config) //new MasonryPlugin(this._element, this._config)
96
+ this._masonry = new MiniMasonry(config)
90
97
  }
91
98
 
92
99
  _createLoader() {
@@ -7,14 +7,13 @@
7
7
  */
8
8
 
9
9
  import BaseComponent from './base-component.js'
10
-
11
10
  import { getElementFromSelector, isVisible, reflow } from './util/index'
12
11
  import EventHandler from './dom/event-handler'
13
12
  import SelectorEngine from './dom/selector-engine'
14
-
15
13
  import { isScreenMobile } from './util/device'
16
- import { getElementIndex } from './util/dom'
17
- import { disablePageScroll, enablePageScroll } from './util/pageScroll'
14
+ import ScrollBarHelper from './util/scrollbar'
15
+ import FocusTrap from './util/focustrap'
16
+ import Backdrop from './util/backdrop'
18
17
 
19
18
  const NAME = 'navbarcollapsible'
20
19
  const DATA_KEY = 'bs.navbarcollapsible'
@@ -23,7 +22,6 @@ const DATA_API_KEY = '.data-api'
23
22
 
24
23
  const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
25
24
  const EVENT_CLICK = `click${EVENT_KEY}`
26
- const EVENT_KEYUP = `keyup${EVENT_KEY}`
27
25
  const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
28
26
  const EVENT_HIDE = `hide${EVENT_KEY}`
29
27
  const EVENT_HIDDEN = `hidden${EVENT_KEY}`
@@ -31,54 +29,88 @@ const EVENT_SHOW = `show${EVENT_KEY}`
31
29
  const EVENT_SHOWN = `shown${EVENT_KEY}`
32
30
  const EVENT_RESIZE = `resize${EVENT_KEY}`
33
31
 
34
- const CLASS_NAME_FADE = 'fade'
32
+ const CLASS_NAME_OPEN = 'navbar-open'
35
33
  const CLASS_NAME_SHOW = 'show'
36
34
  const CLASS_NAME_EXPANDED = 'expanded'
37
35
 
38
36
  const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="navbarcollapsible"]'
39
-
40
- //const SELECTOR_NAVBAR = '.navbar-collapsable'
41
37
  const SELECTOR_BTN_CLOSE = '.close-div button'
42
38
  const SELECTOR_BTN_MENU_CLOSE = '.close-menu'
43
39
  const SELECTOR_BTN_BACK = '.it-back-button'
44
- const SELECTOR_OVERLAY = '.overlay'
45
40
  const SELECTOR_MENU_WRAPPER = '.menu-wrapper'
46
41
  const SELECTOR_NAVLINK = '.nav-link'
47
42
  const SELECTOR_MEGAMENUNAVLINK = '.nav-item .list-item'
48
43
  const SELECTOR_HEADINGLINK = '.it-heading-link'
49
44
  const SELECTOR_FOOTERLINK = '.it-footer-link'
50
45
 
46
+ const Default = {
47
+ backdrop: true,
48
+ focus: true,
49
+ }
50
+
51
+ const DefaultType = {
52
+ backdrop: '(boolean|string)',
53
+ focus: 'boolean',
54
+ }
55
+
51
56
  class NavBarCollapsible extends BaseComponent {
52
- constructor(element) {
53
- super(element)
57
+ constructor(element, config) {
58
+ super(element, config)
59
+
60
+ this._mainElement = SelectorEngine.findOne('main')
61
+ this._isNavbarOutsideMain = this._mainElement && !this._mainElement.contains(this._element)
62
+ this._parentElement = this._element.parentNode
54
63
 
55
64
  this._isShown = this._element.classList.contains(CLASS_NAME_EXPANDED)
56
- this._isTransitioning = false
57
65
 
58
- this._isMobile = isScreenMobile()
59
- this._isKeyShift = false
66
+ if (!this._element.getAttribute('tabindex')) {
67
+ this._element.setAttribute('tabindex', '-1')
68
+ }
60
69
 
61
- this._currItemIdx = 0
70
+ this._backdrop = this._initializeBackDrop()
71
+ this._focustrap = this._initializeFocusTrap()
72
+ this._scrollBar = new ScrollBarHelper()
73
+ this._isTransitioning = false
74
+ this._isMobile = isScreenMobile()
62
75
 
63
76
  this._btnClose = SelectorEngine.findOne(SELECTOR_BTN_CLOSE, this._element)
64
77
  this._btnBack = SelectorEngine.findOne(SELECTOR_BTN_BACK, this._element)
65
78
  this._menuWrapper = SelectorEngine.findOne(SELECTOR_MENU_WRAPPER, this._element)
66
- this._overlay = null
67
- this._setOverlay()
79
+
68
80
  this._menuItems = SelectorEngine.find(
69
81
  [SELECTOR_NAVLINK, SELECTOR_MEGAMENUNAVLINK, SELECTOR_HEADINGLINK, SELECTOR_FOOTERLINK, SELECTOR_BTN_MENU_CLOSE].join(','),
70
82
  this._element
71
83
  )
72
84
 
85
+ this._toggleButton =
86
+ SelectorEngine.findOne(`${SELECTOR_DATA_TOGGLE}[data-bs-target="#${this._element.id}"]`) ||
87
+ SelectorEngine.findOne(`${SELECTOR_DATA_TOGGLE}[href="#${this._element.id}"]`)
88
+
89
+ if (this._toggleButton) {
90
+ if (!this._toggleButton.getAttribute('aria-expanded')) {
91
+ this._toggleButton.setAttribute('aria-expanded', this._isShown ? 'true' : 'false')
92
+ }
93
+ }
73
94
  this._bindEvents()
74
95
  }
96
+
75
97
  // Getters
98
+ static get Default() {
99
+ return Default
100
+ }
101
+
102
+ static get DefaultType() {
103
+ return DefaultType
104
+ }
76
105
 
77
106
  static get NAME() {
78
107
  return NAME
79
108
  }
80
109
 
81
110
  // Public
111
+ toggle(relatedTarget) {
112
+ this._isShown ? this.hide() : this.show(relatedTarget)
113
+ }
82
114
 
83
115
  show(relatedTarget) {
84
116
  if (this._isShown || this._isTransitioning) {
@@ -93,14 +125,22 @@ class NavBarCollapsible extends BaseComponent {
93
125
  return
94
126
  }
95
127
 
128
+ this._isShown = true
129
+ this._isTransitioning = true
130
+ this._scrollBar.hide()
131
+
96
132
  if (this._btnBack) {
97
133
  this._btnBack.classList.add(CLASS_NAME_SHOW)
98
134
  }
99
135
 
100
- this._isShown = true
136
+ document.body.classList.add(CLASS_NAME_OPEN)
101
137
 
102
- disablePageScroll()
138
+ this._backdrop.show()
103
139
  this._showElement()
140
+
141
+ if (this._toggleButton) {
142
+ this._toggleButton.setAttribute('aria-expanded', 'true')
143
+ }
104
144
  }
105
145
 
106
146
  hide() {
@@ -116,45 +156,64 @@ class NavBarCollapsible extends BaseComponent {
116
156
 
117
157
  this._isShown = false
118
158
 
119
- const isAnimated = this._isAnimated()
120
-
121
- if (isAnimated) {
122
- this._isTransitioning = true
123
- }
159
+ this._isTransitioning = true
160
+ this._focustrap.deactivate()
124
161
 
125
162
  if (this._btnBack) {
126
163
  this._btnBack.classList.remove(CLASS_NAME_SHOW)
127
164
  }
128
- if (this._overlay) {
129
- this._overlay.classList.remove(CLASS_NAME_SHOW)
130
- }
131
165
 
132
166
  this._element.classList.remove(CLASS_NAME_EXPANDED)
133
167
 
134
- enablePageScroll()
135
- this._queueCallback(() => this._hideElement(), this._menuWrapper, isAnimated)
136
- }
168
+ this._backdrop.hide()
137
169
 
138
- toggle(relatedTarget) {
139
- this._isShown ? this.hide() : this.show(relatedTarget)
170
+ this._queueCallback(() => this._hideElement(), this._menuWrapper, this._isAnimated())
171
+
172
+ if (this._toggleButton) {
173
+ this._toggleButton.setAttribute('aria-expanded', 'false')
174
+ }
140
175
  }
141
176
 
142
177
  dispose() {
143
178
  if (typeof window !== 'undefined' && typeof document !== 'undefined') {
144
179
  EventHandler.off(window, EVENT_RESIZE)
145
- super.dispose()
180
+ EventHandler.off(document, EVENT_KEYDOWN)
146
181
  }
182
+ this._backdrop.dispose()
183
+
184
+ this._focustrap.deactivate()
185
+ super.dispose()
147
186
  }
148
187
 
149
- // Private
188
+ _initializeBackDrop() {
189
+ return new Backdrop({
190
+ isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value,
191
+ isAnimated: this._isAnimated(),
192
+ className: 'navbar-backdrop',
193
+ rootElement: this._parentElement,
194
+ clickCallback: () => {
195
+ this.hide()
196
+ },
197
+ })
198
+ }
199
+
200
+ _initializeFocusTrap() {
201
+ return new FocusTrap({
202
+ trapElement: this._element,
203
+ initialFocus: () => this._btnClose || this._element.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'),
204
+ })
205
+ }
150
206
 
207
+ // Private
151
208
  _bindEvents() {
152
209
  if (typeof window !== 'undefined' && typeof document !== 'undefined') {
153
210
  EventHandler.on(window, EVENT_RESIZE, () => this._onResize())
211
+ EventHandler.on(document, EVENT_KEYDOWN, (evt) => {
212
+ if (this._isShown && evt.key === 'Escape') {
213
+ this.hide()
214
+ }
215
+ })
154
216
 
155
- if (this._overlay) {
156
- EventHandler.on(this._overlay, EVENT_CLICK, () => this.hide())
157
- }
158
217
  EventHandler.on(this._btnClose, EVENT_CLICK, (evt) => {
159
218
  evt.preventDefault()
160
219
  this.hide()
@@ -163,11 +222,8 @@ class NavBarCollapsible extends BaseComponent {
163
222
  evt.preventDefault()
164
223
  this.hide()
165
224
  })
166
-
167
225
  this._menuItems.forEach((item) => {
168
226
  EventHandler.on(item, EVENT_KEYDOWN, (evt) => this._isMobile && this._onMenuItemKeyDown(evt))
169
- EventHandler.on(item, EVENT_KEYUP, (evt) => this._isMobile && this._onMenuItemKeyUp(evt))
170
- EventHandler.on(item, EVENT_CLICK, (evt) => this._isMobile && this._onMenuItemClick(evt))
171
227
  })
172
228
  }
173
229
  }
@@ -176,30 +232,14 @@ class NavBarCollapsible extends BaseComponent {
176
232
  this._isMobile = isScreenMobile()
177
233
  }
178
234
 
179
- _onMenuItemKeyUp(evt) {
180
- if (evt.key === 'Shift') {
181
- this._isKeyShift = false
182
- }
183
- }
184
235
  _onMenuItemKeyDown(evt) {
185
- if (evt.key === 'Shift') {
186
- this._isKeyShift = true
187
- }
188
- if (evt.key === 'Tab') {
189
- evt.preventDefault()
190
- this._focusNext()
236
+ if (evt.key === 'Escape') {
237
+ this.hide()
191
238
  }
192
239
  }
193
- /**
194
- * Update the last focused element when an interactive element is clicked
195
- */
196
- _onMenuItemClick(evt) {
197
- this.currItemIdx = getElementIndex(evt.currentTarget, this._menuItems)
198
- }
199
240
 
200
241
  _isAnimated() {
201
- //there's no an animation css class you can toggle with a "show" css class, so it is supposed true
202
- return true //this._element.classList.contains(CLASS_NAME_EXPANDED)
242
+ return true
203
243
  }
204
244
 
205
245
  _isElementHidden(element) {
@@ -207,108 +247,47 @@ class NavBarCollapsible extends BaseComponent {
207
247
  }
208
248
 
209
249
  _showElement() {
210
- const isAnimated = this._isAnimated()
211
-
212
250
  this._element.style.display = 'block'
213
- this._element.removeAttribute('aria-hidden')
214
- this._element.setAttribute('aria-expanded', true)
215
- //this._element.setAttribute('role', 'dialog')
216
- if (this._overlay) {
217
- this._overlay.style.display = 'block'
251
+ if (!this._element.getAttribute('aria-label') && !this._element.getAttribute('aria-labelledby')) {
252
+ this._element.setAttribute('aria-label', 'Menu')
218
253
  }
254
+ this._element.setAttribute('aria-modal', true)
255
+ this._element.setAttribute('role', 'dialog')
219
256
 
220
- if (isAnimated) {
221
- reflow(this._element)
257
+ if (this._mainElement && this._isNavbarOutsideMain) {
258
+ this._mainElement.setAttribute('inert', '')
222
259
  }
223
260
 
261
+ reflow(this._element)
262
+
224
263
  this._element.classList.add(CLASS_NAME_EXPANDED)
225
- if (this._overlay) {
226
- this._overlay.classList.add(CLASS_NAME_SHOW)
227
- }
228
264
 
229
265
  const transitionComplete = () => {
230
- this._isTransitioning = false
231
- const firstItem = this._getNextVisibleItem(0) //at pos 0 there's the close button
232
- if (firstItem.item) {
233
- firstItem.item.focus()
234
- this._currItemIdx = firstItem.index
266
+ if (this._config.focus) {
267
+ this._focustrap.activate()
235
268
  }
269
+ this._isTransitioning = false
236
270
  EventHandler.trigger(this._element, EVENT_SHOWN)
237
271
  }
238
272
 
239
- this._queueCallback(transitionComplete, this._menuWrapper, isAnimated)
273
+ this._queueCallback(transitionComplete, this._menuWrapper, this._isAnimated())
240
274
  }
241
275
 
242
276
  _hideElement() {
243
- if (this._overlay) {
244
- this._overlay.style.display = 'none'
245
- }
246
-
247
277
  this._element.style.display = 'none'
248
- this._element.setAttribute('aria-hidden', true)
249
- this._element.removeAttribute('aria-expanded')
250
- //this._element.removeAttribute('aria-modal')
251
- //this._element.removeAttribute('role')
252
- this._isTransitioning = false
253
- EventHandler.trigger(this._element, EVENT_HIDDEN)
254
- }
278
+ this._element.removeAttribute('aria-modal')
279
+ this._element.removeAttribute('role')
255
280
 
256
- _setOverlay() {
257
- this._overlay = SelectorEngine.findOne(SELECTOR_OVERLAY, this._element)
258
- if (this._isAnimated) {
259
- this._overlay.classList.add(CLASS_NAME_FADE)
260
- }
261
- }
281
+ document.body.classList.remove(CLASS_NAME_OPEN)
262
282
 
263
- /**
264
- * Moves focus to the next focusable element based on the DOM exploration direction
265
- */
266
- _focusNext() {
267
- let nextIdx = this._currItemIdx + (this._isKeyShift ? -1 : 1)
268
- if (nextIdx < 0) {
269
- nextIdx = this._menuItems.length - 1
270
- } else if (nextIdx >= this._menuItems.length) {
271
- nextIdx = 0
272
- }
273
- const target = this._getNextVisibleItem(nextIdx, this._isKeyShift)
274
- if (target.item) {
275
- target.item.focus()
276
- this._currItemIdx = target.index
277
- }
278
- }
279
- /**
280
- * Get the next focusable element from a starting point
281
- * @param {int} start - the index of the array of the elements as starting point (included)
282
- * @param {boolean} wayTop - the array search direction (true: bottom to top, false: top to bottom)
283
- * @returns {Object} the item found and its index in the array
284
- */
285
- _getNextVisibleItem(start, wayTop) {
286
- let found = null
287
- let foundIdx = null
288
-
289
- let i = start
290
- let incr = wayTop ? -1 : 1
291
- let firstCheck = false
292
- while (!found && (i != start || !firstCheck)) {
293
- if (i == start) {
294
- firstCheck = true
295
- }
296
- if (!this._isElementHidden(this._menuItems[i])) {
297
- found = this._menuItems[i]
298
- foundIdx = i
299
- }
300
- i = i + incr
301
- if (i < 0) {
302
- i = this._menuItems.length - 1
303
- } else if (i >= this._menuItems.length) {
304
- i = 0
305
- }
283
+ if (this._mainElement && this._isNavbarOutsideMain) {
284
+ this._mainElement.removeAttribute('inert')
306
285
  }
307
286
 
308
- return {
309
- item: found,
310
- index: foundIdx,
311
- }
287
+ this._scrollBar.reset()
288
+ this._isTransitioning = false
289
+
290
+ EventHandler.trigger(this._element, EVENT_HIDDEN)
312
291
  }
313
292
  }
314
293
 
@@ -25,6 +25,7 @@ const SELECTOR_TOGGLER = '.custom-navbar-toggler'
25
25
  const SELECTOR_TOGGLER_ICON = '.it-list'
26
26
  const SELECTOR_COLLAPSIBLE = '.navbar-collapsable'
27
27
  const SELECTOR_PROGRESS_BAR = '.it-navscroll-progressbar'
28
+ const SELECTOR_MENU_WRAPPER = '.menu-wrapper'
28
29
 
29
30
  const Default = {
30
31
  scrollPadding: 10,
@@ -44,6 +45,11 @@ class NavScroll extends BaseComponent {
44
45
  this._callbackQueue = []
45
46
  this._scrollCb = null
46
47
 
48
+ const menuWrapper = SelectorEngine.findOne(SELECTOR_MENU_WRAPPER, this._element)
49
+ if (menuWrapper && !menuWrapper.hasAttribute('tabindex')) {
50
+ menuWrapper.setAttribute('tabindex', '-1')
51
+ }
52
+
47
53
  this._bindEvents()
48
54
  }
49
55
  // Getters
@@ -147,13 +153,31 @@ class NavScroll extends BaseComponent {
147
153
  }
148
154
 
149
155
  _scrollToHash(hash) {
150
- const target = SelectorEngine.findOne(hash, this._sectionContainer)
156
+ if (!hash || hash === '#') {
157
+ // Validate hash to prevent errors
158
+ return
159
+ }
160
+ const target = this._sectionContainer // Fallback: when container is null, omit the second parameter entirely
161
+ ? SelectorEngine.findOne(hash, this._sectionContainer)
162
+ : SelectorEngine.findOne(hash)
151
163
  if (target) {
152
164
  documentScrollTo(target.offsetTop - this._getScrollPadding(), {
153
165
  duration: this._config.duration,
154
166
  easing: this._config.easing,
155
- /*complete: () => {
156
- },*/
167
+ complete: () => {
168
+ const isHeading = target.matches('h1, h2, h3, h4, h5, h6')
169
+ const needsTabIndex = !target.hasAttribute('tabindex')
170
+ if (needsTabIndex) {
171
+ target.setAttribute('tabindex', '-1')
172
+ }
173
+ target.focus({ preventScroll: true }) // preventScroll to avoid double scrolling
174
+ if (needsTabIndex && isHeading) {
175
+ // remove tabIndex for headings after 500ms
176
+ setTimeout(() => {
177
+ target.removeAttribute('tabindex')
178
+ }, 500)
179
+ }
180
+ },
157
181
  })
158
182
 
159
183
  if (history.pushState) {
@@ -6,30 +6,43 @@
6
6
  * --------------------------------------------------------------------------
7
7
  */
8
8
 
9
- import accessibleAutocomplete from 'accessible-autocomplete'
9
+ import accessibleAutocomplete from './vendor/accessible-autocomplete'
10
10
 
11
11
  import BaseComponent from './base-component.js'
12
12
 
13
+ import InputLabel from './input-label'
14
+
13
15
  const NAME = 'selectautocomplete'
14
16
 
15
17
  function onClassChange(element, callback) {
16
18
  const observer = new MutationObserver((mutations) => {
17
19
  mutations.forEach((mutation) => {
18
20
  if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
19
- callback(mutation.target)
21
+ callback(mutation.oldValue, mutation.target)
20
22
  }
21
23
  })
22
24
  })
23
- observer.observe(element, { attributes: true })
25
+ observer.observe(element, { attributes: true, attributeOldValue: true })
24
26
  return observer.disconnect
25
27
  }
26
28
 
27
29
  class SelectAutocomplete extends BaseComponent {
28
30
  constructor(element, config) {
29
31
  super(element)
30
- this._hasFormControl = element.classList.contains('form-control')
31
- this.element_original_id = this._element.id
32
- this._config = config
32
+
33
+ this._config = config || {}
34
+ this._config.inputClasses = 'form-control'
35
+ this._config.showNoOptionsFound = true
36
+ this._config.hintClasses = 'app-hint'
37
+ this._config.autoselect = false
38
+ this._config.showAllValues = false
39
+ this._config.templates = undefined
40
+ this._config.confirmOnBlur = false
41
+ this._config.menuAttributes = {}
42
+ this._config.menuClasses = null
43
+
44
+ this._extraClasses = []
45
+
33
46
  if (!this._config.tAssistiveHint)
34
47
  this._config.tAssistiveHint = () =>
35
48
  'Quando i risultati del completamento automatico sono disponibili, usa le frecce su e giù per rivedere e Invio per selezionare. Utenti di dispositivi touch, esplora tramite tocco o con gesti di scorrimento'
@@ -39,7 +52,7 @@ class SelectAutocomplete extends BaseComponent {
39
52
  if (!this._config.tStatusNoResults) this._config.tStatusNoResults = () => 'Nessun risultato di ricerca'
40
53
  if (!this._config.tStatusSelectedOption)
41
54
  this._config.tStatusSelectedOption = (selectedOption, length, index) => `${selectedOption} ${index + 1} di ${length} è sottolineato`
42
- if (!this._config.tStatusResults)
55
+ if (!this._config.tStatusResults) {
43
56
  this._config.tStatusResults = (length, contentSelectedOption) => {
44
57
  const words = {
45
58
  result: length === 1 ? 'risultato' : 'risultati',
@@ -49,7 +62,10 @@ class SelectAutocomplete extends BaseComponent {
49
62
 
50
63
  return `${length} ${words.result} ${words.is} ${words.available}. ${contentSelectedOption}`
51
64
  }
52
- this._enhance()
65
+ }
66
+ if (typeof document !== 'undefined') {
67
+ this._enhance()
68
+ }
53
69
  }
54
70
 
55
71
  // Getters
@@ -60,20 +76,54 @@ class SelectAutocomplete extends BaseComponent {
60
76
 
61
77
  // Private
62
78
  _enhance() {
63
- accessibleAutocomplete.enhanceSelectElement(Object.assign({}, { selectElement: this._element }, this._config))
79
+ const inputId = this._config.id
80
+ const originalConfirm = this._config.onConfirm
81
+
82
+ this._config.onConfirm = (value) => {
83
+ document.getElementById(inputId).value = value
84
+ document.getElementById(inputId).dispatchEvent(new Event('input'))
85
+ if (originalConfirm) {
86
+ originalConfirm(value)
87
+ }
88
+ }
89
+
90
+ accessibleAutocomplete(
91
+ Object.assign(
92
+ {},
93
+ {
94
+ element: this._element,
95
+ id: inputId,
96
+ },
97
+ this._config
98
+ )
99
+ )
64
100
  setTimeout(() => {
65
- if (this._hasFormControl) {
66
- if (typeof document === 'undefined') {
101
+ this._inputField = document.getElementById(inputId)
102
+ this._label = new InputLabel(this._inputField)
103
+ this._inputField.addEventListener('focus', () => {
104
+ this._extraClasses.forEach((cls) => {
105
+ this._inputField.classList.add(cls)
106
+ })
107
+ this._extraClasses = []
108
+ })
109
+ this._inputField.addEventListener('blur', () => {
110
+ this._extraClasses.forEach((cls) => {
111
+ this._inputField.classList.add(cls)
112
+ })
113
+ this._extraClasses = []
114
+ })
115
+
116
+ onClassChange(this._inputField, (oldClasses) => {
117
+ this._extraClasses = []
118
+ if (oldClasses === this._inputField.classList.value) {
67
119
  return
68
120
  }
69
- const inputField = document.getElementById(this.element_original_id)
70
- inputField.classList.add('form-control')
71
- onClassChange(inputField, (node) => {
72
- if (!node.classList.contains('form-control')) {
73
- node.classList.add('form-control')
121
+ oldClasses.split(' ').forEach((cls) => {
122
+ if (!cls.startsWith('autocomplete')) {
123
+ this._extraClasses.push(cls)
74
124
  }
75
125
  })
76
- }
126
+ })
77
127
  }, 100)
78
128
  }
79
129
  }
@@ -226,6 +226,16 @@ class Tooltip extends BaseComponent {
226
226
  }
227
227
  }
228
228
 
229
+ document.addEventListener(
230
+ 'keyup',
231
+ (event) => {
232
+ if (event.key === 'Escape') {
233
+ this.hide()
234
+ }
235
+ },
236
+ { once: true }
237
+ )
238
+
229
239
  const complete = () => {
230
240
  EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN))
231
241