@orangesk/orange-design-system 2.0.0-beta.30 → 2.0.0-beta.32

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 (82) hide show
  1. package/README.md +7 -7
  2. package/build/appstore.svg +31 -0
  3. package/build/components/Breadcrumbs/style.css +1 -1
  4. package/build/components/Breadcrumbs/style.css.map +1 -1
  5. package/build/components/Carousel/style.css +1 -1
  6. package/build/components/Carousel/style.css.map +1 -1
  7. package/build/components/Footer/style.css +1 -1
  8. package/build/components/Footer/style.css.map +1 -1
  9. package/build/components/Megamenu/style.css +1 -1
  10. package/build/components/Megamenu/style.css.map +1 -1
  11. package/build/components/Preview/style.css +1 -1
  12. package/build/components/Preview/style.css.map +1 -1
  13. package/build/components/PromoBanner/style.css +1 -1
  14. package/build/components/PromoBanner/style.css.map +1 -1
  15. package/build/components/index.js +1 -1
  16. package/build/components/index.js.map +1 -1
  17. package/build/components/tsconfig.tsbuildinfo +1 -1
  18. package/build/components/types/index.d.ts +17 -2
  19. package/build/components/types/src/components/Carousel/Carousel.static.d.ts +1 -11
  20. package/build/components/types/src/components/Footer/Footer.static.d.ts +21 -0
  21. package/build/components/types/src/components/Footer/constants.d.ts +12 -2
  22. package/build/components/types/src/components/Footer/data.d.ts +5 -0
  23. package/build/components/types/src/components/Footer/static.d.ts +21 -0
  24. package/build/components/types/src/components/Megamenu/Megamenu.d.ts +4 -2
  25. package/build/components/types/src/components/Megamenu/Megamenu.static.d.ts +16 -5
  26. package/build/components/types/src/components/Megamenu/MegamenuBlog.d.ts +1 -1
  27. package/build/components/types/src/components/Megamenu/MegamenuSearchContent.d.ts +10 -0
  28. package/build/components/types/src/components/Megamenu/MyOrangeMobilePanel.d.ts +6 -0
  29. package/build/components/types/src/components/Megamenu/constants.d.ts +15 -0
  30. package/build/components/types/src/components/Megamenu/data.d.ts +21 -0
  31. package/build/components/types/src/components/Megamenu/ids.d.ts +11 -0
  32. package/build/components/types/src/components/PromoBanner/PromoBanner.d.ts +5 -1
  33. package/build/components/types/src/components/index.d.ts +2 -1
  34. package/build/googleplay.svg +52 -0
  35. package/build/lib/base.css.map +1 -1
  36. package/build/lib/components.css +1 -1
  37. package/build/lib/components.css.map +1 -1
  38. package/build/lib/footer.css +1 -1
  39. package/build/lib/footer.css.map +1 -1
  40. package/build/lib/footer.js +2 -0
  41. package/build/lib/footer.js.map +1 -0
  42. package/build/lib/megamenu.css +1 -1
  43. package/build/lib/megamenu.css.map +1 -1
  44. package/build/lib/megamenu.js +1 -1
  45. package/build/lib/megamenu.js.map +1 -1
  46. package/build/lib/scripts.js +1 -1
  47. package/build/lib/scripts.js.map +1 -1
  48. package/build/lib/style.css +1 -1
  49. package/build/lib/style.css.map +1 -1
  50. package/build/lib/tsconfig.tsbuildinfo +1 -1
  51. package/build/lib/utilities.css +1 -1
  52. package/build/lib/utilities.css.map +1 -1
  53. package/build/search-index.json +8 -4
  54. package/package.json +12 -12
  55. package/src/components/Breadcrumbs/styles/mixins.scss +6 -1
  56. package/src/components/Carousel/Carousel.static.ts +60 -89
  57. package/src/components/Carousel/styles/mixins.scss +4 -12
  58. package/src/components/Footer/Footer.static.ts +130 -0
  59. package/src/components/Footer/Footer.tsx +142 -62
  60. package/src/components/Footer/constants.ts +12 -2
  61. package/src/components/Footer/data.ts +13 -0
  62. package/src/components/Footer/static.ts +59 -0
  63. package/src/components/Footer/styles/mixins.scss +122 -18
  64. package/src/components/Footer/styles/style.scss +63 -4
  65. package/src/components/Footer/tests/Footer.unit.test.js +2 -2
  66. package/src/components/Megamenu/Megamenu.static.ts +200 -90
  67. package/src/components/Megamenu/Megamenu.tsx +363 -615
  68. package/src/components/Megamenu/MegamenuBlog.tsx +192 -73
  69. package/src/components/Megamenu/MegamenuSearchContent.tsx +74 -0
  70. package/src/components/Megamenu/MyOrangeMobilePanel.tsx +127 -0
  71. package/src/components/Megamenu/constants.ts +15 -0
  72. package/src/components/Megamenu/data.ts +231 -0
  73. package/src/components/Megamenu/ids.ts +35 -0
  74. package/src/components/Megamenu/styles/mixins.scss +223 -16
  75. package/src/components/Megamenu/styles/style.scss +64 -0
  76. package/src/components/Preview/styles/style.scss +2 -1
  77. package/src/components/PromoBanner/PromoBanner.tsx +12 -1
  78. package/src/components/PromoBanner/styles/mixins.scss +31 -7
  79. package/src/components/PromoBanner/styles/style.scss +41 -0
  80. package/src/components/PromoBanner/tests/PromoBanner.unit.test.js +44 -0
  81. package/src/components/index.ts +3 -0
  82. package/src/styles/utilities/horizontal-scroll.scss +28 -11
@@ -185,55 +185,46 @@ export default class Carousel {
185
185
  `.${CLASS_VIEWPORT_WRAPPER}`,
186
186
  ) as HTMLElement;
187
187
  const scrollbar = this.instance?.scrollbar;
188
+ const swiper = this.instance;
188
189
 
189
- if (!viewportWrapper || !scrollbar || !scrollbar.dragEl) {
190
+ if (!viewportWrapper || !scrollbar || !scrollbar.dragEl || !swiper) {
190
191
  return;
191
192
  }
192
193
 
193
- const slidesCount = this.instance.slides.length;
194
- const slidesPerView = Number(this.instance.params.slidesPerView) || 1;
194
+ const minTranslate = swiper.minTranslate();
195
+ const maxTranslate = swiper.maxTranslate();
196
+ const translateRange = Math.abs(maxTranslate - minTranslate);
195
197
 
196
- // Only show scrollbar when there are more slides than visible at once
197
- if (slidesCount > slidesPerView) {
198
+ if (translateRange <= 1 || !swiper.enabled) {
198
199
  if (scrollbar.el) {
199
- scrollbar.el.style.display = "";
200
+ scrollbar.el.style.display = "none";
200
201
  }
202
+ return;
203
+ }
201
204
 
202
- const scrollbarWidth = scrollbar.el.offsetWidth;
203
-
204
- // Calculate drag handle size based on the ratio of visible slides to total slides
205
- // Example: 3 visible out of 5 total = 60% of scrollbar width
206
- const visibleRatio = slidesPerView / slidesCount;
207
- const dragSize = visibleRatio * scrollbarWidth;
208
- // Ensure minimum 30px for usability
209
- const finalDragSize = Math.max(dragSize, 30);
205
+ if (scrollbar.el) {
206
+ scrollbar.el.style.display = "";
207
+ }
210
208
 
211
- scrollbar.dragEl.style.width = `${finalDragSize}px`;
212
- scrollbar.dragEl.style.display = "";
209
+ const scrollbarWidth = scrollbar.el.offsetWidth;
210
+ const viewportWidth = viewportWrapper.clientWidth;
211
+ const offsetBefore = Number(swiper.params.slidesOffsetBefore) || 0;
212
+ const offsetAfter = Number(swiper.params.slidesOffsetAfter) || 0;
213
+ const contentWidth = this.track.scrollWidth + offsetBefore + offsetAfter;
213
214
 
214
- // Calculate scrollbar position based on carousel's actual scroll position
215
- const maxScrollableSlides = slidesCount - slidesPerView;
216
- const currentTranslate = Math.abs(this.instance.translate || 0);
217
- const slideSize = this.instance.slidesSizesGrid?.[0] || 0;
215
+ const visibleRatio =
216
+ contentWidth > 0 ? Math.min(viewportWidth / contentWidth, 1) : 1;
217
+ const dragSize = visibleRatio * scrollbarWidth;
218
+ const finalDragSize = Math.max(dragSize, 30);
218
219
 
219
- // Determine how far we've scrolled as a ratio (0 = start, 1 = end)
220
- const maxTranslateValue = maxScrollableSlides * slideSize;
221
- const scrollRatio =
222
- maxTranslateValue > 0
223
- ? Math.min(currentTranslate / maxTranslateValue, 1)
224
- : 0;
220
+ scrollbar.dragEl.style.width = `${finalDragSize}px`;
221
+ scrollbar.dragEl.style.display = "";
225
222
 
226
- // Position scrollbar drag handle proportionally
227
- const maxDragTranslate = scrollbarWidth - finalDragSize;
228
- const dragTranslateX = scrollRatio * maxDragTranslate;
223
+ const scrollRatio = Math.min(Math.max(swiper.progress || 0, 0), 1);
224
+ const maxDragTranslate = Math.max(scrollbarWidth - finalDragSize, 0);
225
+ const dragTranslateX = scrollRatio * maxDragTranslate;
229
226
 
230
- scrollbar.dragEl.style.transform = `translate3d(${dragTranslateX}px, 0, 0)`;
231
- } else {
232
- // Hide scrollbar when all slides are visible (no scrolling needed)
233
- if (scrollbar.el) {
234
- scrollbar.el.style.display = "none";
235
- }
236
- }
227
+ scrollbar.dragEl.style.transform = `translate3d(${dragTranslateX}px, 0, 0)`;
237
228
  };
238
229
 
239
230
  // Initial update (double requestAnimationFrame ensures DOM is fully ready)
@@ -330,52 +321,44 @@ export default class Carousel {
330
321
  }
331
322
  }
332
323
 
333
- /**
334
- * Calculate how much the carousel should extend beyond the container (bleed effect).
335
- * On screens 2560px and wider, the bleed effect is disabled.
336
- * Also disabled when all slides fit on screen (slidesCount <= slidesPerView).
337
- */
338
- private calculateBleedAmount(container: HTMLElement): number {
339
- const containerWidth = container.offsetWidth;
340
- const halfViewportWidth = window.innerWidth / 2;
341
- const halfContainerWidth = containerWidth / 2;
342
- let bleedAmount = halfViewportWidth - halfContainerWidth;
343
-
344
- if (window.innerWidth >= 2560) {
345
- bleedAmount = 0;
324
+ private applyBleedInsets(viewportWrapper: HTMLElement): void {
325
+ if (!this.instance || !this.instance.params) {
326
+ return;
346
327
  }
347
328
 
348
- // Disable bleed when all slides fit on screen (no scrolling needed)
349
- const slidesPerView = Number(this.instance?.params.slidesPerView) || 1;
350
- const slidesCount = this.instance?.slides.length || 0;
351
- if (slidesCount <= slidesPerView) {
352
- bleedAmount = 0;
353
- }
329
+ const viewportWidth =
330
+ document.documentElement.clientWidth ||
331
+ window.visualViewport?.width ||
332
+ window.innerWidth;
354
333
 
355
- return Math.max(bleedAmount, 0);
356
- }
334
+ const rect = viewportWrapper.getBoundingClientRect();
335
+ const insetLeft = Math.max(Math.round(rect.left), 0);
336
+ const trailingSlidePadding = 20;
337
+ const insetAfter = Math.max(insetLeft - trailingSlidePadding, 0);
357
338
 
358
- /**
359
- * Apply the bleed offset to extend the carousel beyond the container edge.
360
- * Only applies when content exceeds the visible area.
361
- */
362
- private applyBleedOffset(
363
- viewportWrapper: HTMLElement,
364
- bleedAmount: number,
365
- ): void {
366
- // Safety check: ensure instance and params are initialized
367
- if (!this.instance || !this.instance.params) {
368
- return;
369
- }
339
+ const slidesPerView = Number(this.instance.params.slidesPerView) || 1;
340
+ const slidesCount = this.instance.slides.length;
341
+ const shouldDisableBleed =
342
+ viewportWidth >= 2560 || slidesCount <= slidesPerView;
370
343
 
371
- const visibleWidth = viewportWrapper.clientWidth;
372
- const totalWidth = this.track.scrollWidth;
373
- const contentFitsWithoutScroll = totalWidth <= visibleWidth + 5;
344
+ this.element.classList.toggle(CLASS_BLEED_RIGHT, !shouldDisableBleed);
374
345
 
375
- if (bleedAmount > 0 && !contentFitsWithoutScroll) {
376
- this.instance.params.slidesOffsetAfter = bleedAmount;
377
- } else {
346
+ if (shouldDisableBleed) {
347
+ this.instance.params.slidesOffsetBefore = 0;
378
348
  this.instance.params.slidesOffsetAfter = 0;
349
+ this.element.style.setProperty("--carousel-bleed-viewport-width", "100%");
350
+ this.element.style.setProperty("--carousel-bleed-margin-left", "0px");
351
+ } else {
352
+ this.instance.params.slidesOffsetBefore = insetLeft;
353
+ this.instance.params.slidesOffsetAfter = insetAfter;
354
+ this.element.style.setProperty(
355
+ "--carousel-bleed-viewport-width",
356
+ "100dvw",
357
+ );
358
+ this.element.style.setProperty(
359
+ "--carousel-bleed-margin-left",
360
+ "calc(50% - 50dvw)",
361
+ );
379
362
  }
380
363
 
381
364
  this.instance.update();
@@ -389,8 +372,7 @@ export default class Carousel {
389
372
  */
390
373
  adjustConfigForBleedRight() {
391
374
  requestAnimationFrame(() => {
392
- const container = this.element.closest(".container") as HTMLElement;
393
- if (!container || !this.instance) return;
375
+ if (!this.instance) return;
394
376
 
395
377
  const viewportWrapper = this.element.querySelector(
396
378
  `.${CLASS_VIEWPORT_WRAPPER}`,
@@ -398,18 +380,7 @@ export default class Carousel {
398
380
  if (!viewportWrapper) return;
399
381
 
400
382
  const updateBleedState = () => {
401
- const slidesPerView = Number(this.instance?.params.slidesPerView) || 1;
402
- const slidesCount = this.instance?.slides.length || 0;
403
- const shouldDisableBleed = slidesCount <= slidesPerView;
404
-
405
- if (shouldDisableBleed) {
406
- this.element.classList.remove(CLASS_BLEED_RIGHT);
407
- } else {
408
- this.element.classList.add(CLASS_BLEED_RIGHT);
409
- }
410
-
411
- const bleedAmount = this.calculateBleedAmount(container);
412
- this.applyBleedOffset(viewportWrapper, bleedAmount);
383
+ this.applyBleedInsets(viewportWrapper);
413
384
 
414
385
  // Update margin compensation on resize
415
386
  this.applyTrackMarginCompensation();
@@ -210,25 +210,17 @@
210
210
  }
211
211
 
212
212
  @mixin bleed-right-variant {
213
- // Allow overflow on the left side at the carousel container level
214
213
  overflow: visible;
215
214
 
216
215
  .carousel__viewport-wrapper {
217
- // Allow overflow from viewport
218
216
  overflow: visible;
219
217
  }
220
218
 
221
219
  .carousel__viewport {
222
- // Extend the viewport to the right edge of the screen
223
- width: calc(100% + 50vw - 50%);
220
+ width: var(--carousel-bleed-viewport-width, 100%);
221
+ max-width: var(--carousel-bleed-viewport-width, 100%);
224
222
  margin-right: 0;
225
- margin-left: 0 !important; // Don't add left margin - let content flow naturally
226
- overflow: visible !important; // Allow content to overflow on left side
227
- overflow-y: hidden; // Still hide vertical overflow
228
-
229
- // On very large screens (above 2K), stop the bleeding effect entirely
230
- @media (min-width: 2560px) {
231
- width: 100%;
232
- }
223
+ margin-left: var(--carousel-bleed-margin-left, 0) !important;
224
+ overflow: hidden !important;
233
225
  }
234
226
  }
@@ -0,0 +1,130 @@
1
+ import { CLASS_ACCORDION_TOGGLE, CLASS_MENU_SECTION } from "./constants";
2
+
3
+ const MOBILE_MEDIA_QUERY = "(max-width: 479.98px)";
4
+
5
+ interface FooterSection {
6
+ element: HTMLElement;
7
+ toggle: HTMLButtonElement | null;
8
+ panel: HTMLElement | null;
9
+ }
10
+
11
+ export default class Footer {
12
+ element!: HTMLElement;
13
+ sections!: FooterSection[];
14
+ mediaQueryList!: MediaQueryList;
15
+ onToggleClick!: (event: Event) => void;
16
+ onMediaQueryChange!: () => void;
17
+
18
+ constructor(element: HTMLElement) {
19
+ if ((element as any).ODS_Footer) {
20
+ return (element as any).ODS_Footer;
21
+ }
22
+
23
+ this.element = element;
24
+ this.sections = [];
25
+ this.mediaQueryList = window.matchMedia(MOBILE_MEDIA_QUERY);
26
+
27
+ this.onToggleClick = this.handleToggleClick.bind(this);
28
+ this.onMediaQueryChange = this.syncViewportState.bind(this);
29
+
30
+ (this.element as any).ODS_Footer = this;
31
+
32
+ this.init();
33
+ return this;
34
+ }
35
+
36
+ static getInstance(el: HTMLElement): Footer | null {
37
+ return el && (el as any).ODS_Footer ? (el as any).ODS_Footer : null;
38
+ }
39
+
40
+ init(): void {
41
+ this.sections = Array.from(
42
+ this.element.querySelectorAll(
43
+ `[data-footer-section].${CLASS_MENU_SECTION}`,
44
+ ),
45
+ ).map((section) => {
46
+ const toggle = section.querySelector(
47
+ `.${CLASS_ACCORDION_TOGGLE}`,
48
+ ) as HTMLButtonElement | null;
49
+ const panelId = toggle?.getAttribute("aria-controls");
50
+ const panel = panelId
51
+ ? (this.element.querySelector(`#${panelId}`) as HTMLElement | null)
52
+ : null;
53
+
54
+ if (toggle) {
55
+ toggle.addEventListener("click", this.onToggleClick);
56
+ }
57
+
58
+ return {
59
+ element: section as HTMLElement,
60
+ toggle,
61
+ panel,
62
+ };
63
+ });
64
+
65
+ this.mediaQueryList.addEventListener("change", this.onMediaQueryChange);
66
+
67
+ this.syncViewportState();
68
+ }
69
+
70
+ private setSectionState(section: FooterSection, isExpanded: boolean): void {
71
+ if (!section.toggle || !section.panel) return;
72
+
73
+ section.toggle.setAttribute("aria-expanded", String(isExpanded));
74
+ section.panel.hidden = !isExpanded;
75
+ }
76
+
77
+ private syncViewportState(): void {
78
+ const isMobile = this.mediaQueryList.matches;
79
+
80
+ this.sections.forEach((section, index) => {
81
+ if (!section.toggle || !section.panel) return;
82
+
83
+ if (!isMobile) {
84
+ this.setSectionState(section, true);
85
+ section.element.removeAttribute("data-footer-mobile-initialized");
86
+ return;
87
+ }
88
+
89
+ const isInitialized =
90
+ section.element.getAttribute("data-footer-mobile-initialized") ===
91
+ "true";
92
+
93
+ if (!isInitialized) {
94
+ this.setSectionState(section, index === 0);
95
+ section.element.setAttribute("data-footer-mobile-initialized", "true");
96
+ return;
97
+ }
98
+
99
+ const isExpanded =
100
+ section.toggle.getAttribute("aria-expanded") === "true";
101
+ this.setSectionState(section, isExpanded);
102
+ });
103
+ }
104
+
105
+ private handleToggleClick(event: Event): void {
106
+ if (!this.mediaQueryList.matches) return;
107
+
108
+ const toggle = event.currentTarget as HTMLButtonElement;
109
+ const section = this.sections.find((item) => item.toggle === toggle);
110
+
111
+ if (!section || !section.toggle) return;
112
+
113
+ const isExpanded = section.toggle.getAttribute("aria-expanded") === "true";
114
+ this.setSectionState(section, !isExpanded);
115
+ }
116
+
117
+ destroy(): void {
118
+ this.sections.forEach((section) => {
119
+ if (!section.toggle) return;
120
+ section.toggle.removeEventListener("click", this.onToggleClick);
121
+ });
122
+
123
+ this.mediaQueryList.removeEventListener("change", this.onMediaQueryChange);
124
+ }
125
+
126
+ update(): void {
127
+ this.destroy();
128
+ this.init();
129
+ }
130
+ }
@@ -1,17 +1,27 @@
1
1
  import React from "react";
2
2
  import cx from "classnames";
3
3
 
4
- import footerData, { socials } from "./data";
4
+ import footerData, { apps, socials } from "./data";
5
5
  import {
6
+ CLASS_ACCORDION_ICON,
7
+ CLASS_ACCORDION_TOGGLE,
8
+ CLASS_BAR,
6
9
  CLASS_BOTTOM,
7
10
  CLASS_BOTTOM_COPYRIGHT,
8
11
  CLASS_CONTAINER,
9
- CLASS_GRID,
12
+ CLASS_DIVIDER,
13
+ CLASS_DIVIDER_HIDE_XS_DOWN,
10
14
  CLASS_LIST,
15
+ CLASS_LIST_BREAK_SM_DOWN,
11
16
  CLASS_LIST_INLINE,
17
+ CLASS_LIST_ITEM_LEAD,
18
+ CLASS_LIST_STACK_SM_DOWN,
19
+ CLASS_LIST_SPACE_SMALL,
12
20
  CLASS_LOGO,
21
+ CLASS_MB_LARGE,
22
+ CLASS_MENU,
23
+ CLASS_MENU_SECTION,
13
24
  CLASS_ROOT,
14
- CLASS_SOCIALS,
15
25
  CLASS_TITLE,
16
26
  } from "./constants";
17
27
  import { SocialButton } from "../SocialButton";
@@ -23,56 +33,113 @@ interface FooterProps extends React.HTMLAttributes<HTMLDivElement> {
23
33
  }
24
34
 
25
35
  const Footer: React.FC<FooterProps> = ({ className, data = footerData }) => {
36
+ const accordionIdPrefix = `footer-${React.useId().replace(/:/g, "")}`;
37
+
26
38
  return (
27
- <footer className={cx(CLASS_ROOT, className)}>
39
+ <footer className={cx(CLASS_ROOT, className)} data-footer>
28
40
  <div className={CLASS_CONTAINER}>
29
- <a href="/" aria-label="Odkaz na úvodnú stránku" className={CLASS_LOGO}>
30
- <svg
31
- width="48"
32
- height="48"
33
- viewBox="0 0 48 48"
34
- fill="none"
35
- xmlns="http://www.w3.org/2000/svg"
41
+ <div className={cx(CLASS_BAR, CLASS_MB_LARGE)}>
42
+ <a
43
+ href="/"
44
+ aria-label="Odkaz na úvodnú stránku"
45
+ className={CLASS_LOGO}
46
+ >
47
+ <svg
48
+ width="48"
49
+ height="48"
50
+ viewBox="0 0 48 48"
51
+ fill="none"
52
+ xmlns="http://www.w3.org/2000/svg"
53
+ >
54
+ <path d="M48 0H0V48H48V0Z" fill="black" />
55
+ <path
56
+ fillRule="evenodd"
57
+ clipRule="evenodd"
58
+ d="M48 48H0V0H48V48Z"
59
+ fill="#FF7900"
60
+ />
61
+ <path
62
+ d="M5.9078 44.1298C4.1702 44.1298 2.60156 43.0229 2.60156 40.6047C2.60156 38.1855 4.16924 37.0796 5.9078 37.0796C7.6454 37.0796 9.21116 38.1865 9.21116 40.6047C9.21116 43.0229 7.64636 44.1298 5.9078 44.1298ZM5.9078 38.5657C4.59356 38.5657 4.35356 39.7494 4.35356 40.6047C4.35356 41.4591 4.59356 42.6457 5.9078 42.6457C7.22204 42.6457 7.46204 41.4601 7.46204 40.6047C7.46204 39.7503 7.22204 38.5657 5.9078 38.5657Z"
63
+ fill="white"
64
+ />
65
+ <path
66
+ fillRule="evenodd"
67
+ clipRule="evenodd"
68
+ d="M10.418 37.2559H12.0912V38.0412C12.41 37.6179 13.1952 37.1196 13.9316 37.1196C14.0016 37.1196 14.088 37.1196 14.16 37.1283V38.7795H14.0736C13.3095 38.7795 12.4733 38.8985 12.2026 39.4927V43.9356H10.418V37.2559Z"
69
+ fill="white"
70
+ />
71
+ <path
72
+ d="M18.831 43.3547C18.1777 43.7913 17.4087 44.0223 16.623 44.0181C15.375 44.0181 14.6367 43.1887 14.6367 42.0751C14.6367 40.5794 16.0114 39.7903 18.8444 39.4706V39.0981C18.8444 38.6133 18.4767 38.3291 17.7961 38.3291C17.1164 38.3291 16.5634 38.5989 16.165 39.0971L14.9794 38.4223C15.6015 37.5506 16.55 37.1147 17.8239 37.1147C19.5673 37.1147 20.5455 37.8703 20.5455 39.0981C20.5455 39.0981 20.5436 43.9269 20.5455 43.9394H18.9865L18.831 43.3547ZM16.3686 41.9474C16.3686 42.3967 16.6498 42.8162 17.1606 42.8162C17.7126 42.8162 18.2511 42.5877 18.7887 42.1125V40.5304C17.1471 40.733 16.3686 41.1573 16.3686 41.9474Z"
73
+ fill="white"
74
+ />
75
+ <path
76
+ fillRule="evenodd"
77
+ clipRule="evenodd"
78
+ d="M21.9336 37.4342L23.3899 37.2336L23.5512 38.0256C24.371 37.4246 25.0229 37.1059 25.8437 37.1059C27.2184 37.1059 27.9288 37.8355 27.9288 39.2793V43.9296H26.1691V39.5846C26.1691 38.7667 25.9579 38.399 25.3214 38.399C24.7973 38.399 24.2731 38.6361 23.6741 39.1392V43.9296H21.9336V37.4342Z"
79
+ fill="white"
80
+ />
81
+ <path
82
+ d="M32.2317 42.5003C33.5642 42.4907 33.6784 41.1323 33.6784 40.2491C33.6784 39.2008 33.1677 38.355 32.2192 38.355C31.5885 38.355 30.8819 38.8158 30.8819 40.3221C30.8819 41.1438 30.9405 42.5118 32.2317 42.5003ZM35.3671 37.2328V43.5582C35.3671 44.6718 35.2787 46.515 32.0983 46.5314C30.785 46.541 29.5658 46.013 29.3239 44.8677L31.0567 44.5874C31.1296 44.9166 31.3322 45.2421 32.3152 45.2421C33.2263 45.2421 33.664 44.8072 33.664 43.7637V42.9851L33.641 42.9611C33.3616 43.4622 32.9373 43.9355 31.9168 43.9355C30.3578 43.9355 29.1328 42.8546 29.1328 40.5966C29.1328 38.3627 30.3981 37.1118 31.817 37.109C33.1514 37.107 33.6429 37.7147 33.76 38.0334L33.736 38.0315L33.8829 37.2328H35.3671ZM39.5709 44.0584C37.6029 44.0584 36.4279 42.7912 36.4279 40.5928C36.4279 38.3704 37.6163 37.1051 39.5431 37.1051C41.4698 37.1051 42.616 38.3339 42.616 40.5045C42.616 40.6216 42.6016 40.7349 42.6016 40.8501H38.1539C38.1664 42.1173 38.6925 42.7557 39.7274 42.7557C40.3907 42.7557 40.8314 42.4869 41.2442 41.9003L42.5306 42.6136C41.9642 43.5611 40.9427 44.0584 39.5709 44.0584ZM40.8899 39.7096C40.8899 38.8178 40.3773 38.2917 39.5431 38.2917C38.7482 38.2917 38.2519 38.8062 38.1827 39.7106L40.8899 39.7096ZM45.6256 34.7214L44.9776 36.3966L44.3191 34.7214H43.7546V36.9294H44.1203V35.0872H44.1299L44.8547 36.9304H45.088L45.808 35.0872H45.8195V36.9304H46.1824V34.7224L45.6256 34.7214ZM41.56 34.7214V35.0152H42.2503V36.9304H42.6151V35.0152H43.3072V34.7214H41.56Z"
83
+ fill="white"
84
+ />
85
+ </svg>
86
+ </a>
87
+ <ul
88
+ className={cx(
89
+ CLASS_LIST,
90
+ CLASS_LIST_INLINE,
91
+ CLASS_LIST_SPACE_SMALL,
92
+ )}
36
93
  >
37
- <path d="M48 0H0V48H48V0Z" fill="black" />
38
- <path
39
- fillRule="evenodd"
40
- clipRule="evenodd"
41
- d="M48 48H0V0H48V48Z"
42
- fill="#FF7900"
43
- />
44
- <path
45
- d="M5.9078 44.1298C4.1702 44.1298 2.60156 43.0229 2.60156 40.6047C2.60156 38.1855 4.16924 37.0796 5.9078 37.0796C7.6454 37.0796 9.21116 38.1865 9.21116 40.6047C9.21116 43.0229 7.64636 44.1298 5.9078 44.1298ZM5.9078 38.5657C4.59356 38.5657 4.35356 39.7494 4.35356 40.6047C4.35356 41.4591 4.59356 42.6457 5.9078 42.6457C7.22204 42.6457 7.46204 41.4601 7.46204 40.6047C7.46204 39.7503 7.22204 38.5657 5.9078 38.5657Z"
46
- fill="white"
47
- />
48
- <path
49
- fillRule="evenodd"
50
- clipRule="evenodd"
51
- d="M10.418 37.2559H12.0912V38.0412C12.41 37.6179 13.1952 37.1196 13.9316 37.1196C14.0016 37.1196 14.088 37.1196 14.16 37.1283V38.7795H14.0736C13.3095 38.7795 12.4733 38.8985 12.2026 39.4927V43.9356H10.418V37.2559Z"
52
- fill="white"
53
- />
54
- <path
55
- d="M18.831 43.3547C18.1777 43.7913 17.4087 44.0223 16.623 44.0181C15.375 44.0181 14.6367 43.1887 14.6367 42.0751C14.6367 40.5794 16.0114 39.7903 18.8444 39.4706V39.0981C18.8444 38.6133 18.4767 38.3291 17.7961 38.3291C17.1164 38.3291 16.5634 38.5989 16.165 39.0971L14.9794 38.4223C15.6015 37.5506 16.55 37.1147 17.8239 37.1147C19.5673 37.1147 20.5455 37.8703 20.5455 39.0981C20.5455 39.0981 20.5436 43.9269 20.5455 43.9394H18.9865L18.831 43.3547ZM16.3686 41.9474C16.3686 42.3967 16.6498 42.8162 17.1606 42.8162C17.7126 42.8162 18.2511 42.5877 18.7887 42.1125V40.5304C17.1471 40.733 16.3686 41.1573 16.3686 41.9474Z"
56
- fill="white"
57
- />
58
- <path
59
- fillRule="evenodd"
60
- clipRule="evenodd"
61
- d="M21.9336 37.4342L23.3899 37.2336L23.5512 38.0256C24.371 37.4246 25.0229 37.1059 25.8437 37.1059C27.2184 37.1059 27.9288 37.8355 27.9288 39.2793V43.9296H26.1691V39.5846C26.1691 38.7667 25.9579 38.399 25.3214 38.399C24.7973 38.399 24.2731 38.6361 23.6741 39.1392V43.9296H21.9336V37.4342Z"
62
- fill="white"
63
- />
64
- <path
65
- d="M32.2317 42.5003C33.5642 42.4907 33.6784 41.1323 33.6784 40.2491C33.6784 39.2008 33.1677 38.355 32.2192 38.355C31.5885 38.355 30.8819 38.8158 30.8819 40.3221C30.8819 41.1438 30.9405 42.5118 32.2317 42.5003ZM35.3671 37.2328V43.5582C35.3671 44.6718 35.2787 46.515 32.0983 46.5314C30.785 46.541 29.5658 46.013 29.3239 44.8677L31.0567 44.5874C31.1296 44.9166 31.3322 45.2421 32.3152 45.2421C33.2263 45.2421 33.664 44.8072 33.664 43.7637V42.9851L33.641 42.9611C33.3616 43.4622 32.9373 43.9355 31.9168 43.9355C30.3578 43.9355 29.1328 42.8546 29.1328 40.5966C29.1328 38.3627 30.3981 37.1118 31.817 37.109C33.1514 37.107 33.6429 37.7147 33.76 38.0334L33.736 38.0315L33.8829 37.2328H35.3671ZM39.5709 44.0584C37.6029 44.0584 36.4279 42.7912 36.4279 40.5928C36.4279 38.3704 37.6163 37.1051 39.5431 37.1051C41.4698 37.1051 42.616 38.3339 42.616 40.5045C42.616 40.6216 42.6016 40.7349 42.6016 40.8501H38.1539C38.1664 42.1173 38.6925 42.7557 39.7274 42.7557C40.3907 42.7557 40.8314 42.4869 41.2442 41.9003L42.5306 42.6136C41.9642 43.5611 40.9427 44.0584 39.5709 44.0584ZM40.8899 39.7096C40.8899 38.8178 40.3773 38.2917 39.5431 38.2917C38.7482 38.2917 38.2519 38.8062 38.1827 39.7106L40.8899 39.7096ZM45.6256 34.7214L44.9776 36.3966L44.3191 34.7214H43.7546V36.9294H44.1203V35.0872H44.1299L44.8547 36.9304H45.088L45.808 35.0872H45.8195V36.9304H46.1824V34.7224L45.6256 34.7214ZM41.56 34.7214V35.0152H42.2503V36.9304H42.6151V35.0152H43.3072V34.7214H41.56Z"
66
- fill="white"
67
- />
68
- </svg>
69
- </a>
94
+ <li className={CLASS_LIST_ITEM_LEAD}>Sledujte nás</li>
95
+ <li className={CLASS_LIST_BREAK_SM_DOWN} aria-hidden="true" />
96
+ {socials.map((social, index) => (
97
+ <li key={`footer-social-link-${index}`}>
98
+ <SocialButton
99
+ name={social.name}
100
+ href={social.url}
101
+ aria-label={social.label}
102
+ />
103
+ </li>
104
+ ))}
105
+ </ul>
106
+ </div>
70
107
 
71
- <div className={CLASS_GRID}>
108
+ <div className={CLASS_MENU}>
72
109
  {data.columns.map((column, index) => (
73
- <div key={`footer-column-${index}`}>
74
- <p className={CLASS_TITLE}>{column.title}</p>
75
- <ul className={CLASS_LIST}>
110
+ // id pairs keep button/panel relationship unique when multiple footers exist on one page
111
+ <div
112
+ className={CLASS_MENU_SECTION}
113
+ data-footer-section
114
+ key={`footer-column-${index}`}
115
+ >
116
+ <button
117
+ type="button"
118
+ className={CLASS_ACCORDION_TOGGLE}
119
+ data-footer-toggle
120
+ id={`${accordionIdPrefix}-footer-section-toggle-${index}`}
121
+ aria-expanded={index === 0 ? "true" : "false"}
122
+ aria-controls={`${accordionIdPrefix}-footer-section-panel-${index}`}
123
+ >
124
+ <span className={CLASS_TITLE}>{column.title}</span>
125
+ <span className={CLASS_ACCORDION_ICON} aria-hidden="true">
126
+ <svg
127
+ width="24"
128
+ height="24"
129
+ viewBox="0 0 80 80"
130
+ xmlns="http://www.w3.org/2000/svg"
131
+ >
132
+ <path
133
+ d="M66.666 33.333 40 60 13.333 33.333 20 26.667l20 20 20-20 6.666 6.666Z"
134
+ fill="currentColor"
135
+ />
136
+ </svg>
137
+ </span>
138
+ </button>
139
+ <ul
140
+ className={CLASS_LIST}
141
+ id={`${accordionIdPrefix}-footer-section-panel-${index}`}
142
+ >
76
143
  {column.links.map((link, linkIndex) => (
77
144
  <li
78
145
  className="mb-small"
@@ -86,18 +153,25 @@ const Footer: React.FC<FooterProps> = ({ className, data = footerData }) => {
86
153
  ))}
87
154
  </div>
88
155
 
89
- <hr />
156
+ <hr className={cx(CLASS_DIVIDER, CLASS_DIVIDER_HIDE_XS_DOWN)} />
90
157
 
91
- <div className={CLASS_SOCIALS}>
92
- <ul className={cx(CLASS_LIST, CLASS_LIST_INLINE)}>
93
- <li>Sledujte nás</li>
94
- {socials.map((social, index) => (
95
- <li className="mr-small" key={`footer-social-link-${index}`}>
96
- <SocialButton
97
- name={social.name}
98
- href={social.url}
99
- aria-label={social.label}
100
- />
158
+ <div className={CLASS_BAR}>
159
+ <ul
160
+ className={cx(
161
+ CLASS_LIST,
162
+ CLASS_LIST_INLINE,
163
+ CLASS_LIST_SPACE_SMALL,
164
+ )}
165
+ >
166
+ <li className={CLASS_LIST_ITEM_LEAD}>
167
+ Stiahnite si aplikáciu Môj Orange
168
+ </li>
169
+ <li className={CLASS_LIST_BREAK_SM_DOWN} aria-hidden="true" />
170
+ {apps.map((app, index) => (
171
+ <li key={`footer-app-link-${index}`}>
172
+ <a href={app.url} target="_blank" rel="noopener noreferrer">
173
+ <img src={app.src} alt={app.label} />
174
+ </a>
101
175
  </li>
102
176
  ))}
103
177
  </ul>
@@ -110,12 +184,18 @@ const Footer: React.FC<FooterProps> = ({ className, data = footerData }) => {
110
184
  </a>
111
185
  </div>
112
186
 
113
- <hr />
187
+ <hr className={CLASS_DIVIDER} />
114
188
 
115
189
  <div className={CLASS_BOTTOM}>
116
- <ul className={cx(CLASS_LIST, CLASS_LIST_INLINE)}>
190
+ <ul
191
+ className={cx(
192
+ CLASS_LIST,
193
+ CLASS_LIST_INLINE,
194
+ CLASS_LIST_STACK_SM_DOWN,
195
+ )}
196
+ >
117
197
  {data.bottomLinks?.map((link, index) => (
118
- <li className="mr-small" key={`footer-legal-link-${index}`}>
198
+ <li key={`footer-legal-link-${index}`}>
119
199
  <a href={link.url}>{link.label}</a>
120
200
  </li>
121
201
  ))}
@@ -1,11 +1,21 @@
1
1
  export const CLASS_ROOT = "footer";
2
2
 
3
3
  export const CLASS_CONTAINER = `${CLASS_ROOT}__container`;
4
- export const CLASS_GRID = `${CLASS_ROOT}__grid`;
5
4
  export const CLASS_LOGO = `${CLASS_ROOT}__logo`;
6
5
  export const CLASS_TITLE = `${CLASS_ROOT}__title`;
7
6
  export const CLASS_LIST = `${CLASS_ROOT}__list`;
7
+ export const CLASS_LIST_SPACE_SMALL = `${CLASS_ROOT}__list--space-small`;
8
8
  export const CLASS_LIST_INLINE = `${CLASS_ROOT}__list--inline`;
9
- export const CLASS_SOCIALS = `${CLASS_ROOT}__socials`;
9
+ export const CLASS_LIST_STACK_SM_DOWN = `${CLASS_ROOT}__list--stack-sm-down`;
10
+ export const CLASS_LIST_BREAK_SM_DOWN = `${CLASS_ROOT}__list-break--sm-down`;
11
+ export const CLASS_LIST_ITEM_LEAD = `${CLASS_ROOT}__list-item--lead`;
10
12
  export const CLASS_BOTTOM = `${CLASS_ROOT}__bottom`;
11
13
  export const CLASS_BOTTOM_COPYRIGHT = `${CLASS_ROOT}__bottom-copyright`;
14
+ export const CLASS_MENU = `${CLASS_ROOT}__menu`;
15
+ export const CLASS_MENU_SECTION = `${CLASS_ROOT}__menu-section`;
16
+ export const CLASS_BAR = `${CLASS_ROOT}__bar`;
17
+ export const CLASS_MB_LARGE = `${CLASS_ROOT}--mb-xlarge`;
18
+ export const CLASS_ACCORDION_TOGGLE = `${CLASS_ROOT}__accordion-toggle`;
19
+ export const CLASS_ACCORDION_ICON = `${CLASS_ROOT}__accordion-icon`;
20
+ export const CLASS_DIVIDER = `${CLASS_ROOT}__divider`;
21
+ export const CLASS_DIVIDER_HIDE_XS_DOWN = `${CLASS_DIVIDER}--hide-xs-down`;
@@ -95,4 +95,17 @@ export const socials: Array<{ name: SocialName; url: string; label: string }> =
95
95
  },
96
96
  ];
97
97
 
98
+ export const apps = [
99
+ {
100
+ label: "App Store",
101
+ src: "/appstore.svg",
102
+ url: "https://apps.apple.com/sk/app/m%C3%B4j-orange/id519421813",
103
+ },
104
+ {
105
+ label: "Google Play",
106
+ src: "/googleplay.svg",
107
+ url: "https://play.google.com/store/apps/details?id=sk.orange.android.orangego",
108
+ },
109
+ ];
110
+
98
111
  export default data;