ublo-lib 1.0.0 → 1.0.3

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 (45) hide show
  1. package/es/{esf/components/contact-form → common/components/gesco-contact-form}/api.js +0 -0
  2. package/es/{esf/components/contact-form → common/components/gesco-contact-form}/data.js +0 -0
  3. package/es/{esf/components/contact-form/contact-form.js → common/components/gesco-contact-form/gesco-contact-form.js} +0 -0
  4. package/es/{esf/components/contact-form/contact-form.module.css → common/components/gesco-contact-form/gesco-contact-form.module.css} +0 -0
  5. package/es/common/components/gesco-contact-form/index.js +2 -0
  6. package/es/{esf/components/contact-form → common/components/gesco-contact-form}/messages.js +0 -0
  7. package/es/{esf/components/contact-form → common/components/gesco-contact-form}/validation.js +0 -0
  8. package/es/common/components/search-bar/hooks/use-constant.js +15 -0
  9. package/es/common/components/search-bar/hooks/use-debounced-search.js +18 -0
  10. package/es/common/components/search-bar/hooks/use-search.js +3 -0
  11. package/es/common/components/search-bar/icons.js +25 -0
  12. package/es/common/components/search-bar/index.js +2 -0
  13. package/es/common/components/search-bar/messages.js +15 -0
  14. package/es/common/components/search-bar/search-bar.js +207 -0
  15. package/es/common/components/search-bar/utils/fetcher.js +14 -0
  16. package/es/common/components/search-bar/utils/keyboard-keys.js +5 -0
  17. package/es/common/components/search-bar/utils/params.js +8 -0
  18. package/es/esf/hooks/use-booking-links.js +1 -1
  19. package/es/esf/hooks/use-reviews.js +1 -1
  20. package/es/lbm/components/msem-linker/actions.js +65 -0
  21. package/es/lbm/components/msem-linker/actions.module.css +75 -0
  22. package/es/lbm/components/msem-linker/dropdown.js +247 -0
  23. package/es/lbm/components/msem-linker/dropdown.module.css +225 -0
  24. package/es/lbm/components/msem-linker/editor/editor.js +120 -0
  25. package/es/lbm/components/msem-linker/editor/editor.module.css +88 -0
  26. package/es/lbm/components/msem-linker/editor/index.js +1 -0
  27. package/es/lbm/components/msem-linker/editor/lodging.js +417 -0
  28. package/es/lbm/components/msem-linker/editor/lodging.module.css +59 -0
  29. package/es/lbm/components/msem-linker/editor/messages.js +25 -0
  30. package/es/lbm/components/msem-linker/editor/ski-pass.js +362 -0
  31. package/es/lbm/components/msem-linker/editor/ski-pass.module.css +76 -0
  32. package/es/lbm/components/msem-linker/hooks/use-widgets.js +89 -0
  33. package/es/lbm/components/msem-linker/index.js +1 -0
  34. package/es/lbm/components/msem-linker/loader.js +13 -0
  35. package/es/lbm/components/msem-linker/loader.module.css +22 -0
  36. package/es/lbm/components/msem-linker/msem-linker.js +101 -0
  37. package/es/lbm/components/msem-linker/msem-linker.module.css +140 -0
  38. package/es/lbm/components/msem-linker/msem-logo.js +17 -0
  39. package/es/lbm/components/msem-linker/services/api.js +34 -0
  40. package/es/lbm/components/msem-linker/services/config.js +6 -0
  41. package/es/lbm/components/msem-linker/services/dates.js +11 -0
  42. package/es/lbm/components/msem-linker/services/ui.js +89 -0
  43. package/es/lbm/components/msem-linker/services/url-params.js +40 -0
  44. package/package.json +52 -49
  45. package/es/esf/components/contact-form/index.js +0 -2
@@ -0,0 +1,2 @@
1
+ import GescoContactForm from "./gesco-contact-form";
2
+ export default GescoContactForm;
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+
3
+ const useConstant = fn => {
4
+ const ref = React.useRef();
5
+
6
+ if (!ref.current) {
7
+ ref.current = {
8
+ v: fn()
9
+ };
10
+ }
11
+
12
+ return ref.current.v;
13
+ };
14
+
15
+ export default useConstant;
@@ -0,0 +1,18 @@
1
+ import { useState } from "react";
2
+ import debouncePromise from "awesome-debounce-promise";
3
+ import useConstant from "./use-constant";
4
+ import { useAsync } from "react-async-hook";
5
+ export const useDebouncedSearch = searchFunction => {
6
+ const [text, setText] = useState("");
7
+ const debouncedSearchFunction = useConstant(() => debouncePromise(searchFunction, 200));
8
+ const search = useAsync(async () => text.length === 0 ? undefined : debouncedSearchFunction(text), [debouncedSearchFunction, text], {
9
+ setLoading: state => ({ ...state,
10
+ loading: true
11
+ })
12
+ });
13
+ return {
14
+ text,
15
+ setText,
16
+ search
17
+ };
18
+ };
@@ -0,0 +1,3 @@
1
+ import { useDebouncedSearch } from "./use-debounced-search";
2
+ import { fetchResults } from "../utils/fetcher";
3
+ export const useSearch = (ubloApi, site, lang, seo, exclude) => useDebouncedSearch(query => fetchResults(ubloApi, site, lang, query, seo, exclude));
@@ -0,0 +1,25 @@
1
+ import * as React from "react";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+
4
+ const Icon = ({
5
+ width = 24,
6
+ height = 24,
7
+ ...props
8
+ }) => _jsx("svg", {
9
+ viewBox: `0 0 ${width} ${height}`,
10
+ width: width,
11
+ height: height,
12
+ ...props,
13
+ children: props.children
14
+ });
15
+
16
+ export const SearchIcon = props => _jsx(Icon, { ...props,
17
+ children: _jsx("path", {
18
+ d: "M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
19
+ })
20
+ });
21
+ export const LoadIcon = props => _jsx(Icon, { ...props,
22
+ children: _jsx("path", {
23
+ d: "M19 8l-4 4h3c0 3.31-2.69 6-6 6-1.01 0-1.97-.25-2.8-.7l-1.46 1.46C8.97 19.54 10.43 20 12 20c4.42 0 8-3.58 8-8h3l-4-4zM6 12c0-3.31 2.69-6 6-6 1.01 0 1.97.25 2.8.7l1.46-1.46C15.03 4.46 13.57 4 12 4c-4.42 0-8 3.58-8 8H1l4 4 4-4H6z"
24
+ })
25
+ });
@@ -0,0 +1,2 @@
1
+ import SearchBar from "./search-bar";
2
+ export default SearchBar;
@@ -0,0 +1,15 @@
1
+ const locales = {
2
+ fr: {
3
+ "what-are-you-looking-for": "Que recherchez-vous ?",
4
+ "no-results": "Pas de résultats..."
5
+ },
6
+ en: {
7
+ "what-are-you-looking-for": "What are you looking for?",
8
+ "no-results": "No results..."
9
+ }
10
+ };
11
+ export const message = (lang, id, messages = locales) => {
12
+ if (!messages || !messages[lang]) return id;
13
+ const langMessages = messages[lang];
14
+ return langMessages[id] || `??${id}??`;
15
+ };
@@ -0,0 +1,207 @@
1
+ import * as React from "react";
2
+ import { useState, useEffect, useRef } from "react";
3
+ import classnames from "classnames";
4
+ import Link from "ublo/link";
5
+ import { useUbloContext } from "ublo/with-ublo";
6
+ import { SearchIcon, LoadIcon } from "./icons";
7
+ import * as Plausible from "../plausible";
8
+ import { useSearch } from "./hooks/use-search";
9
+ import * as KeyboardKeys from "./utils/keyboard-keys";
10
+ import { message } from "./messages";
11
+ import { jsx as _jsx } from "react/jsx-runtime";
12
+ import { jsxs as _jsxs } from "react/jsx-runtime";
13
+
14
+ const SearchBar = ({
15
+ resultFormatter,
16
+ backdrop,
17
+ OverrideIcons = {},
18
+ messages,
19
+ seo,
20
+ exclude,
21
+ autofocus
22
+ }) => {
23
+ const input = useRef();
24
+ const results = useRef();
25
+ const [state, setState] = useState({
26
+ active: 0,
27
+ selected: undefined
28
+ });
29
+ const {
30
+ lang,
31
+ config
32
+ } = useUbloContext();
33
+ const {
34
+ ubloApi,
35
+ site
36
+ } = config;
37
+ const {
38
+ text,
39
+ setText,
40
+ search
41
+ } = useSearch(ubloApi, site, lang, seo, exclude);
42
+ const DefaultIcons = {
43
+ SearchIcon,
44
+ LoadIcon
45
+ };
46
+ const Icons = Object.assign(DefaultIcons, OverrideIcons);
47
+
48
+ const close = () => setText("");
49
+
50
+ const onChange = e => {
51
+ const userInput = e.currentTarget.value;
52
+ setText(userInput);
53
+ setState({
54
+ active: 0
55
+ });
56
+ };
57
+
58
+ const onKeyDown = e => {
59
+ const allowedKeys = ["ArrowUp", "ArrowDown", "Enter", "Escape"];
60
+ const {
61
+ code,
62
+ keyCode,
63
+ key,
64
+ which
65
+ } = e;
66
+ const isAllowed = allowedKeys.includes(code) || allowedKeys.includes(key);
67
+ if (!isAllowed) return;
68
+ const keyId = keyCode ?? which;
69
+ const up = keyId === KeyboardKeys.ArrowUp;
70
+ const enter = keyId === KeyboardKeys.Enter;
71
+ const escape = keyId === KeyboardKeys.Escape;
72
+
73
+ if (escape) {
74
+ close();
75
+ return;
76
+ }
77
+
78
+ const container = results.current;
79
+ const firstResult = container?.querySelector(".search-bar__result");
80
+ const allResults = Array.from(container?.querySelectorAll(".search-bar__result"));
81
+ const lastResult = allResults[allResults.length - 1];
82
+ const selectedResult = container?.querySelector(".search-bar__result--selected");
83
+
84
+ if (enter && selectedResult) {
85
+ selectedResult.click();
86
+ return;
87
+ }
88
+
89
+ let newSelected;
90
+
91
+ if (!selectedResult) {
92
+ newSelected = up ? lastResult : firstResult;
93
+ } else {
94
+ newSelected = up ? selectedResult.previousElementSibling || lastResult : selectedResult.nextElementSibling || firstResult;
95
+ }
96
+
97
+ allResults.forEach(result => result.classList.remove("search-bar__result--selected"));
98
+ newSelected && newSelected.classList.add("search-bar__result--selected");
99
+ container && newSelected && (container.scrollTop = newSelected.offsetTop - newSelected.clientHeight);
100
+ };
101
+
102
+ const focusInput = () => {
103
+ input.current.focus();
104
+ };
105
+
106
+ const handleKeyPresses = React.useCallback(e => {
107
+ const allowedKeys = ["k"];
108
+ const {
109
+ code,
110
+ ctrlKey,
111
+ key,
112
+ keyCode,
113
+ metaKey,
114
+ which
115
+ } = e;
116
+ const isAllowed = !text && (ctrlKey || metaKey) && (allowedKeys.includes(code) || allowedKeys.includes(key));
117
+ if (!isAllowed) return;
118
+ e.preventDefault();
119
+ const keyId = keyCode ?? which;
120
+
121
+ if (keyId === KeyboardKeys.K && (ctrlKey || metaKey)) {
122
+ focusInput();
123
+ }
124
+ }, []);
125
+ useEffect(() => {
126
+ if (autofocus) focusInput();
127
+ window.addEventListener("keydown", handleKeyPresses);
128
+ return () => {
129
+ window.removeEventListener("keydown", handleKeyPresses);
130
+ };
131
+ }, []);
132
+ const Icon = search?.loading ? Icons.LoadIcon : Icons.SearchIcon;
133
+ const classes = classnames("search-bar", {
134
+ "search-bar--opened": search?.result,
135
+ "search-bar--loading": search?.loading
136
+ });
137
+ return _jsxs("div", {
138
+ className: classes,
139
+ children: [backdrop && _jsx("div", {
140
+ className: "search-bar__backdrop",
141
+ onClick: close
142
+ }), _jsxs("div", {
143
+ className: "search-bar__input-container",
144
+ children: [_jsx("input", {
145
+ ref: input,
146
+ type: "text",
147
+ value: text,
148
+ onChange: onChange,
149
+ onKeyDown: onKeyDown,
150
+ className: "search-bar__input",
151
+ placeholder: message(lang, "what-are-you-looking-for", messages)
152
+ }), _jsx(Icon, {
153
+ className: "search-bar__icon"
154
+ }), _jsxs("div", {
155
+ ref: results,
156
+ className: "search-bar__results",
157
+ children: [search.result?.map((result, i) => {
158
+ const {
159
+ name,
160
+ title,
161
+ page,
162
+ path
163
+ } = result;
164
+ const externalLink = page === undefined;
165
+ const Tag = externalLink ? "a" : Link;
166
+ const resultLabel = typeof resultFormatter === "function" ? resultFormatter({
167
+ lang,
168
+ result,
169
+ text
170
+ }) : title;
171
+ const anchorProps = {
172
+ href: path,
173
+ dangerouslySetInnerHTML: {
174
+ __html: resultLabel
175
+ }
176
+ };
177
+ const linkProps = {
178
+ as: path,
179
+ page,
180
+ title: resultLabel
181
+ };
182
+
183
+ const sendPlausibleGoal = () => {
184
+ Plausible.sendGoal("Search", {
185
+ "Terms - Destination": `${text} - ${path}`
186
+ });
187
+ };
188
+
189
+ const props = externalLink ? anchorProps : linkProps;
190
+ const classes = classnames("search-bar__result", {
191
+ "search-bar__result--active": i === state.active
192
+ });
193
+ return _jsx(Tag, {
194
+ className: classes,
195
+ ...props,
196
+ onClick: sendPlausibleGoal
197
+ }, `${name}-${i}`);
198
+ }), search.result?.length === 0 && _jsx("div", {
199
+ className: "search-bar__no-result",
200
+ children: message(lang, "no-results", messages)
201
+ })]
202
+ })]
203
+ })]
204
+ });
205
+ };
206
+
207
+ export default SearchBar;
@@ -0,0 +1,14 @@
1
+ import * as Params from "./params";
2
+ export const fetchResults = async (ubloApi, site, lang, query, seo, exclude) => {
3
+ const seoParam = seo === false ? "false" : undefined;
4
+ const params = Params.build({
5
+ site,
6
+ lang,
7
+ query,
8
+ seo: seoParam,
9
+ exclude
10
+ });
11
+ const url = `${ubloApi}/api/search${params}`;
12
+ const res = await fetch(url);
13
+ return res.json();
14
+ };
@@ -0,0 +1,5 @@
1
+ export const K = 75;
2
+ export const ArrowUp = 38;
3
+ export const ArrowDown = 40;
4
+ export const Enter = 13;
5
+ export const Escape = 27;
@@ -0,0 +1,8 @@
1
+ export const build = params => {
2
+ return Object.keys(params).map((key, i) => {
3
+ const param = params[key];
4
+ if (!param) return "";
5
+ const sign = i === 0 ? "?" : "&";
6
+ return `${sign}${key}=${param}`;
7
+ }).join("");
8
+ };
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { useUbloContext } from "ublo/with-ublo";
3
- import { loadJS } from "../utils/load-js";
3
+ import { loadJS } from "../../common/utils/load-js";
4
4
  import * as Plausible from "../plausible";
5
5
 
6
6
  const setupConfig = (lang, breadcrumb, cart = [], multipleVillages, hasLangPrefix) => {
@@ -1,6 +1,6 @@
1
1
  import { useEffect } from "react";
2
2
  import { useUbloContext } from "ublo/with-ublo";
3
- import { loadJS } from "../utils/load-js";
3
+ import { loadJS } from "../../common/utils/load-js";
4
4
 
5
5
  const useReviews = (lang, resort, containerId = "msem-reviews", preventInit) => {
6
6
  const {
@@ -0,0 +1,65 @@
1
+ import * as React from "react";
2
+ import * as Icons from "dt-design-system/es/icons";
3
+ import MseMLogo from "./msem-logo";
4
+ import { CONNECTED, EDITING } from "./index";
5
+ import * as UI from "./services/ui";
6
+ import styles from "./actions.module.css";
7
+ import { jsx as _jsx } from "react/jsx-runtime";
8
+ import { Fragment as _Fragment } from "react/jsx-runtime";
9
+ import { jsxs as _jsxs } from "react/jsx-runtime";
10
+
11
+ const Actions = ({
12
+ mode,
13
+ setMode,
14
+ renderEditingUi
15
+ }) => {
16
+ const toggleMode = () => {
17
+ const sections = Array.from(document.querySelectorAll("section[data-class]"));
18
+
19
+ if (mode === CONNECTED) {
20
+ setMode(EDITING);
21
+ window.Cms.desactivate();
22
+ window.Cms.hideInfo();
23
+ window.Cms.pageUi.removeAttribute("active");
24
+ window.Cms.pageUi.setAttribute("hidden", "");
25
+ sections.forEach(renderEditingUi);
26
+ } else {
27
+ setMode(CONNECTED);
28
+ window.Cms.pageUi.removeAttribute("hidden");
29
+ sections.forEach(section => UI.remove(section));
30
+ }
31
+ };
32
+
33
+ const onSaveClick = async () => {
34
+ if (!window.Cms) return;
35
+ window.Cms.pageUi.removeAttribute("hidden");
36
+ window.Cms.pageUi.setAttribute("active", "");
37
+ window.Cms.pageUi.startloading();
38
+ setMode(CONNECTED);
39
+ const sections = Array.from(document.querySelectorAll("section[data-class]"));
40
+ sections.forEach(section => UI.remove(section, true));
41
+ window.Cms.pageUi.unsavedChanges = false;
42
+ await window.Cms.save();
43
+ };
44
+
45
+ return _jsxs(_Fragment, {
46
+ children: [mode === CONNECTED && _jsx("button", {
47
+ className: `${styles.action} ${styles.toggle}`,
48
+ onClick: toggleMode,
49
+ "data-tooltip": "G\xE9rer les widgets",
50
+ children: _jsx(MseMLogo, {})
51
+ }), mode === EDITING && _jsxs(_Fragment, {
52
+ children: [_jsx("button", {
53
+ className: `${styles.action} ${styles.cancel}`,
54
+ onClick: toggleMode,
55
+ children: _jsx(Icons.Cross, {})
56
+ }), _jsx("button", {
57
+ className: `${styles.action} ${styles.save}`,
58
+ onClick: onSaveClick,
59
+ children: _jsx(Icons.Save, {})
60
+ })]
61
+ })]
62
+ });
63
+ };
64
+
65
+ export default Actions;
@@ -0,0 +1,75 @@
1
+ .action {
2
+ position: absolute;
3
+ display: flex;
4
+ align-items: center;
5
+ justify-content: center;
6
+ font-family: "Open Sans", sans-serif;
7
+ background-color: #fff;
8
+ border-radius: 50%;
9
+ box-shadow: var(--cms-shadow-100, 0 5px 10px rgba(0, 0, 0, 0.12));
10
+ cursor: pointer;
11
+ transition: box-shadow 160ms
12
+ var(--cms-transition-easing, cubic-bezier(0.4, 0, 0.2, 1));
13
+ animation: action-button-appearance 320ms
14
+ var(--cms-transition-easing, cubic-bezier(0.4, 0, 0.2, 1));
15
+ }
16
+
17
+ @keyframes action-button-appearance {
18
+ 0% {
19
+ opacity: 0;
20
+ }
21
+ }
22
+
23
+ .toggle {
24
+ bottom: 236px;
25
+ right: 25px;
26
+ width: 36px;
27
+ height: 36px;
28
+ }
29
+
30
+ .cancel,
31
+ .save {
32
+ bottom: 16px;
33
+ width: 56px;
34
+ height: 56px;
35
+ fill: #fff;
36
+ }
37
+
38
+ .cancel {
39
+ right: 16px;
40
+ background-color: var(--cms-red-400, #d50000);
41
+ }
42
+
43
+ .save {
44
+ right: 86px;
45
+ background-color: var(--cms-green-400, #4caf50);
46
+ }
47
+
48
+ .action[data-tooltip]::after {
49
+ content: attr(data-tooltip);
50
+ position: absolute;
51
+ top: calc(50% - 12px);
52
+ right: calc(100% + 4px);
53
+ padding: 6px 10px;
54
+ font-size: 11px;
55
+ line-height: 1;
56
+ font-family: "Open Sans", sans-serif;
57
+ color: var(--cms-grey-000, #ffffff);
58
+ background-color: var(--cms-grey-500, #383838);
59
+ white-space: nowrap;
60
+ border-radius: var(--cms-radius-200, 10px);
61
+ opacity: 0;
62
+ pointer-events: none;
63
+ touch-action: none;
64
+ transition: opacity 240ms
65
+ var(--cms-transition-easing, cubic-bezier(0.4, 0, 0.2, 1));
66
+ }
67
+
68
+ .action:hover::after {
69
+ opacity: 1;
70
+ }
71
+
72
+ .action > svg {
73
+ width: 24px;
74
+ height: 24px;
75
+ }