ublo-lib 1.2.4 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/common/hooks/use-in-view.js +40 -37
- package/es/esf/components/magic-box/services/api.js +1 -1
- package/es/esf/components/period-picker/controls.js +47 -0
- package/es/esf/components/period-picker/controls.module.css +36 -0
- package/es/esf/components/period-picker/date-display.js +67 -0
- package/es/esf/components/period-picker/date-display.module.css +18 -0
- package/es/esf/components/period-picker/days.js +112 -0
- package/es/esf/components/period-picker/days.module.css +113 -0
- package/es/esf/components/period-picker/extended-stay-picker.js +61 -0
- package/es/esf/components/period-picker/extended-stay-picker.module.css +50 -0
- package/es/esf/components/period-picker/index.js +4 -0
- package/es/esf/components/period-picker/messages.js +39 -0
- package/es/esf/components/period-picker/period-picker.js +202 -0
- package/es/esf/components/period-picker/period-picker.module.css +160 -0
- package/es/esf/components/period-picker/popup.js +42 -0
- package/es/esf/components/period-picker/popup.module.css +21 -0
- package/es/esf/components/period-picker/services/dates.js +101 -0
- package/es/esf/components/period-picker/services/events.js +7 -0
- package/es/esf/components/period-picker/services/motions.js +38 -0
- package/es/esf/components/period-picker/services/sections.js +24 -0
- package/es/esf/components/period-picker/services/stay.js +27 -0
- package/es/esf/components/period-picker/services/utils.js +0 -0
- package/es/esf/components/period-picker/title.js +33 -0
- package/es/esf/components/period-picker/title.module.css +6 -0
- package/es/esf/components/period-picker/warning.js +59 -0
- package/es/esf/components/period-picker/warning.module.css +53 -0
- package/es/esf/components/period-picker/weeks.js +120 -0
- package/es/esf/components/period-picker/weeks.module.css +194 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as React from "react";
|
|
2
2
|
|
|
3
3
|
const ratio = steps => Array.from({
|
|
4
4
|
length: steps + 1
|
|
@@ -6,13 +6,14 @@ const ratio = steps => Array.from({
|
|
|
6
6
|
|
|
7
7
|
const buildClass = className => `${className}--in-view`;
|
|
8
8
|
|
|
9
|
+
const options = {
|
|
10
|
+
rootMargin: "0px",
|
|
11
|
+
threshold: ratio(100)
|
|
12
|
+
};
|
|
13
|
+
|
|
9
14
|
const useInView = (ref, cmsMode, targetedClass, repeat = true, intersectionValue = 0.2) => {
|
|
10
|
-
const [compatible, setCompatible] = useState(false);
|
|
11
|
-
const
|
|
12
|
-
rootMargin: "0px",
|
|
13
|
-
threshold: ratio(100)
|
|
14
|
-
};
|
|
15
|
-
const callback = useCallback(entries => {
|
|
15
|
+
const [compatible, setCompatible] = React.useState(false);
|
|
16
|
+
const callback = React.useCallback(entries => {
|
|
16
17
|
entries.forEach(entry => {
|
|
17
18
|
const {
|
|
18
19
|
intersectionRatio,
|
|
@@ -32,42 +33,44 @@ const useInView = (ref, cmsMode, targetedClass, repeat = true, intersectionValue
|
|
|
32
33
|
}
|
|
33
34
|
});
|
|
34
35
|
}, [intersectionValue, repeat]);
|
|
35
|
-
useEffect(() => {
|
|
36
|
+
React.useEffect(() => {
|
|
36
37
|
setCompatible(typeof IntersectionObserver !== "undefined");
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
}
|
|
38
|
+
const container = ref.current;
|
|
39
|
+
const targets = targetedClass !== undefined ? Array.from(container.querySelectorAll(targetedClass)) : [container];
|
|
53
40
|
|
|
54
|
-
|
|
41
|
+
if (container) {
|
|
42
|
+
if (!cmsMode) {
|
|
43
|
+
if (!compatible) {
|
|
44
|
+
targets.forEach(target => {
|
|
45
|
+
const inViewClass = buildClass(target.classList[0]);
|
|
46
|
+
target.classList.add(inViewClass);
|
|
47
|
+
});
|
|
48
|
+
} else {
|
|
49
|
+
targets.forEach(target => {
|
|
50
|
+
const inViewClass = buildClass(target.classList[0]);
|
|
51
|
+
target.classList.remove(inViewClass);
|
|
52
|
+
});
|
|
53
|
+
const observer = new IntersectionObserver(callback, options);
|
|
55
54
|
|
|
56
|
-
|
|
55
|
+
const observe = () => {
|
|
56
|
+
targets.forEach(target => observer?.observe(target));
|
|
57
|
+
};
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
};
|
|
59
|
+
const unobserve = () => 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
|
+
});
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
observe();
|
|
67
|
+
return () => {
|
|
68
|
+
unobserve();
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
69
72
|
}
|
|
70
|
-
}, [cmsMode, compatible,
|
|
73
|
+
}, [callback, cmsMode, compatible, ref, targetedClass]);
|
|
71
74
|
};
|
|
72
75
|
|
|
73
76
|
export default useInView;
|
|
@@ -23,7 +23,7 @@ export const fetchFilters = async ({
|
|
|
23
23
|
activities
|
|
24
24
|
}) => {
|
|
25
25
|
const selectedActivities = activities ? new Array(activities) : undefined;
|
|
26
|
-
return Fetcher.post(`${velApi}/
|
|
26
|
+
return Fetcher.post(`${velApi}/filters`, {
|
|
27
27
|
lang,
|
|
28
28
|
resort,
|
|
29
29
|
season,
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as Icons from "dt-design-system/es/icons";
|
|
3
|
+
import css from "./controls.module.css";
|
|
4
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
6
|
+
const LEFT = 0;
|
|
7
|
+
const RIGHT = 1;
|
|
8
|
+
|
|
9
|
+
const Controls = ({
|
|
10
|
+
barRef,
|
|
11
|
+
distance
|
|
12
|
+
}) => {
|
|
13
|
+
const scrollTo = direction => () => {
|
|
14
|
+
const bar = barRef.current;
|
|
15
|
+
if (!bar) return;
|
|
16
|
+
const currentScroll = bar.scrollLeft;
|
|
17
|
+
|
|
18
|
+
if (direction === LEFT) {
|
|
19
|
+
bar.scrollLeft = currentScroll - distance;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (direction === RIGHT) {
|
|
23
|
+
bar.scrollLeft = currentScroll + distance;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
return _jsxs("div", {
|
|
28
|
+
className: css.controls,
|
|
29
|
+
children: [_jsx("button", {
|
|
30
|
+
className: css.control,
|
|
31
|
+
onClick: scrollTo(LEFT),
|
|
32
|
+
"aria-label": "Previous",
|
|
33
|
+
children: _jsx(Icons.ChevronsLeft, {
|
|
34
|
+
className: css.icon
|
|
35
|
+
})
|
|
36
|
+
}), _jsx("button", {
|
|
37
|
+
className: css.control,
|
|
38
|
+
onClick: scrollTo(RIGHT),
|
|
39
|
+
"aria-label": "Next",
|
|
40
|
+
children: _jsx(Icons.ChevronsRight, {
|
|
41
|
+
className: css.icon
|
|
42
|
+
})
|
|
43
|
+
})]
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default Controls;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
.controls {
|
|
2
|
+
--control-size: 48px;
|
|
3
|
+
|
|
4
|
+
position: absolute;
|
|
5
|
+
top: calc(100% - var(--control-size) / 3);
|
|
6
|
+
left: 0;
|
|
7
|
+
width: 100%;
|
|
8
|
+
display: flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
justify-content: space-between;
|
|
11
|
+
pointer-events: none;
|
|
12
|
+
touch-action: none;
|
|
13
|
+
}
|
|
14
|
+
@media (min-width: 900px) {
|
|
15
|
+
.controls {
|
|
16
|
+
display: none;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.control {
|
|
21
|
+
flex: 0 0 var(--control-size);
|
|
22
|
+
height: var(--control-size);
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
border-radius: var(--ds-radius-100, 3px);
|
|
27
|
+
cursor: pointer;
|
|
28
|
+
pointer-events: auto;
|
|
29
|
+
touch-action: auto;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.icon {
|
|
33
|
+
width: 24px;
|
|
34
|
+
height: 24px;
|
|
35
|
+
fill: currentColor;
|
|
36
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import classnames from "classnames";
|
|
3
|
+
import { motion } from "framer-motion";
|
|
4
|
+
import { useUbloContext } from "ublo/with-ublo";
|
|
5
|
+
import * as Messages from "./messages";
|
|
6
|
+
import * as Stay from "./services/stay";
|
|
7
|
+
import * as Motions from "./services/motions";
|
|
8
|
+
import css from "./date-display.module.css";
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
|
|
12
|
+
const DateDisplay = ({
|
|
13
|
+
className
|
|
14
|
+
}) => {
|
|
15
|
+
const [stay, setStay] = React.useState();
|
|
16
|
+
const {
|
|
17
|
+
lang,
|
|
18
|
+
metadata
|
|
19
|
+
} = useUbloContext();
|
|
20
|
+
const classes = classnames(css.display, {
|
|
21
|
+
[className]: className
|
|
22
|
+
});
|
|
23
|
+
const getStoredStay = React.useCallback(e => {
|
|
24
|
+
const storedStay = window.sessionStorage.getItem("stay");
|
|
25
|
+
|
|
26
|
+
if (storedStay) {
|
|
27
|
+
const newStay = JSON.parse(storedStay);
|
|
28
|
+
setStay(newStay);
|
|
29
|
+
}
|
|
30
|
+
}, []);
|
|
31
|
+
const updateStay = React.useCallback(e => {
|
|
32
|
+
setStay(e.detail);
|
|
33
|
+
}, []);
|
|
34
|
+
React.useEffect(() => {
|
|
35
|
+
if (!metadata?.disableWeekpicker) {
|
|
36
|
+
getStoredStay();
|
|
37
|
+
window.addEventListener("msem-stay-changed", updateStay);
|
|
38
|
+
return () => {
|
|
39
|
+
window.removeEventListener("msem-stay-changed", updateStay);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}, [getStoredStay, metadata?.disableWeekpicker, updateStay]);
|
|
43
|
+
if (!stay || metadata?.disableWeekpicker) return null;
|
|
44
|
+
const formatedStay = Stay.formatStay(lang, stay);
|
|
45
|
+
return _jsxs("div", {
|
|
46
|
+
className: classes,
|
|
47
|
+
children: [Messages.get(lang, "offers"), " ", _jsx("span", {
|
|
48
|
+
className: css.dates,
|
|
49
|
+
children: formatedStay.split(" ").map((word, index) => _jsx(motion.span, {
|
|
50
|
+
className: css.word,
|
|
51
|
+
transition: {
|
|
52
|
+
delayChildren: index * 0.05,
|
|
53
|
+
staggerChildren: 0.05
|
|
54
|
+
},
|
|
55
|
+
initial: "hidden",
|
|
56
|
+
animate: "visible",
|
|
57
|
+
children: word.split("").map((letter, index) => _jsx(motion.span, {
|
|
58
|
+
className: css.letter,
|
|
59
|
+
variants: Motions.character,
|
|
60
|
+
children: letter
|
|
61
|
+
}, `${letter}-${index}`))
|
|
62
|
+
}, `${word}-${index}`))
|
|
63
|
+
})]
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default DateDisplay;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.display {
|
|
2
|
+
max-width: 620px;
|
|
3
|
+
font-size: 40px;
|
|
4
|
+
font-weight: 700;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.dates {
|
|
8
|
+
color: var(--ds-secondary, var(--ds-blue-400, #4177f6));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.word {
|
|
12
|
+
display: inline-block;
|
|
13
|
+
margin-right: 1rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.letter {
|
|
17
|
+
display: inline-block;
|
|
18
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import classnames from "classnames";
|
|
3
|
+
import { useUbloContext } from "ublo/with-ublo";
|
|
4
|
+
import * as Stay from "./services/stay";
|
|
5
|
+
import * as Dates from "./services/dates";
|
|
6
|
+
import css from "./days.module.css";
|
|
7
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
|
|
10
|
+
const Day = ({
|
|
11
|
+
index,
|
|
12
|
+
day,
|
|
13
|
+
fullStay,
|
|
14
|
+
firstSelected,
|
|
15
|
+
updateSelection
|
|
16
|
+
}) => {
|
|
17
|
+
const {
|
|
18
|
+
lang
|
|
19
|
+
} = useUbloContext();
|
|
20
|
+
const locale = Dates.locales[lang] || Dates.locales.en;
|
|
21
|
+
const isFirstDayOfStay = day.getTime() === fullStay[0]?.getTime();
|
|
22
|
+
const isLastDayOfStay = day.getTime() === fullStay[fullStay.length - 1].getTime();
|
|
23
|
+
const isInStay = fullStay.some(date => date.getTime() === day.getTime());
|
|
24
|
+
const isFirstSelected = day.getTime() === new Date(firstSelected).getTime();
|
|
25
|
+
const dayLabel = new Date(day).toLocaleDateString(locale, {
|
|
26
|
+
weekday: "short"
|
|
27
|
+
});
|
|
28
|
+
const dayNumber = new Date(day).toLocaleDateString(locale, {
|
|
29
|
+
day: "2-digit"
|
|
30
|
+
});
|
|
31
|
+
const prevDay = new Date(day);
|
|
32
|
+
prevDay.setDate(day.getDate() - 1);
|
|
33
|
+
const prevMonth = prevDay.getMonth();
|
|
34
|
+
const currMonth = day.getMonth();
|
|
35
|
+
const hasMonthChanged = prevMonth !== currMonth;
|
|
36
|
+
const showMonth = index === 0 || hasMonthChanged;
|
|
37
|
+
const formatedMonth = day.toLocaleString(locale, {
|
|
38
|
+
month: "short"
|
|
39
|
+
});
|
|
40
|
+
const classes = classnames(css.day, {
|
|
41
|
+
[css.firstSelected]: isFirstSelected,
|
|
42
|
+
[css.dayFirstInStay]: !firstSelected && isFirstDayOfStay,
|
|
43
|
+
[css.dayLastInStay]: !firstSelected && isLastDayOfStay,
|
|
44
|
+
[css.dayBoundary]: !firstSelected && (isFirstDayOfStay || isLastDayOfStay),
|
|
45
|
+
[css.dayInStay]: !firstSelected && isInStay
|
|
46
|
+
});
|
|
47
|
+
return _jsxs("div", {
|
|
48
|
+
className: classes,
|
|
49
|
+
children: [_jsx("div", {
|
|
50
|
+
className: css.weekday,
|
|
51
|
+
children: dayLabel
|
|
52
|
+
}), _jsx("div", {
|
|
53
|
+
className: css.dayBottom,
|
|
54
|
+
children: _jsx("button", {
|
|
55
|
+
className: css.dayNumber,
|
|
56
|
+
onClick: updateSelection(day),
|
|
57
|
+
children: _jsx("span", {
|
|
58
|
+
className: css.dayNumberText,
|
|
59
|
+
children: dayNumber
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
}), showMonth && _jsx("span", {
|
|
63
|
+
className: css.month,
|
|
64
|
+
children: formatedMonth
|
|
65
|
+
})]
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const Days = React.forwardRef(({
|
|
70
|
+
fullStay,
|
|
71
|
+
interval
|
|
72
|
+
}, ref) => {
|
|
73
|
+
const [firstSelected, setFirstSelected] = React.useState();
|
|
74
|
+
|
|
75
|
+
const updateSelection = day => e => {
|
|
76
|
+
e.stopPropagation();
|
|
77
|
+
|
|
78
|
+
if (firstSelected) {
|
|
79
|
+
const isBefore = firstSelected.getTime() > day.getTime();
|
|
80
|
+
Stay.update({
|
|
81
|
+
from: Dates.format(isBefore ? day : firstSelected),
|
|
82
|
+
to: Dates.format(isBefore ? firstSelected : day)
|
|
83
|
+
});
|
|
84
|
+
cancelSelection();
|
|
85
|
+
} else {
|
|
86
|
+
setFirstSelected(day);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const cancelSelection = () => {
|
|
91
|
+
setFirstSelected(undefined);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
React.useEffect(() => {
|
|
95
|
+
window.addEventListener("click", cancelSelection);
|
|
96
|
+
return () => {
|
|
97
|
+
window.removeEventListener("click", cancelSelection);
|
|
98
|
+
};
|
|
99
|
+
}, []);
|
|
100
|
+
return _jsx("div", {
|
|
101
|
+
ref: ref,
|
|
102
|
+
className: css.days,
|
|
103
|
+
children: interval.map((day, i) => _jsx(Day, {
|
|
104
|
+
index: i,
|
|
105
|
+
day: day,
|
|
106
|
+
fullStay: fullStay,
|
|
107
|
+
firstSelected: firstSelected,
|
|
108
|
+
updateSelection: updateSelection
|
|
109
|
+
}, day))
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
export default Days;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
.days {
|
|
2
|
+
width: 100%;
|
|
3
|
+
display: inline-flex;
|
|
4
|
+
align-items: flex-start;
|
|
5
|
+
margin: 0 auto;
|
|
6
|
+
padding: 0 18px 10px 18px;
|
|
7
|
+
overflow: auto;
|
|
8
|
+
scroll-behavior: smooth;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@media (min-width: 1100px) {
|
|
12
|
+
.days {
|
|
13
|
+
padding: 0 26px;
|
|
14
|
+
overflow: visible;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.day {
|
|
19
|
+
--item-size: 48px;
|
|
20
|
+
--padding: 6px;
|
|
21
|
+
|
|
22
|
+
flex: 0 0 var(--item-size);
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
align-items: stretch;
|
|
26
|
+
gap: 4px;
|
|
27
|
+
font-size: 14px;
|
|
28
|
+
font-weight: 700;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.day:first-of-type {
|
|
32
|
+
margin-left: auto;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.day:last-of-type {
|
|
36
|
+
margin-right: auto;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.weekday {
|
|
40
|
+
padding: 0 var(--padding) 10px var(--padding);
|
|
41
|
+
text-align: center;
|
|
42
|
+
border-bottom: 1px solid var(--ds-grey-700, #171e30);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.weekday::first-letter {
|
|
46
|
+
text-transform: uppercase;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.dayBottom {
|
|
50
|
+
width: var(--item-size);
|
|
51
|
+
height: var(--item-size);
|
|
52
|
+
display: flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
justify-content: center;
|
|
55
|
+
margin-top: 10px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.dayFirstInStay .dayBottom {
|
|
59
|
+
border-radius: 50% 0 0 50%;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.dayLastInStay .dayBottom {
|
|
63
|
+
border-radius: 0 50% 50% 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.dayFirstInStay.dayLastInStay .dayBottom {
|
|
67
|
+
border-radius: 50%;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.dayInStay .dayBottom {
|
|
71
|
+
background-color: var(--ds-grey-200, #efefef);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.dayNumber {
|
|
75
|
+
width: 100%;
|
|
76
|
+
height: 100%;
|
|
77
|
+
color: var(--ds-grey-700, #171e30);
|
|
78
|
+
font-weight: 700;
|
|
79
|
+
cursor: pointer;
|
|
80
|
+
transition: color 160ms
|
|
81
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.day:not(.dayBoundary):not(.dayInStay):not(.firstSelected) .dayNumber:hover {
|
|
85
|
+
background-color: var(--ds-grey-100, #f5f5f5);
|
|
86
|
+
border-radius: 50%;
|
|
87
|
+
transition: color 160ms
|
|
88
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0, 0.2, 1)),
|
|
89
|
+
background-color 160ms
|
|
90
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.firstSelected .dayNumber,
|
|
94
|
+
.dayBoundary .dayNumber {
|
|
95
|
+
position: relative;
|
|
96
|
+
border-radius: 50%;
|
|
97
|
+
color: var(--ds-grey-000, #ffffff);
|
|
98
|
+
background-color: var(--ds-secondary, var(--ds-blue-400, #4177f6));
|
|
99
|
+
border-radius: 50%;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.firstSelected .dayNumberText,
|
|
103
|
+
.dayBoundary .dayNumberText {
|
|
104
|
+
position: relative;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.month {
|
|
108
|
+
width: 0;
|
|
109
|
+
margin-top: 8px;
|
|
110
|
+
color: var(--ds-grey-700, #171e30);
|
|
111
|
+
font-size: 11px;
|
|
112
|
+
text-transform: uppercase;
|
|
113
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { AnimatePresence, motion } from "framer-motion";
|
|
3
|
+
import { useUbloContext } from "ublo/with-ublo";
|
|
4
|
+
import Button from "dt-design-system/es/button";
|
|
5
|
+
import Days from "./days";
|
|
6
|
+
import * as Dates from "./services/dates";
|
|
7
|
+
import * as Motions from "./services/motions";
|
|
8
|
+
import * as Messages from "./messages";
|
|
9
|
+
import css from "./extended-stay-picker.module.css";
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
|
+
|
|
13
|
+
const ExtendedStayPicker = ({
|
|
14
|
+
stay,
|
|
15
|
+
extend,
|
|
16
|
+
week,
|
|
17
|
+
endWeek,
|
|
18
|
+
forceSeasonSwitch,
|
|
19
|
+
opened,
|
|
20
|
+
setExtendedOpened
|
|
21
|
+
}) => {
|
|
22
|
+
const barRef = React.useRef();
|
|
23
|
+
const {
|
|
24
|
+
lang
|
|
25
|
+
} = useUbloContext();
|
|
26
|
+
const startDate = Dates.weekToDate(week, endWeek, forceSeasonSwitch);
|
|
27
|
+
const endDate = Dates.weekToDate(week + 1, endWeek, forceSeasonSwitch);
|
|
28
|
+
const fullStay = stay ? Dates.getInterval(new Date(stay.from), new Date(stay.to)) : [];
|
|
29
|
+
const interval = Dates.getInterval(startDate, endDate, extend);
|
|
30
|
+
|
|
31
|
+
const close = () => {
|
|
32
|
+
setExtendedOpened(false);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return _jsx(AnimatePresence, {
|
|
36
|
+
mode: "wait",
|
|
37
|
+
children: opened && _jsxs(motion.div, {
|
|
38
|
+
className: css.picker,
|
|
39
|
+
initial: "hidden",
|
|
40
|
+
animate: "visible",
|
|
41
|
+
exit: "hidden",
|
|
42
|
+
variants: Motions.fading,
|
|
43
|
+
children: [_jsxs("div", {
|
|
44
|
+
className: css.title,
|
|
45
|
+
children: [Messages.get(lang, "extended-title"), _jsx(Button, {
|
|
46
|
+
className: css.button,
|
|
47
|
+
variant: "link",
|
|
48
|
+
onClick: close,
|
|
49
|
+
compact: true,
|
|
50
|
+
children: Messages.get(lang, "update-period")
|
|
51
|
+
})]
|
|
52
|
+
}), _jsx(Days, {
|
|
53
|
+
ref: barRef,
|
|
54
|
+
fullStay: fullStay,
|
|
55
|
+
interval: interval
|
|
56
|
+
})]
|
|
57
|
+
})
|
|
58
|
+
});
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default ExtendedStayPicker;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
.picker {
|
|
2
|
+
position: absolute;
|
|
3
|
+
top: 0;
|
|
4
|
+
left: 0;
|
|
5
|
+
width: 100%;
|
|
6
|
+
height: 100%;
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
gap: 16px;
|
|
11
|
+
padding: 24px 0;
|
|
12
|
+
background-color: var(--ds-grey-000, #ffffff);
|
|
13
|
+
z-index: 1;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.title {
|
|
17
|
+
align-self: center;
|
|
18
|
+
position: relative;
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
gap: 6px;
|
|
24
|
+
padding: 0 20px;
|
|
25
|
+
font-size: 20px;
|
|
26
|
+
text-transform: uppercase;
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
text-align: center;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@media (min-width: 1100px) {
|
|
32
|
+
.picker {
|
|
33
|
+
padding: 46px 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.button {
|
|
38
|
+
font-size: 12px;
|
|
39
|
+
color: var(--ds-secondary, var(--ds-blue-400, #4177f6));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.buttonLabel {
|
|
43
|
+
display: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@media (min-width: 780px) {
|
|
47
|
+
.buttonLabel {
|
|
48
|
+
display: block;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const messages = {
|
|
2
|
+
fr: {
|
|
3
|
+
title: "<b>A quelle période</b> souhaitez-vous venir ?",
|
|
4
|
+
"selected-title": "<b>Vous venez</b> du",
|
|
5
|
+
warn: "Attention, modifier vos dates de séjour entraine la suppression du contenu du panier",
|
|
6
|
+
ok: "Confirmer",
|
|
7
|
+
cancel: "Annuler",
|
|
8
|
+
"update-period": "Choisir une autre plage de dates",
|
|
9
|
+
offers: "Les offres pour la période du",
|
|
10
|
+
to: "au",
|
|
11
|
+
"extended-title": "Vous pouvez ajuster vos dates de séjour"
|
|
12
|
+
},
|
|
13
|
+
en: {
|
|
14
|
+
title: "<span>When</span> are you coming?",
|
|
15
|
+
"selected-title": "<b>You are comming</b> from",
|
|
16
|
+
warn: "Warning, if you change your dates of stay, products in your cart will be removed",
|
|
17
|
+
ok: "Confirm",
|
|
18
|
+
cancel: "Cancel",
|
|
19
|
+
"update-period": "Update your stay",
|
|
20
|
+
offers: "Offers for the period from",
|
|
21
|
+
to: "to",
|
|
22
|
+
"extended-title": "You are coming more precisely"
|
|
23
|
+
},
|
|
24
|
+
nl: {
|
|
25
|
+
title: "<span>Wanneer</span> kom je?",
|
|
26
|
+
"selected-title": "<b>You are comming</b> from",
|
|
27
|
+
warn: "Waarschuwing, als u uw verblijfsdata wijzigt, wordt uw winkelwagentje gewist",
|
|
28
|
+
ok: "Aanpassen",
|
|
29
|
+
cancel: "Annuleren",
|
|
30
|
+
"update-period": "Update je verblijf",
|
|
31
|
+
offers: "Aanbiedingen voor de periode van",
|
|
32
|
+
to: "tot",
|
|
33
|
+
"extended-title": "Je komt precies"
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
export const get = (lang, message) => {
|
|
37
|
+
const locale = !messages[lang] ? "en" : lang;
|
|
38
|
+
return messages[locale][message];
|
|
39
|
+
};
|