@openeuropa/bcl-theme-joinup 0.3791.202505281825 → 0.3821.202510100015

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 (98) hide show
  1. package/bcl-builder.config.js +2 -0
  2. package/css/oe-bcl-joinup.css +782 -275
  3. package/css/oe-bcl-joinup.css.map +1 -1
  4. package/css/oe-bcl-joinup.min.css +1 -1
  5. package/css/oe-bcl-joinup.min.css.map +1 -1
  6. package/fonts/files/roboto-cyrillic-400-italic.woff +0 -0
  7. package/fonts/files/roboto-cyrillic-400-italic.woff2 +0 -0
  8. package/fonts/files/roboto-cyrillic-400-normal.woff +0 -0
  9. package/fonts/files/roboto-cyrillic-400-normal.woff2 +0 -0
  10. package/fonts/files/roboto-cyrillic-500-italic.woff +0 -0
  11. package/fonts/files/roboto-cyrillic-500-italic.woff2 +0 -0
  12. package/fonts/files/roboto-cyrillic-500-normal.woff +0 -0
  13. package/fonts/files/roboto-cyrillic-500-normal.woff2 +0 -0
  14. package/fonts/files/roboto-cyrillic-700-italic.woff +0 -0
  15. package/fonts/files/roboto-cyrillic-700-italic.woff2 +0 -0
  16. package/fonts/files/roboto-cyrillic-700-normal.woff +0 -0
  17. package/fonts/files/roboto-cyrillic-700-normal.woff2 +0 -0
  18. package/fonts/files/roboto-greek-400-italic.woff +0 -0
  19. package/fonts/files/roboto-greek-400-italic.woff2 +0 -0
  20. package/fonts/files/roboto-greek-400-normal.woff +0 -0
  21. package/fonts/files/roboto-greek-400-normal.woff2 +0 -0
  22. package/fonts/files/roboto-greek-500-italic.woff +0 -0
  23. package/fonts/files/roboto-greek-500-italic.woff2 +0 -0
  24. package/fonts/files/roboto-greek-500-normal.woff +0 -0
  25. package/fonts/files/roboto-greek-500-normal.woff2 +0 -0
  26. package/fonts/files/roboto-greek-700-italic.woff +0 -0
  27. package/fonts/files/roboto-greek-700-italic.woff2 +0 -0
  28. package/fonts/files/roboto-greek-700-normal.woff +0 -0
  29. package/fonts/files/roboto-greek-700-normal.woff2 +0 -0
  30. package/fonts/files/roboto-latin-400-italic.woff +0 -0
  31. package/fonts/files/roboto-latin-400-italic.woff2 +0 -0
  32. package/fonts/files/roboto-latin-400-normal.woff +0 -0
  33. package/fonts/files/roboto-latin-400-normal.woff2 +0 -0
  34. package/fonts/files/roboto-latin-500-italic.woff +0 -0
  35. package/fonts/files/roboto-latin-500-italic.woff2 +0 -0
  36. package/fonts/files/roboto-latin-500-normal.woff +0 -0
  37. package/fonts/files/roboto-latin-500-normal.woff2 +0 -0
  38. package/fonts/files/roboto-latin-700-italic.woff +0 -0
  39. package/fonts/files/roboto-latin-700-italic.woff2 +0 -0
  40. package/fonts/files/roboto-latin-700-normal.woff +0 -0
  41. package/fonts/files/roboto-latin-700-normal.woff2 +0 -0
  42. package/fonts/files/roboto-latin-ext-400-italic.woff +0 -0
  43. package/fonts/files/roboto-latin-ext-400-italic.woff2 +0 -0
  44. package/fonts/files/roboto-latin-ext-400-normal.woff +0 -0
  45. package/fonts/files/roboto-latin-ext-400-normal.woff2 +0 -0
  46. package/fonts/files/roboto-latin-ext-500-italic.woff +0 -0
  47. package/fonts/files/roboto-latin-ext-500-italic.woff2 +0 -0
  48. package/fonts/files/roboto-latin-ext-500-normal.woff +0 -0
  49. package/fonts/files/roboto-latin-ext-500-normal.woff2 +0 -0
  50. package/fonts/files/roboto-latin-ext-700-italic.woff +0 -0
  51. package/fonts/files/roboto-latin-ext-700-italic.woff2 +0 -0
  52. package/fonts/files/roboto-latin-ext-700-normal.woff +0 -0
  53. package/fonts/files/roboto-latin-ext-700-normal.woff2 +0 -0
  54. package/icons/bcl-default-icons.svg +1 -1
  55. package/icons/bootstrap-icons.svg +1 -1
  56. package/js/oe-bcl-joinup.bundle.js +189 -5
  57. package/js/oe-bcl-joinup.bundle.js.map +1 -1
  58. package/js/oe-bcl-joinup.bundle.min.js +1 -1
  59. package/js/oe-bcl-joinup.bundle.min.js.map +1 -1
  60. package/js/oe-bcl-joinup.esm.js +188 -6
  61. package/js/oe-bcl-joinup.esm.js.map +1 -1
  62. package/js/oe-bcl-joinup.esm.min.js +1 -1
  63. package/js/oe-bcl-joinup.esm.min.js.map +1 -1
  64. package/js/oe-bcl-joinup.umd.js +189 -5
  65. package/js/oe-bcl-joinup.umd.js.map +1 -1
  66. package/js/oe-bcl-joinup.umd.min.js +1 -1
  67. package/js/oe-bcl-joinup.umd.min.js.map +1 -1
  68. package/js/slim-select-2/slimselect.min.js +1 -1
  69. package/package.json +12 -12
  70. package/src/js/index.esm.js +4 -0
  71. package/src/js/index.umd.js +4 -0
  72. package/src/scss/oe-bcl-joinup.scss +1 -0
  73. package/templates/bcl-base-templates/listing-page.html.twig +17 -19
  74. package/templates/bcl-button/button.html.twig +3 -2
  75. package/templates/bcl-contact-form/contact-form.html.twig +3 -3
  76. package/templates/bcl-content-banner/content-banner.html.twig +3 -0
  77. package/templates/bcl-dropdown/dropdown.html.twig +12 -7
  78. package/templates/bcl-event/event.html.twig +5 -5
  79. package/templates/bcl-glossary/glossary-detail.html.twig +4 -5
  80. package/templates/bcl-glossary/glossary-listing.html.twig +5 -5
  81. package/templates/bcl-group/group-landing.html.twig +5 -5
  82. package/templates/bcl-group/group.html.twig +3 -3
  83. package/templates/bcl-header/header.html.twig +37 -6
  84. package/templates/bcl-inpage-navigation/inpage-navigation.html.twig +7 -2
  85. package/templates/bcl-landing-page/landing-page.html.twig +5 -8
  86. package/templates/bcl-mega-menu/mega-menu-items.html.twig +35 -0
  87. package/templates/bcl-mega-menu/mega-menu-submenu.html.twig +65 -0
  88. package/templates/bcl-mega-menu/mega-menu.html.twig +115 -0
  89. package/templates/bcl-navigation/navigation.html.twig +11 -1
  90. package/templates/bcl-offcanvas/offcanvas.html.twig +9 -6
  91. package/templates/bcl-page/page.html.twig +5 -5
  92. package/templates/bcl-person/person.html.twig +5 -5
  93. package/templates/bcl-project/project.html.twig +7 -7
  94. package/templates/bcl-project-status/project-contributions.html.twig +1 -1
  95. package/templates/bcl-search/search.html.twig +3 -3
  96. package/templates/bcl-subscription/subscription.html.twig +5 -5
  97. package/templates/bcl-user/user-terms.html.twig +3 -3
  98. package/templates/bcl-user/user.html.twig +3 -3
@@ -642,7 +642,7 @@
642
642
  * Constants
643
643
  */
644
644
 
645
- const VERSION = '5.3.6';
645
+ const VERSION = '5.3.8';
646
646
 
647
647
  /**
648
648
  * Class definition
@@ -3511,9 +3511,6 @@
3511
3511
  this._element.setAttribute('aria-expanded', 'false');
3512
3512
  Manipulator$1.removeDataAttribute(this._menu, 'popper');
3513
3513
  EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget);
3514
-
3515
- // Explicitly return focus to the trigger element
3516
- this._element.focus();
3517
3514
  }
3518
3515
  _getConfig(config) {
3519
3516
  config = super._getConfig(config);
@@ -4834,6 +4831,191 @@
4834
4831
  }
4835
4832
  }
4836
4833
 
4834
+ class MainNavigation {
4835
+ constructor(toggler) {
4836
+ this.toggler = toggler;
4837
+ this.top = 0;
4838
+ this.target = this.getTargetFromToggler(toggler);
4839
+ if (!this.target) return;
4840
+ this.addListeners();
4841
+ }
4842
+ getTargetFromToggler(toggler) {
4843
+ const selector = toggler.getAttribute("data-bs-target");
4844
+ if (!selector) return null;
4845
+ try {
4846
+ return document.querySelector(selector);
4847
+ } catch {
4848
+ return null;
4849
+ }
4850
+ }
4851
+ addListeners() {
4852
+ EventHandler.on(this.target, "show.bs.collapse", () => {
4853
+ window.scrollTo(0, this.top);
4854
+ });
4855
+ }
4856
+ static init(selector = ".bcl-toggler", options) {
4857
+ const togglers = SelectorEngine.find(selector);
4858
+ togglers.forEach(toggler => new MainNavigation(toggler, options));
4859
+ }
4860
+ }
4861
+ document.addEventListener("DOMContentLoaded", () => {
4862
+ // Run for all .bcl-toggler buttons
4863
+ MainNavigation.init(".bcl-navbar-toggler");
4864
+ });
4865
+
4866
+ class MegaMenu {
4867
+ constructor(root) {
4868
+ this.root = root;
4869
+ this.backButton = SelectorEngine.findOne(".back-button", this.root);
4870
+ this.trigger = SelectorEngine.findOne(':scope > .dropdown-toggle[data-bs-toggle="dropdown"]', this.root);
4871
+ this.ul = SelectorEngine.findOne('.bcl-mega-menu__items.__level-1', this.root);
4872
+ this.addSubmenuTriggerListeners();
4873
+ this.addBackButtonListener();
4874
+ this.addTriggerListeners();
4875
+ this.addEscapeKeyHandler();
4876
+ }
4877
+ getPanelForTrigger(trigger) {
4878
+ const id = trigger.getAttribute('aria-controls');
4879
+ return id ? document.getElementById(id) : null;
4880
+ }
4881
+ getFocusableChildren(container) {
4882
+ if (!container) return [];
4883
+ return Array.from(container.querySelectorAll('a[href], button:not([disabled]):not(.back-button), [tabindex]:not([tabindex="-1"])')).filter(el => !el.hasAttribute('disabled') && el.tabIndex !== -1);
4884
+ }
4885
+ focusFirstItemInPanel(trigger) {
4886
+ const panel = this.getPanelForTrigger(trigger);
4887
+ if (!panel) return;
4888
+ const list = SelectorEngine.findOne('ul.bcl-mega-menu__items', panel);
4889
+ if (!list) return;
4890
+ const first = this.getFocusableChildren(list)[0];
4891
+ if (first) first.focus();
4892
+ }
4893
+ addEscapeKeyHandler() {
4894
+ // Bootstrap attaches its dropdown keydown listener on `document` in the capture phase.
4895
+ // By listening on `window` in the capture phase, our handler runs *before* Bootstrap’s.
4896
+ // This lets us intercept Esc inside mega menu submenus and stop Bootstrap from closing
4897
+ // the entire dropdown.
4898
+ window.addEventListener('keydown', e => {
4899
+ if (e.key !== 'Escape') return;
4900
+
4901
+ // Only act if Esc originated inside THIS mega menu's open submenu
4902
+ const panel = e.target.closest('.bcl-mega-menu__submenu');
4903
+ if (!panel || panel.hidden || !this.root.contains(panel)) {
4904
+ // Not our submenu: let Bootstrap handle it normally
4905
+ return;
4906
+ }
4907
+
4908
+ // Stop the event BEFORE it reaches Bootstrap's document-capture handler
4909
+ e.preventDefault();
4910
+ e.stopImmediatePropagation();
4911
+ e.stopPropagation();
4912
+
4913
+ // Close only this submenu and focus its trigger
4914
+ const triggerId = panel.getAttribute('aria-labelledby');
4915
+ const trigger = triggerId ? document.getElementById(triggerId) : null;
4916
+ if (trigger) {
4917
+ this.closeSubmenu(trigger);
4918
+ trigger.focus();
4919
+ }
4920
+ }, true);
4921
+ }
4922
+ addTriggerListeners() {
4923
+ if (!this.trigger) return;
4924
+
4925
+ // When the mega menu is opened, focus the first item in the menu.
4926
+ EventHandler.on(this.trigger, 'shown.bs.dropdown', () => {
4927
+ const panelId = this.trigger.getAttribute('aria-controls');
4928
+ const panel = panelId ? document.getElementById(panelId) : null;
4929
+ const firstFocusable = panel ? this.getFocusableChildren(panel)[0] : null;
4930
+ if (firstFocusable) firstFocusable.focus();
4931
+ });
4932
+
4933
+ // When the mega menu is closed, close all submenus.
4934
+ EventHandler.on(this.trigger, 'hide.bs.dropdown', () => {
4935
+ this.closeAllSubmenus();
4936
+ });
4937
+ }
4938
+ addSubmenuTriggerListeners() {
4939
+ // Clicking/activating a parent item button toggles the submenu.
4940
+ SelectorEngine.find(':scope li > button[aria-expanded]', this.root).forEach(trigger => {
4941
+ EventHandler.on(trigger, "click", () => {
4942
+ const expanded = trigger.getAttribute('aria-expanded') === 'true';
4943
+ if (expanded) {
4944
+ // Close this submenu.
4945
+ this.closeSubmenu(trigger);
4946
+ } else {
4947
+ this.openSubmenu(trigger);
4948
+ // The back button is only visible in mobile / narrow viewport.
4949
+ if (this.backButton && this.backButton.offsetParent !== null) {
4950
+ this.backButton.focus();
4951
+ } else {
4952
+ this.focusFirstItemInPanel(trigger);
4953
+ }
4954
+ }
4955
+ });
4956
+ });
4957
+ }
4958
+ addBackButtonListener() {
4959
+ // Clicking a back button closes the submenu or the menu itself.
4960
+ if (!this.backButton) {
4961
+ return;
4962
+ }
4963
+ EventHandler.on(this.backButton, "click", () => {
4964
+ const submenusThatWereOpen = this.closeAllSubmenus();
4965
+ if (submenusThatWereOpen.length > 0) {
4966
+ // Focus the submenu trigger, to allow quick reopen by keystroke.
4967
+ submenusThatWereOpen[0].focus();
4968
+ return;
4969
+ }
4970
+ // Close the mega menu itself.
4971
+ if (this.trigger) {
4972
+ // Close using the Bootstrap dropdown API.
4973
+ Dropdown.getOrCreateInstance(this.trigger).hide();
4974
+ // Focus the main trigger, to allow quick reopen by keystroke.
4975
+ this.trigger.focus();
4976
+ }
4977
+ });
4978
+ }
4979
+ openSubmenu(trigger) {
4980
+ // Close all submenus, then open the current submenu.
4981
+ this.closeAllSubmenus();
4982
+ trigger.setAttribute('aria-expanded', 'true');
4983
+ const panel = this.getPanelForTrigger(trigger);
4984
+ if (panel) panel.hidden = false;
4985
+ }
4986
+
4987
+ /**
4988
+ * Closes all submenus.
4989
+ *
4990
+ * This is simple while there is only one submenu level.
4991
+ *
4992
+ * @returns {HTMLElement[]}
4993
+ * Triggers for submenus that were closed.
4994
+ * Usually this is either exactly one, or none.
4995
+ */
4996
+ closeAllSubmenus() {
4997
+ if (!this.ul) {
4998
+ return;
4999
+ }
5000
+ const triggers = SelectorEngine.find(':scope > li > button[aria-expanded="true"]', this.ul);
5001
+ // Use arrow fn to keep `this` bound.
5002
+ triggers.forEach(t => this.closeSubmenu(t));
5003
+ return triggers;
5004
+ }
5005
+ closeSubmenu(trigger) {
5006
+ trigger.setAttribute('aria-expanded', 'false');
5007
+ const panel = this.getPanelForTrigger(trigger);
5008
+ if (panel) panel.hidden = true;
5009
+ }
5010
+ static init(selector = ".bcl-mega-menu") {
5011
+ const megaMenus = SelectorEngine.find(selector);
5012
+ megaMenus.forEach(menuEl => new MegaMenu(menuEl));
5013
+ }
5014
+ }
5015
+ document.addEventListener("DOMContentLoaded", () => {
5016
+ MegaMenu.init();
5017
+ });
5018
+
4837
5019
  /**
4838
5020
  * --------------------------------------------------------------------------
4839
5021
  * Bootstrap util/sanitizer.js
@@ -4889,7 +5071,6 @@
4889
5071
  *
4890
5072
  * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38
4891
5073
  */
4892
- // eslint-disable-next-line unicorn/better-regex
4893
5074
  const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;
4894
5075
  const allowedAttribute = (attribute, allowedAttributeList) => {
4895
5076
  const attributeName = attribute.nodeName.toLowerCase();
@@ -5433,6 +5614,7 @@
5433
5614
  if (trigger === 'click') {
5434
5615
  EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => {
5435
5616
  const context = this._initializeOnDelegatedTarget(event);
5617
+ context._activeTrigger[TRIGGER_CLICK] = !(context._isShown() && context._activeTrigger[TRIGGER_CLICK]);
5436
5618
  context.toggle();
5437
5619
  });
5438
5620
  } else if (trigger !== TRIGGER_MANUAL) {
@@ -6748,6 +6930,8 @@
6748
6930
  Dropdown,
6749
6931
  Gallery,
6750
6932
  Modal,
6933
+ MainNavigation,
6934
+ MegaMenu,
6751
6935
  AccessibleToggle,
6752
6936
  AccordionToggle,
6753
6937
  Offcanvas,