ublo-lib 1.0.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 (168) hide show
  1. package/es/common/components/admin-links/admin-links.js +107 -0
  2. package/es/common/components/admin-links/admin-links.module.css +85 -0
  3. package/es/common/components/admin-links/index.js +2 -0
  4. package/es/common/components/analytics.js +46 -0
  5. package/es/common/components/breadcrumb.js +69 -0
  6. package/es/common/components/carousel-zone.js +61 -0
  7. package/es/common/components/carousel.js +365 -0
  8. package/es/common/components/cookie-consent/cookie-consent.js +111 -0
  9. package/es/common/components/cookie-consent/cookie-consent.module.css +103 -0
  10. package/es/common/components/cookie-consent/index.js +3 -0
  11. package/es/common/components/cookie-consent/messages.js +31 -0
  12. package/es/common/components/custom-contact-form/attachment.js +229 -0
  13. package/es/common/components/custom-contact-form/attachment.module.css +211 -0
  14. package/es/common/components/custom-contact-form/custom-contact-form.js +168 -0
  15. package/es/common/components/custom-contact-form/field.js +294 -0
  16. package/es/common/components/custom-contact-form/field.module.css +17 -0
  17. package/es/common/components/custom-contact-form/icons.js +55 -0
  18. package/es/common/components/custom-contact-form/index.js +2 -0
  19. package/es/common/components/custom-contact-form/index.module.css +119 -0
  20. package/es/common/components/custom-contact-form/messages.js +79 -0
  21. package/es/common/components/custom-contact-form/utils.js +132 -0
  22. package/es/common/components/date-picker/calendar.js +246 -0
  23. package/es/common/components/date-picker/calendar.module.css +123 -0
  24. package/es/common/components/date-picker/data.js +96 -0
  25. package/es/common/components/date-picker/date-item.js +127 -0
  26. package/es/common/components/date-picker/date-item.module.css +78 -0
  27. package/es/common/components/date-picker/date-picker.js +119 -0
  28. package/es/common/components/date-picker/date-picker.module.css +111 -0
  29. package/es/common/components/date-picker/helper.js +41 -0
  30. package/es/common/components/date-picker/helper.module.css +81 -0
  31. package/es/common/components/date-picker/index.js +3 -0
  32. package/es/common/components/date-picker/messages.js +34 -0
  33. package/es/common/components/date-picker/modes.js +42 -0
  34. package/es/common/components/date-picker/modes.module.css +90 -0
  35. package/es/common/components/date-picker/month.js +78 -0
  36. package/es/common/components/date-picker/month.module.css +54 -0
  37. package/es/common/components/date-picker/utils.js +121 -0
  38. package/es/common/components/error-404/error-404.js +51 -0
  39. package/es/common/components/error-404/error-404.module.css +55 -0
  40. package/es/common/components/error-404/index.js +2 -0
  41. package/es/common/components/error-404/messages.js +29 -0
  42. package/es/common/components/faq.js +54 -0
  43. package/es/common/components/info-buttons.js +116 -0
  44. package/es/common/components/plausible/hooks/use-plausible.js +33 -0
  45. package/es/common/components/plausible/index.js +7 -0
  46. package/es/common/components/plausible/plausible.js +20 -0
  47. package/es/common/components/plausible/services/callback.js +63 -0
  48. package/es/common/components/plausible/services/load.js +7 -0
  49. package/es/common/components/plausible/services/send-goal.js +10 -0
  50. package/es/common/components/popup.js +90 -0
  51. package/es/common/components/scroll-spy.js +53 -0
  52. package/es/common/components/tabbed-zones.js +110 -0
  53. package/es/common/components/unsupported-browser.js +158 -0
  54. package/es/common/components/video-player/controls.js +125 -0
  55. package/es/common/components/video-player/icons.js +45 -0
  56. package/es/common/components/video-player/index.js +1 -0
  57. package/es/common/components/video-player/player.module.css +151 -0
  58. package/es/common/components/video-player/utils.js +43 -0
  59. package/es/common/components/video-player/video-player.js +111 -0
  60. package/es/common/hooks/use-faq.js +44 -0
  61. package/es/common/hooks/use-in-view.js +73 -0
  62. package/es/common/hooks/use-injected-cms-markup.js +50 -0
  63. package/es/common/hooks/use-packages.js +127 -0
  64. package/es/common/hooks/use-scroll-direction.js +44 -0
  65. package/es/common/hooks/use-stay.js +32 -0
  66. package/es/common/hooks/use-sticky.js +38 -0
  67. package/es/common/hooks/use-tunnel.js +50 -0
  68. package/es/common/hooks/use-window-sizes.js +37 -0
  69. package/es/common/hooks/use-youtube-popup.js +62 -0
  70. package/es/common/hooks/use-zone-sync.js +65 -0
  71. package/es/common/utils/cookies.js +13 -0
  72. package/es/common/utils/copy.js +11 -0
  73. package/es/common/utils/dates.js +8 -0
  74. package/es/common/utils/events.js +25 -0
  75. package/es/common/utils/fetcher.js +37 -0
  76. package/es/common/utils/file-manager.js +14 -0
  77. package/es/common/utils/load-js.js +11 -0
  78. package/es/common/utils/msem-widget.js +16 -0
  79. package/es/common/utils/touch-device.js +1 -0
  80. package/es/common/utils/url-parameters.js +12 -0
  81. package/es/empty.d.ts +4 -0
  82. package/es/empty.d.ts.map +1 -0
  83. package/es/empty.js +6 -0
  84. package/es/esf/components/booking-form/data.js +213 -0
  85. package/es/esf/components/booking-form/field.js +140 -0
  86. package/es/esf/components/booking-form/hooks/use-custom-fields.js +20 -0
  87. package/es/esf/components/booking-form/hooks/use-stay.js +12 -0
  88. package/es/esf/components/booking-form/icons.js +50 -0
  89. package/es/esf/components/booking-form/index.js +78 -0
  90. package/es/esf/components/booking-form/lesson.js +59 -0
  91. package/es/esf/components/booking-form/lessons.js +93 -0
  92. package/es/esf/components/booking-form/messages.js +52 -0
  93. package/es/esf/components/booking-form/personal-data.js +73 -0
  94. package/es/esf/components/booking-form/progress-bar.js +35 -0
  95. package/es/esf/components/booking-form/response.js +42 -0
  96. package/es/esf/components/booking-form/steps.js +81 -0
  97. package/es/esf/components/booking-form/summary.js +138 -0
  98. package/es/esf/components/booking-form/utils.js +72 -0
  99. package/es/esf/components/contact-form/api.js +36 -0
  100. package/es/esf/components/contact-form/contact-form.js +293 -0
  101. package/es/esf/components/contact-form/contact-form.module.css +51 -0
  102. package/es/esf/components/contact-form/data.js +53 -0
  103. package/es/esf/components/contact-form/index.js +2 -0
  104. package/es/esf/components/contact-form/messages.js +75 -0
  105. package/es/esf/components/contact-form/validation.js +63 -0
  106. package/es/esf/components/covid-link/index.js +119 -0
  107. package/es/esf/components/covid-link/index.module.css +108 -0
  108. package/es/esf/components/covid-link/mask-icon.js +17 -0
  109. package/es/esf/components/covid-link/vax-pass-icon.js +34 -0
  110. package/es/esf/components/cp-form.js +65 -0
  111. package/es/esf/components/instructor-suggestions/fetcher.js +17 -0
  112. package/es/esf/components/instructor-suggestions/icons.js +266 -0
  113. package/es/esf/components/instructor-suggestions/index.js +181 -0
  114. package/es/esf/components/instructor-suggestions/loader.js +10 -0
  115. package/es/esf/components/instructor-suggestions/messages.js +16 -0
  116. package/es/esf/components/instructors-book/container.js +18 -0
  117. package/es/esf/components/instructors-book/details.js +120 -0
  118. package/es/esf/components/instructors-book/icons.js +266 -0
  119. package/es/esf/components/instructors-book/index.js +15 -0
  120. package/es/esf/components/instructors-book/link.js +17 -0
  121. package/es/esf/components/instructors-book/list-utils.js +21 -0
  122. package/es/esf/components/instructors-book/list.js +184 -0
  123. package/es/esf/components/instructors-book/loader.js +10 -0
  124. package/es/esf/components/instructors-book/messages.js +44 -0
  125. package/es/esf/components/instructors-book/utils.js +5 -0
  126. package/es/esf/components/levels.js +265 -0
  127. package/es/esf/components/loyal-customers/api.js +24 -0
  128. package/es/esf/components/loyal-customers/components/bin-icon.js +31 -0
  129. package/es/esf/components/loyal-customers/components/bin-icon.module.css +9 -0
  130. package/es/esf/components/loyal-customers/components/customer-form.js +105 -0
  131. package/es/esf/components/loyal-customers/components/customer-form.module.css +40 -0
  132. package/es/esf/components/loyal-customers/components/field.js +119 -0
  133. package/es/esf/components/loyal-customers/components/field.module.css +3 -0
  134. package/es/esf/components/loyal-customers/components/row.js +77 -0
  135. package/es/esf/components/loyal-customers/components/row.module.css +95 -0
  136. package/es/esf/components/loyal-customers/components/rows.js +38 -0
  137. package/es/esf/components/loyal-customers/components/rows.module.css +11 -0
  138. package/es/esf/components/loyal-customers/components/stay.js +37 -0
  139. package/es/esf/components/loyal-customers/components/stay.module.css +18 -0
  140. package/es/esf/components/loyal-customers/components/student-form.js +105 -0
  141. package/es/esf/components/loyal-customers/components/student-form.module.css +68 -0
  142. package/es/esf/components/loyal-customers/components/voucher.js +26 -0
  143. package/es/esf/components/loyal-customers/components/voucher.module.css +7 -0
  144. package/es/esf/components/loyal-customers/content.js +55 -0
  145. package/es/esf/components/loyal-customers/data.js +131 -0
  146. package/es/esf/components/loyal-customers/hooks/use-stored-rows.js +14 -0
  147. package/es/esf/components/loyal-customers/index.js +2 -0
  148. package/es/esf/components/loyal-customers/loyal-customers.js +141 -0
  149. package/es/esf/components/loyal-customers/loyal-customers.module.css +62 -0
  150. package/es/esf/components/loyal-customers/messages.js +59 -0
  151. package/es/esf/components/loyal-customers/utils.js +81 -0
  152. package/es/esf/components/village-maps/icons.js +35 -0
  153. package/es/esf/components/village-maps/index.js +214 -0
  154. package/es/esf/components/village-maps/messages.js +19 -0
  155. package/es/esf/components/village-maps/utils.js +26 -0
  156. package/es/esf/components/week-picker/index.js +244 -0
  157. package/es/esf/components/week-picker/messages.js +36 -0
  158. package/es/esf/components/week-picker/utils.js +65 -0
  159. package/es/esf/components/week-picker/week.js +52 -0
  160. package/es/esf/components/week-picker-2/index.js +283 -0
  161. package/es/esf/components/week-picker-2/messages.js +27 -0
  162. package/es/esf/components/week-picker-2/utils.js +65 -0
  163. package/es/esf/components/week-picker-2/week.js +55 -0
  164. package/es/esf/hooks/use-affiliation.js +26 -0
  165. package/es/esf/hooks/use-booking-links.js +36 -0
  166. package/es/esf/hooks/use-reviews.js +28 -0
  167. package/es/esf/hooks/use-season-products.js +100 -0
  168. package/package.json +51 -0
@@ -0,0 +1,111 @@
1
+ import * as React from "react";
2
+ import classnames from "classnames";
3
+ import ReactPlayer from "react-player/lazy";
4
+ import Controls from "./controls";
5
+ import * as Utils from "./utils";
6
+ import styles from "./player.module.css";
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ import { jsxs as _jsxs } from "react/jsx-runtime";
9
+
10
+ const Player = ({
11
+ url,
12
+ thumb,
13
+ forcePlay,
14
+ hideCustomControls,
15
+ ...props
16
+ }) => {
17
+ const containerRef = React.useRef();
18
+ const playRef = React.useRef();
19
+ const [mounted, setMounted] = React.useState(false);
20
+ const [playing, setPlaying] = React.useState(false);
21
+ const [started, setStarted] = React.useState(false);
22
+ const [progress, setProgress] = React.useState(0);
23
+ const [loadedAmount, setLoadedAmount] = React.useState(0);
24
+ const [newProgress, setNewProgress] = React.useState();
25
+ const [duration, setDuration] = React.useState(0);
26
+ const [currentTime, setCurrentTime] = React.useState(0);
27
+ const [fullScreen, setFullScreen] = React.useState();
28
+ const [muted, setMuted] = React.useState(true);
29
+
30
+ const tick = e => {
31
+ const player = playRef.current;
32
+ const {
33
+ played,
34
+ playedSeconds,
35
+ loaded
36
+ } = e;
37
+ if (!player) return;
38
+ setProgress(played);
39
+ setLoadedAmount(loaded);
40
+ setCurrentTime(playedSeconds);
41
+ setDuration(player.getDuration());
42
+ };
43
+
44
+ const updatePlaying = () => {
45
+ setPlaying(!playing);
46
+ };
47
+
48
+ const startVideo = () => setStarted(true);
49
+
50
+ const endVideo = () => setPlaying(false);
51
+
52
+ React.useEffect(() => {
53
+ if (!mounted) {
54
+ setMounted(true);
55
+ }
56
+ }, [mounted]);
57
+ React.useEffect(() => {
58
+ const player = playRef.current;
59
+ if (!player) return;
60
+ player.seekTo(newProgress);
61
+ }, [newProgress]);
62
+ React.useEffect(() => {
63
+ const container = containerRef.current;
64
+ if (fullScreen === undefined || !container) return;
65
+ fullScreen ? Utils.enterFullScreen(container) : Utils.exitFullScreen();
66
+ }, [fullScreen]);
67
+ React.useEffect(() => {
68
+ setPlaying(forcePlay);
69
+ setStarted(forcePlay);
70
+ }, [forcePlay]);
71
+ const classes = classnames(styles.player, {
72
+ [styles.playerStarted]: started,
73
+ [styles.playerPlaying]: playing
74
+ });
75
+ if (!mounted) return null;
76
+ return _jsxs("div", {
77
+ ref: containerRef,
78
+ className: classes,
79
+ children: [_jsx(ReactPlayer, {
80
+ ref: playRef,
81
+ light: forcePlay ? false : thumb,
82
+ url: url,
83
+ playing: playing,
84
+ muted: muted,
85
+ onClick: updatePlaying,
86
+ onStart: startVideo,
87
+ onProgress: tick,
88
+ onEnded: endVideo,
89
+ progressInterval: 160,
90
+ width: "100%",
91
+ height: "100%",
92
+ className: styles.playerInner,
93
+ ...props
94
+ }), !hideCustomControls && _jsx(Controls, {
95
+ started: started,
96
+ playing: playing,
97
+ updatePlaying: updatePlaying,
98
+ progress: progress,
99
+ loadedAmount: loadedAmount,
100
+ setNewProgress: setNewProgress,
101
+ duration: duration,
102
+ currentTime: currentTime,
103
+ fullScreen: fullScreen,
104
+ setFullScreen: setFullScreen,
105
+ muted: muted,
106
+ setMuted: setMuted
107
+ })]
108
+ });
109
+ };
110
+
111
+ export default React.memo(Player);
@@ -0,0 +1,44 @@
1
+ import * as React from "react";
2
+ import { useUbloContext } from "ublo/with-ublo";
3
+
4
+ const buildClass = className => `${className}--opened`;
5
+
6
+ const useFaq = (selector = ".faq-block", refresh) => {
7
+ const [blocks, setBlocks] = React.useState([]);
8
+ const {
9
+ cmsMode
10
+ } = useUbloContext();
11
+ const clicked = React.useCallback((clickedBlock, blocks) => () => {
12
+ if (cmsMode) return;
13
+ const openedClass = buildClass(clickedBlock.classList[0]);
14
+ blocks.forEach(block => {
15
+ const {
16
+ classList
17
+ } = block;
18
+ block !== clickedBlock || block.classList.contains(openedClass) ? classList.remove(openedClass) : classList.add(openedClass);
19
+ });
20
+ }, [cmsMode]);
21
+ const init = React.useCallback((block, blocks) => {
22
+ block.clicked = clicked(block, blocks);
23
+ block.addEventListener("click", block.clicked);
24
+ }, [clicked]);
25
+ const cleanup = React.useCallback(block => {
26
+ block.removeEventListener("click", block.clicked);
27
+ const openedClass = buildClass(block.classList[0]);
28
+ block.classList.remove(openedClass);
29
+ }, []);
30
+ React.useEffect(() => {
31
+ if (cmsMode === undefined) {
32
+ setTimeout(() => {
33
+ const blocks = Array.from(document.querySelectorAll(selector));
34
+ setBlocks(blocks);
35
+ });
36
+ }
37
+ }, [cmsMode, selector, refresh]);
38
+ React.useEffect(() => {
39
+ blocks.forEach(block => init(block, blocks));
40
+ return () => blocks.forEach(cleanup);
41
+ }, [blocks, cleanup, init]);
42
+ };
43
+
44
+ export default useFaq;
@@ -0,0 +1,73 @@
1
+ import { useEffect, useState, useCallback } from "react";
2
+
3
+ const ratio = steps => Array.from({
4
+ length: steps + 1
5
+ }).map((_, i) => i / steps);
6
+
7
+ const buildClass = className => `${className}--in-view`;
8
+
9
+ const useInView = (ref, cmsMode, targetedClass, repeat = true, intersectionValue = 0.2) => {
10
+ const [compatible, setCompatible] = useState(false);
11
+ const options = {
12
+ rootMargin: "0px",
13
+ threshold: ratio(100)
14
+ };
15
+ const callback = useCallback(entries => {
16
+ entries.forEach(entry => {
17
+ const {
18
+ intersectionRatio,
19
+ target
20
+ } = entry;
21
+ if (!target || !target.classList[0]) return;
22
+ const inViewClass = buildClass(target.classList[0]);
23
+
24
+ if (intersectionRatio !== 0) {
25
+ if (repeat) {
26
+ intersectionRatio <= intersectionValue ? target.classList.remove(inViewClass) : target.classList.add(inViewClass);
27
+ } else {
28
+ if (intersectionRatio >= intersectionValue) {
29
+ target.classList.add(inViewClass);
30
+ }
31
+ }
32
+ }
33
+ });
34
+ }, [intersectionValue, repeat]);
35
+ useEffect(() => {
36
+ setCompatible(typeof IntersectionObserver !== "undefined");
37
+ }, []);
38
+ useEffect(() => {
39
+ if (ref.current === undefined) return;
40
+ const targets = targetedClass !== undefined ? Array.from(ref.current.querySelectorAll(targetedClass)) : [ref.current];
41
+ targets.forEach(target => {
42
+ const inViewClass = buildClass(target.classList[0]);
43
+ target.classList.remove(inViewClass);
44
+ });
45
+
46
+ if (cmsMode !== "editing" && cmsMode !== "info" && cmsMode !== "connected") {
47
+ if (!compatible) {
48
+ return targets.forEach(target => {
49
+ const inViewClass = buildClass(target.classList[0]);
50
+ target.classList.add(inViewClass);
51
+ });
52
+ }
53
+
54
+ const observer = new IntersectionObserver(callback, options);
55
+
56
+ const observe = () => targets.forEach(target => observer?.observe(target));
57
+
58
+ const unobserve = () => {
59
+ targets.forEach(target => {
60
+ if (!target || !target.classList[0]) return;
61
+ const inViewClass = buildClass(target.classList[0]);
62
+ target.classList.remove(inViewClass);
63
+ observer.disconnect();
64
+ });
65
+ };
66
+
67
+ observe();
68
+ return () => unobserve();
69
+ }
70
+ }, [cmsMode, compatible, options, ref, repeat, targetedClass, callback]);
71
+ };
72
+
73
+ export default useInView;
@@ -0,0 +1,50 @@
1
+ import * as React from "react";
2
+ import { useUbloContext } from "ublo/with-ublo";
3
+
4
+ const findAllByKey = (obj, keyToFind) => {
5
+ return Object.entries(obj).reduce((acc, [key, value]) => {
6
+ return key === keyToFind ? acc.concat(value) : typeof value === "object" ? acc.concat(findAllByKey(value, keyToFind)) : acc;
7
+ }, []);
8
+ };
9
+
10
+ const useInjectedCmsMarkup = (entries, isReady) => {
11
+ const {
12
+ cmsMode
13
+ } = useUbloContext();
14
+ const injectCmsMarkup = React.useCallback(async () => {
15
+ await window.Cms.register();
16
+ const formats = window.Cms.sectionClasses;
17
+ entries.forEach(entry => {
18
+ const {
19
+ key,
20
+ value
21
+ } = entry;
22
+ const isArray = Array.isArray(value);
23
+ const targets = findAllByKey(formats, key);
24
+ targets.forEach(target => {
25
+ if (!target?.markup) return;
26
+
27
+ if (isArray) {
28
+ const formatedRows = value.map(v => {
29
+ return {
30
+ name: v,
31
+ markup: `<tr><td>${v}</td></tr>`
32
+ };
33
+ });
34
+ const [anchor] = target.markup;
35
+ target.markup = anchor ? [anchor, ...formatedRows] : formatedRows;
36
+ } else {
37
+ target.markup = value;
38
+ }
39
+ });
40
+ });
41
+ await window.Cms.analyse();
42
+ }, [entries]);
43
+ React.useEffect(() => {
44
+ if (isReady && cmsMode === "connected") {
45
+ injectCmsMarkup();
46
+ }
47
+ }, [cmsMode, injectCmsMarkup, isReady]);
48
+ };
49
+
50
+ export default useInjectedCmsMarkup;
@@ -0,0 +1,127 @@
1
+ import * as React from "react";
2
+ import { useUbloContext } from "ublo/with-ublo";
3
+ import PackagesSelector from "../packages-selector";
4
+ import { loadJS } from "../utils/load-js";
5
+ import { openMseM } from "../utils/msem-widget";
6
+ import { jsx as _jsx } from "react/jsx-runtime";
7
+ const preview = typeof window !== "undefined" && window.location.search.indexOf("preview") !== -1;
8
+
9
+ const loadWigetMseM = () => {
10
+ const widgetUrl = "https://widget.msem.tech/static/js/widget-msem.js";
11
+ return loadJS(widgetUrl, "widget-msem");
12
+ };
13
+
14
+ const buttonStyles = `
15
+ display: table;
16
+ position: absolute;
17
+ left: 50%;
18
+ bottom: calc(100% + 10px);
19
+ margin: 0px auto;
20
+ padding: 15px;
21
+ color: rgb(255, 255, 255);
22
+ font-family: "Open Sans";
23
+ text-transform: uppercase;
24
+ font-weight: 700;
25
+ background-color: var(--cms-primary-color, #004cc2);
26
+ border-radius: 8px;
27
+ cursor: pointer;
28
+ user-select: none;
29
+ outline: none;
30
+ transform: translateX(-50%);
31
+ `;
32
+
33
+ const usePackages = (defaultPopupContent, setPopupContent, cartUrl, selector = "[data-package], .package-lodging__container, .banner-discount") => {
34
+ const {
35
+ cmsMode,
36
+ config
37
+ } = useUbloContext();
38
+ const {
39
+ channel,
40
+ resort
41
+ } = config;
42
+ const onPackageSelection = React.useCallback(link => id => {
43
+ if (!link || !id) return;
44
+ link.setAttribute("data-id", id);
45
+ setPopupContent(defaultPopupContent);
46
+ }, [defaultPopupContent, setPopupContent]);
47
+ const onButtonClick = React.useCallback(e => {
48
+ e.stopPropagation();
49
+ const link = e.target.closest(selector);
50
+ const id = link.getAttribute("data-id");
51
+ setPopupContent({
52
+ title: "Associer un package",
53
+ content: _jsx(PackagesSelector, {
54
+ onSubmit: onPackageSelection(link),
55
+ resort: resort,
56
+ selected: id
57
+ })
58
+ });
59
+ }, [onPackageSelection, resort, selector, setPopupContent]);
60
+ const toggleEditButton = React.useCallback(section => {
61
+ const button = section.querySelector(".package-edit-button");
62
+
63
+ if (cmsMode === "editing" && !button) {
64
+ const newButton = Object.assign(document.createElement("button"), {
65
+ className: "package-edit-button",
66
+ innerHTML: "Associer un package",
67
+ style: buttonStyles
68
+ });
69
+ section.appendChild(newButton);
70
+ newButton.addEventListener("click", onButtonClick);
71
+ } else {
72
+ button && button.removeEventListener("click", onButtonClick);
73
+ button && button.remove();
74
+ }
75
+ }, [cmsMode, onButtonClick]);
76
+ const onPackageClick = React.useCallback(async e => {
77
+ if (cmsMode === "editing") return e.preventDefault();
78
+ const target = e.target.closest(selector);
79
+ const id = target.getAttribute("data-id");
80
+ if (!id || id === "") return;
81
+ e.preventDefault();
82
+ const packageId = parseInt(id);
83
+ const options = {
84
+ resort,
85
+ channel,
86
+ verticalMargin: 100,
87
+ merchant: "",
88
+ packageId,
89
+ catalog: "PUBLIC",
90
+ preview,
91
+ theme: "/theme.css",
92
+ cartUrl
93
+ };
94
+ await openMseM("widgetPackage", options);
95
+ }, [cartUrl, cmsMode, resort, selector]);
96
+ const callback = React.useCallback(e => {
97
+ const {
98
+ section: newSection
99
+ } = e.detail;
100
+ const link = newSection.querySelector(selector);
101
+
102
+ if (link) {
103
+ toggleEditButton(link);
104
+ }
105
+ }, [selector, toggleEditButton]);
106
+ const init = React.useCallback(section => {
107
+ const zone = section.closest(".cms");
108
+ zone.addEventListener("ubloSectionCreated", callback);
109
+ section.addEventListener("click", onPackageClick);
110
+ }, [callback, onPackageClick]);
111
+ const cleanup = React.useCallback(section => {
112
+ const zone = section.closest(".cms");
113
+ zone.removeEventListener("ubloSectionCreated", callback);
114
+ section.removeEventListener("click", onPackageClick);
115
+ }, [callback, onPackageClick]);
116
+ React.useEffect(() => {
117
+ const sections = Array.from(document.querySelectorAll(selector));
118
+ sections.forEach(toggleEditButton);
119
+ sections.forEach(init);
120
+ return () => {
121
+ sections.forEach(cleanup);
122
+ sections.forEach(toggleEditButton);
123
+ };
124
+ }, [cleanup, cmsMode, init, selector, toggleEditButton]);
125
+ };
126
+
127
+ export default usePackages;
@@ -0,0 +1,44 @@
1
+ import { useState, useEffect } from "react";
2
+ const SCROLL_UP = "up";
3
+ const SCROLL_DOWN = "down";
4
+
5
+ const useScrollDirection = ({
6
+ initialDirection,
7
+ thresholdPixels,
8
+ off
9
+ } = {}) => {
10
+ const [scrollDir, setScrollDir] = useState(initialDirection);
11
+ useEffect(() => {
12
+ const threshold = thresholdPixels || 0;
13
+ let lastScrollY = window.pageYOffset;
14
+ let ticking = false;
15
+
16
+ const updateScrollDir = () => {
17
+ const scrollY = window.pageYOffset;
18
+
19
+ if (Math.abs(scrollY - lastScrollY) < threshold) {
20
+ ticking = false;
21
+ return;
22
+ }
23
+
24
+ setScrollDir(scrollY > lastScrollY ? SCROLL_DOWN : SCROLL_UP);
25
+ lastScrollY = scrollY > 0 ? scrollY : 0;
26
+ ticking = false;
27
+ };
28
+
29
+ const onScroll = () => {
30
+ if (!ticking) {
31
+ window.requestAnimationFrame(updateScrollDir);
32
+ ticking = true;
33
+ }
34
+ };
35
+
36
+ !off ? window.addEventListener("scroll", onScroll) : setScrollDir(initialDirection);
37
+ return () => window.removeEventListener("scroll", onScroll);
38
+ }, [initialDirection, thresholdPixels, off]);
39
+ return {
40
+ scrollDir
41
+ };
42
+ };
43
+
44
+ export default useScrollDirection;
@@ -0,0 +1,32 @@
1
+ import { useEffect, useState } from "react";
2
+ import { isValidDate } from "../utils/dates";
3
+
4
+ const useStay = () => {
5
+ const [stay, setStay] = useState();
6
+ useEffect(() => {
7
+ const params = new URLSearchParams(document.location.search);
8
+ const hasAllParams = params.has("from") && params.has("to");
9
+ let stayParams;
10
+
11
+ if (hasAllParams) {
12
+ const from = params.get("from");
13
+ const to = params.get("to");
14
+ const areParamsValid = isValidDate(from) && isValidDate(to);
15
+
16
+ if (areParamsValid) {
17
+ stayParams = {
18
+ from,
19
+ to
20
+ };
21
+ window.sessionStorage.setItem("stay", JSON.stringify(stayParams));
22
+ window.sessionStorage.setItem("Stay", JSON.stringify(stayParams));
23
+ }
24
+ }
25
+
26
+ const storedStay = window.sessionStorage.getItem("stay");
27
+ setStay(stayParams ? stayParams : storedStay ? JSON.parse(storedStay) : undefined);
28
+ }, []);
29
+ return [stay, setStay];
30
+ };
31
+
32
+ export default useStay;
@@ -0,0 +1,38 @@
1
+ import * as React from "react";
2
+ const eventsToBind = {
3
+ document: ["scroll"],
4
+ window: ["resize", "orientationchange"]
5
+ };
6
+
7
+ const useSticky = () => {
8
+ const ref = React.useRef(null);
9
+ const [sticky, setSticky] = React.useState(false);
10
+ const observe = React.useCallback(() => {
11
+ const refPageOffset = ref.current.getBoundingClientRect().top;
12
+ const stickyOffset = parseInt(getComputedStyle(ref.current).top);
13
+ const stickyActive = refPageOffset <= stickyOffset;
14
+ setSticky(stickyActive && !sticky || stickyActive && sticky);
15
+ }, [sticky]);
16
+ const init = React.useCallback(() => {
17
+ Object.keys(eventsToBind).forEach(key => {
18
+ const events = eventsToBind[key];
19
+ const target = window[key];
20
+ events.forEach(event => target.addEventListener(event, observe));
21
+ });
22
+ }, [observe]);
23
+ const cleanup = React.useCallback(() => {
24
+ Object.keys(eventsToBind).forEach(key => {
25
+ const events = eventsToBind[key];
26
+ const target = window[key];
27
+ events.forEach(event => target.removeEventListener(event, observe));
28
+ });
29
+ }, [observe]);
30
+ React.useEffect(() => {
31
+ observe();
32
+ init();
33
+ return () => cleanup();
34
+ }, [cleanup, init, observe]);
35
+ return [ref, sticky];
36
+ };
37
+
38
+ export default useSticky;
@@ -0,0 +1,50 @@
1
+ import { useEffect } from "react";
2
+ import { useUbloContext } from "ublo/with-ublo";
3
+ import * as Plausible from "../plausible";
4
+ import { loadJS } from "../utils/load-js";
5
+ const preview = typeof window !== "undefined" && window.location.search.indexOf("preview") !== -1;
6
+
7
+ const loadWigetMseM = async integration => {
8
+ const source = integration ? "https://widget-integration.msem.tech/static/js/widget-msem.js" : "https://widget.msem.tech/static/js/widget-msem.js";
9
+ await loadJS(source, "widget-msem");
10
+ };
11
+
12
+ const useTunnel = (cart, multipleVillages, channel = "ESF", integration) => {
13
+ const {
14
+ lang,
15
+ breadcrumb,
16
+ config
17
+ } = useUbloContext();
18
+ const {
19
+ path: cartUrl
20
+ } = cart;
21
+ const {
22
+ path: esfUrl
23
+ } = multipleVillages ? breadcrumb?.next : breadcrumb;
24
+ const widgetLang = lang === "fr" ? "fr" : "en";
25
+ useEffect(() => {
26
+ const run = async () => {
27
+ await loadWigetMseM(integration);
28
+
29
+ const open = () => window.MseM.tunnel({
30
+ inside: true,
31
+ resort: config.resort,
32
+ channel,
33
+ groundedTo: "#esfplus_container",
34
+ lang: widgetLang,
35
+ preview,
36
+ cartUrl,
37
+ esfUrl,
38
+ fullWidth: true,
39
+ analytics: Plausible.callback,
40
+ useAnalytics: true
41
+ });
42
+
43
+ window.MseM.onLoad(open);
44
+ };
45
+
46
+ run();
47
+ }, [cartUrl, channel, config.resort, esfUrl, lang, widgetLang]);
48
+ };
49
+
50
+ export default useTunnel;
@@ -0,0 +1,37 @@
1
+ import * as React from "react";
2
+ const DEFAULT_SIZES = {
3
+ width: undefined,
4
+ height: undefined
5
+ };
6
+
7
+ const useWindowSizes = () => {
8
+ const [windowSizes, setWindowSizes] = React.useState(DEFAULT_SIZES);
9
+
10
+ const resized = () => {
11
+ setWindowSizes({
12
+ width: window.innerWidth,
13
+ height: window.innerHeight
14
+ });
15
+ };
16
+
17
+ React.useEffect(() => {
18
+ resized();
19
+ const isCompatible = typeof ResizeObserver !== "undefined";
20
+
21
+ if (isCompatible) {
22
+ const observer = new ResizeObserver(resized);
23
+ observer.observe(document.body);
24
+ return () => {
25
+ observer.unobserve(document.body);
26
+ };
27
+ } else {
28
+ window.addEventListener("resize", resized);
29
+ return () => {
30
+ window.removeEventListener("resize", resized);
31
+ };
32
+ }
33
+ }, []);
34
+ return windowSizes;
35
+ };
36
+
37
+ export default useWindowSizes;
@@ -0,0 +1,62 @@
1
+ import * as React from "react";
2
+ import { useUbloContext } from "ublo/with-ublo";
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+
5
+ const extractYoutubeVideoID = url => {
6
+ const regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
7
+ const match = url.match(regExp);
8
+
9
+ if (match && match[7].length === 11) {
10
+ return match[7];
11
+ } else {
12
+ alert("Could not extract video ID.");
13
+ }
14
+ };
15
+
16
+ const useYoutubePopup = (selector, setPopupContent) => {
17
+ const {
18
+ cmsMode
19
+ } = useUbloContext();
20
+ const openVideo = React.useCallback(e => {
21
+ e.preventDefault();
22
+ const link = e.target.closest("a")?.getAttribute("href")?.trim();
23
+ if (!link) return;
24
+ const code = extractYoutubeVideoID(link);
25
+ const url = `https://www.youtube.com/embed/${code}`;
26
+ setPopupContent({
27
+ content: _jsx("div", {
28
+ style: {
29
+ position: "relative",
30
+ width: 1200,
31
+ maxWidth: "100%",
32
+ paddingTop: "56.25%"
33
+ },
34
+ children: _jsx("iframe", {
35
+ style: {
36
+ position: "absolute",
37
+ top: 0,
38
+ left: 0,
39
+ width: "100%",
40
+ height: "100%"
41
+ },
42
+ src: url,
43
+ frameBorder: "0",
44
+ allowFullScreen: true,
45
+ title: ""
46
+ })
47
+ }),
48
+ popupClassName: "iframe-video"
49
+ });
50
+ }, [setPopupContent]);
51
+ const init = React.useCallback(video => video.addEventListener("click", openVideo), [openVideo]);
52
+ const cleanup = React.useCallback(video => video.removeEventListener("click", openVideo), [openVideo]);
53
+ React.useEffect(() => {
54
+ if (cmsMode !== "editing") {
55
+ const videos = Array.from(document.querySelectorAll(selector));
56
+ videos.forEach(init);
57
+ return () => videos.forEach(cleanup);
58
+ }
59
+ }, [cleanup, cmsMode, init, selector]);
60
+ };
61
+
62
+ export default useYoutubePopup;