ublo-lib 1.15.0 → 1.16.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/components/gesco-contact-form/gesco-contact-form.js +2 -3
- package/es/common/components/instant-search/hooks/use-constant.d.ts +2 -0
- package/es/common/components/instant-search/hooks/use-constant.d.ts.map +1 -0
- package/es/common/components/instant-search/hooks/use-constant.js +8 -0
- package/es/common/components/instant-search/hooks/use-debounced-search.d.ts +7 -0
- package/es/common/components/instant-search/hooks/use-debounced-search.d.ts.map +1 -0
- package/es/common/components/instant-search/hooks/use-debounced-search.js +11 -0
- package/es/common/components/instant-search/hooks/use-search.d.ts +16 -0
- package/es/common/components/instant-search/hooks/use-search.d.ts.map +1 -0
- package/es/common/components/instant-search/hooks/use-search.js +10 -0
- package/es/common/components/instant-search/i18n.json +76 -0
- package/es/common/components/instant-search/index.d.ts +3 -0
- package/es/common/components/instant-search/index.d.ts.map +1 -0
- package/es/common/components/instant-search/index.js +2 -0
- package/es/common/components/instant-search/input.d.ts +16 -0
- package/es/common/components/instant-search/input.d.ts.map +1 -0
- package/es/common/components/instant-search/input.js +56 -0
- package/es/common/components/instant-search/input.module.css +87 -0
- package/es/common/components/instant-search/instant-search.d.ts +15 -0
- package/es/common/components/instant-search/instant-search.d.ts.map +1 -0
- package/es/common/components/instant-search/instant-search.js +95 -0
- package/es/common/components/instant-search/instant-search.module.css +38 -0
- package/es/common/components/instant-search/links.d.ts +13 -0
- package/es/common/components/instant-search/links.d.ts.map +1 -0
- package/es/common/components/instant-search/links.js +32 -0
- package/es/common/components/instant-search/links.module.css +96 -0
- package/es/common/components/instant-search/no-product.d.ts +7 -0
- package/es/common/components/instant-search/no-product.d.ts.map +1 -0
- package/es/common/components/instant-search/no-product.js +4 -0
- package/es/common/components/instant-search/products.d.ts +14 -0
- package/es/common/components/instant-search/products.d.ts.map +1 -0
- package/es/common/components/instant-search/products.js +44 -0
- package/es/common/components/instant-search/products.module.css +133 -0
- package/es/common/components/instant-search/results.d.ts +77 -0
- package/es/common/components/instant-search/results.d.ts.map +1 -0
- package/es/common/components/instant-search/results.js +41 -0
- package/es/common/components/instant-search/results.module.css +95 -0
- package/es/common/components/instant-search/services/api.d.ts +2 -0
- package/es/common/components/instant-search/services/api.d.ts.map +1 -0
- package/es/common/components/instant-search/services/api.js +13 -0
- package/es/common/components/instant-search/services/dates.js +50 -0
- package/es/common/components/instant-search/services/messages.d.ts +2 -0
- package/es/common/components/instant-search/services/messages.d.ts.map +1 -0
- package/es/common/components/instant-search/services/messages.js +5 -0
- package/es/common/components/instant-search/services/utils.d.ts +4 -0
- package/es/common/components/instant-search/services/utils.d.ts.map +1 -0
- package/es/common/components/instant-search/services/utils.js +39 -0
- package/package.json +3 -3
|
@@ -163,7 +163,6 @@ function ContactForm({
|
|
|
163
163
|
children: [_jsx(Select, {
|
|
164
164
|
label: Messages.get(widgetLang, "subject"),
|
|
165
165
|
className: css.fullWidthField,
|
|
166
|
-
icon: "Mail",
|
|
167
166
|
placeholder: Messages.get(widgetLang, "defaultSubject"),
|
|
168
167
|
value: fields.subject.value,
|
|
169
168
|
onValueChange: changed("subject"),
|
|
@@ -197,7 +196,7 @@ function ContactForm({
|
|
|
197
196
|
valid: fields.lastname.touched && !fields.lastname.error
|
|
198
197
|
}), _jsx(Input, {
|
|
199
198
|
type: "email",
|
|
200
|
-
icon: "
|
|
199
|
+
icon: "Mail",
|
|
201
200
|
label: Messages.get(widgetLang, "email"),
|
|
202
201
|
value: fields.email.value,
|
|
203
202
|
onValueChange: changed("email"),
|
|
@@ -282,7 +281,7 @@ function ContactForm({
|
|
|
282
281
|
children: [_jsx(Icons.Loader2, {}), Messages.get(widgetLang, "submit")]
|
|
283
282
|
}) : _jsxs(Button, {
|
|
284
283
|
type: "submit",
|
|
285
|
-
children: [_jsx(Icons.
|
|
284
|
+
children: [_jsx(Icons.Plane, {}), Messages.get(widgetLang, "submit")]
|
|
286
285
|
})
|
|
287
286
|
})]
|
|
288
287
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-constant.d.ts","sourceRoot":"","sources":["../../../../../src/common/components/instant-search/hooks/use-constant.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAAE,EAAE,GAAG,OAQ1C"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
export default function useDebouncedSearch(searchFunction: any): {
|
|
3
|
+
text: string;
|
|
4
|
+
setText: React.Dispatch<React.SetStateAction<string>>;
|
|
5
|
+
search: import("react-async-hook").UseAsyncReturn<any, any[]>;
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=use-debounced-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-debounced-search.d.ts","sourceRoot":"","sources":["../../../../../src/common/components/instant-search/hooks/use-debounced-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAO/B,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,cAAc,KAAA;;;;EAWxD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import debouncePromise from "awesome-debounce-promise";
|
|
3
|
+
import { useAsync } from "react-async-hook";
|
|
4
|
+
import useConstant from "./use-constant";
|
|
5
|
+
const DEBOUNCE_AMOUNT = 400;
|
|
6
|
+
export default function useDebouncedSearch(searchFunction) {
|
|
7
|
+
const [text, setText] = React.useState("");
|
|
8
|
+
const debouncedSearchFunction = useConstant(() => debouncePromise(searchFunction, DEBOUNCE_AMOUNT));
|
|
9
|
+
const search = useAsync(async () => (text.length === 0 ? undefined : debouncedSearchFunction(text)), [debouncedSearchFunction, text], { setLoading: (state) => ({ ...state, loading: true }) });
|
|
10
|
+
return { text, setText, search };
|
|
11
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
export declare type SearchOptions = {
|
|
3
|
+
groupLimit?: number;
|
|
4
|
+
page?: number;
|
|
5
|
+
perPage?: number;
|
|
6
|
+
queryBy?: string;
|
|
7
|
+
queryByWeights?: string;
|
|
8
|
+
filterBy?: string;
|
|
9
|
+
sortBy?: string;
|
|
10
|
+
};
|
|
11
|
+
export default function useSearch(lang: string, options?: SearchOptions): {
|
|
12
|
+
text: string;
|
|
13
|
+
setText: import("react").Dispatch<import("react").SetStateAction<string>>;
|
|
14
|
+
search: import("react-async-hook").UseAsyncReturn<any, any[]>;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=use-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-search.d.ts","sourceRoot":"","sources":["../../../../../src/common/components/instant-search/hooks/use-search.ts"],"names":[],"mappings":";AAOA,oBAAY,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB;;;;EAW1E"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import getConfig from "next/config";
|
|
2
|
+
import useDebouncedSearch from "./use-debounced-search";
|
|
3
|
+
import * as API from "../services/api";
|
|
4
|
+
const { publicRuntimeConfig } = getConfig();
|
|
5
|
+
const { site } = publicRuntimeConfig;
|
|
6
|
+
export default function useSearch(lang, options = {}) {
|
|
7
|
+
return useDebouncedSearch(async (query) => {
|
|
8
|
+
return API.fetchResults(site, lang, query, options.groupLimit, options.queryBy, options.queryByWeights);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"fr": {
|
|
3
|
+
"try": "Essayez",
|
|
4
|
+
"pages": "Pages",
|
|
5
|
+
"faqs": "Questions fréquentes",
|
|
6
|
+
"information": "Informations",
|
|
7
|
+
"instructors": "Moniteurs",
|
|
8
|
+
"noProducts": "Pas de produit correspondant",
|
|
9
|
+
"productTitleWithWeek": "Les offres pour la période du ",
|
|
10
|
+
"productTitleWithoutWeek": "Choisissez votre période pour afficher les tarifs et affiner les résultats",
|
|
11
|
+
"chooseWeek": "Choisir ma période",
|
|
12
|
+
"updateWeek": "Modifier ma période",
|
|
13
|
+
"showOtherResults": "Afficher les autres résultats"
|
|
14
|
+
},
|
|
15
|
+
"en": {
|
|
16
|
+
"try": "Try",
|
|
17
|
+
"pages": "Pages",
|
|
18
|
+
"faqs": "FAQs",
|
|
19
|
+
"information": "Information",
|
|
20
|
+
"instructors": "Instructors",
|
|
21
|
+
"noProducts": "No matching product",
|
|
22
|
+
"productTitleWithWeek": "Offers for the period from ",
|
|
23
|
+
"productTitleWithoutWeek": "Choose your period to view rates and refine results below",
|
|
24
|
+
"chooseWeek": "Choose my period",
|
|
25
|
+
"updateWeek": "Change my period",
|
|
26
|
+
"showOtherResults": "Show other results"
|
|
27
|
+
},
|
|
28
|
+
"es": {
|
|
29
|
+
"pages": "Paginas",
|
|
30
|
+
"faqs": "Preguntas frecuentes",
|
|
31
|
+
"information": "Informaciones",
|
|
32
|
+
"instructors": "Instructores",
|
|
33
|
+
"noProducts": "Ningún producto coincidente",
|
|
34
|
+
"productTitleWithWeek": "Ofertas para el período de ",
|
|
35
|
+
"productTitleWithoutWeek": "Elija su período para ver las tarifas y refinar los resultados a continuación",
|
|
36
|
+
"chooseWeek": "Elige mi periodo",
|
|
37
|
+
"updateWeek": "Cambiar mi periodo",
|
|
38
|
+
"showOtherResults": "Mostrar otros resultados"
|
|
39
|
+
},
|
|
40
|
+
"it": {
|
|
41
|
+
"pages": "Pagine",
|
|
42
|
+
"faqs": "Domande frequenti",
|
|
43
|
+
"information": "Informazioni",
|
|
44
|
+
"instructors": "Insegnanti",
|
|
45
|
+
"noProducts": "Nessun prodotto corrispondente",
|
|
46
|
+
"productTitleWithWeek": "Offerte per il periodo dal ",
|
|
47
|
+
"productTitleWithoutWeek": "Scegli il tuo periodo per visualizzare le tariffe e perfezionare i risultati di seguito",
|
|
48
|
+
"chooseWeek": "Scegli il mio periodo",
|
|
49
|
+
"updateWeek": "Cambia il mio ciclo",
|
|
50
|
+
"showOtherResults": "Mostra altri risultati"
|
|
51
|
+
},
|
|
52
|
+
"nl": {
|
|
53
|
+
"pages": "Pagina's",
|
|
54
|
+
"faqs": "Veel Gestelde Vragen",
|
|
55
|
+
"information": "Informatie",
|
|
56
|
+
"instructors": "Instructeurs",
|
|
57
|
+
"noProducts": "Geen bijpassend product",
|
|
58
|
+
"productTitleWithWeek": "Aanbiedingen voor de periode van ",
|
|
59
|
+
"productTitleWithoutWeek": "Kies hieronder uw periode om tarieven te bekijken en resultaten te verfijnen",
|
|
60
|
+
"chooseWeek": "Kies mijn periode",
|
|
61
|
+
"updateWeek": "Wijzig mijn menstruatie",
|
|
62
|
+
"showOtherResults": "Toon andere resultaten"
|
|
63
|
+
},
|
|
64
|
+
"ru": {
|
|
65
|
+
"pages": "Страницы",
|
|
66
|
+
"faqs": "Часто задаваемые вопросы",
|
|
67
|
+
"information": "Информация",
|
|
68
|
+
"instructors": "Преподаватели",
|
|
69
|
+
"noProducts": "Нет подходящего продукта",
|
|
70
|
+
"productTitleWithWeek": "Предложения на период с ",
|
|
71
|
+
"productTitleWithoutWeek": "Выберите период, чтобы просмотреть цены и уточнить результаты ниже",
|
|
72
|
+
"chooseWeek": "Выберите мой период",
|
|
73
|
+
"updateWeek": "Изменить мой период",
|
|
74
|
+
"showOtherResults": "Показать другие результаты"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/common/components/instant-search/index.tsx"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,kBAAkB,CAAC;AAE7C,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
declare type Suggestions = {
|
|
3
|
+
fr?: string[];
|
|
4
|
+
en?: string[];
|
|
5
|
+
};
|
|
6
|
+
declare type Props = {
|
|
7
|
+
lang: string;
|
|
8
|
+
text: string;
|
|
9
|
+
setText: (value: string) => void;
|
|
10
|
+
loading: boolean;
|
|
11
|
+
closeSearch: () => void;
|
|
12
|
+
suggestions?: Suggestions;
|
|
13
|
+
};
|
|
14
|
+
export default function SearchInput({ lang, text, setText, closeSearch, loading, suggestions, }: Props): JSX.Element;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../../../src/common/components/instant-search/input.tsx"],"names":[],"mappings":";AASA,aAAK,WAAW,GAAG;IACjB,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;CACf,CAAC;AAEF,aAAK,KAAK,GAAG;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B,CAAC;AAIF,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,WAAW,EACX,OAAO,EACP,WAAW,GACZ,EAAE,KAAK,eA8EP"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import classNames from "classnames";
|
|
4
|
+
import Input from "dt-design-system/es/input";
|
|
5
|
+
import Button from "dt-design-system/es/button";
|
|
6
|
+
import * as Icons from "dt-design-system/es/icons";
|
|
7
|
+
import * as Ripple from "dt-design-system/es/ripple";
|
|
8
|
+
import message from "./services/messages";
|
|
9
|
+
import css from "./input.module.css";
|
|
10
|
+
const TRANSITION_DURATION = 1800;
|
|
11
|
+
export default function SearchInput({ lang, text, setText, closeSearch, loading, suggestions, }) {
|
|
12
|
+
const [currentSuggestion, setCurrentSuggestion] = React.useState(0);
|
|
13
|
+
const updateText = (value) => () => {
|
|
14
|
+
setText(value);
|
|
15
|
+
};
|
|
16
|
+
const classes = classNames(css.input, {
|
|
17
|
+
[css.inputLoading]: loading,
|
|
18
|
+
});
|
|
19
|
+
const inputIcon = loading ? "Loader2" : "Search";
|
|
20
|
+
const suggestionsSet = getSuggestions(suggestions)[lang];
|
|
21
|
+
const enableSuggestion = Boolean(suggestionsSet);
|
|
22
|
+
const createRipple = (e) => {
|
|
23
|
+
Ripple.create(e);
|
|
24
|
+
};
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
if (enableSuggestion && !text) {
|
|
27
|
+
const interval = setInterval(() => {
|
|
28
|
+
setCurrentSuggestion((current) => {
|
|
29
|
+
const nextSuggestion = current >= suggestionsSet.length - 1 ? 0 : current + 1;
|
|
30
|
+
return nextSuggestion;
|
|
31
|
+
});
|
|
32
|
+
}, TRANSITION_DURATION);
|
|
33
|
+
return () => {
|
|
34
|
+
clearInterval(interval);
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}, [enableSuggestion, suggestionsSet, text]);
|
|
38
|
+
return (_jsxs("div", { className: css.container, children: [_jsx(Input, { value: text, onValueChange: setText, icon: inputIcon, className: classes }), enableSuggestion && !text && (_jsxs("div", { className: css.suggestion, children: [message(lang, "try"), _jsx("div", { className: css.buttons, children: suggestionsSet.map((suggestion, i) => {
|
|
39
|
+
const isCurrent = i === currentSuggestion;
|
|
40
|
+
const isPrev = currentSuggestion === 0
|
|
41
|
+
? i === suggestionsSet.length - 1
|
|
42
|
+
: i === currentSuggestion - 1;
|
|
43
|
+
const classes = classNames(css.button, {
|
|
44
|
+
[css.buttonCurrent]: isCurrent,
|
|
45
|
+
[css.buttonPrev]: isPrev,
|
|
46
|
+
});
|
|
47
|
+
return (_jsxs("button", { className: classes, onClick: updateText(suggestion), onMouseDown: createRipple, children: ["\u00AB ", suggestion, " \u00BB"] }, suggestion));
|
|
48
|
+
}) })] })), _jsx(Button, { variant: "transparent", className: css.close, onClick: closeSearch, children: _jsx(Icons.Cross, {}) })] }));
|
|
49
|
+
}
|
|
50
|
+
function getSuggestions(suggestions = {}) {
|
|
51
|
+
return {
|
|
52
|
+
fr: ["cours de ski", "cours privés", "compétition", "cours débutants"],
|
|
53
|
+
en: ["ski lessons", "private lessons", "competition"],
|
|
54
|
+
...suggestions,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
.container {
|
|
2
|
+
position: relative;
|
|
3
|
+
padding: 10px;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.input {
|
|
7
|
+
--ds-input-padding: 14px;
|
|
8
|
+
--ds-input-background: var(--ds-grey-000, #fff);
|
|
9
|
+
|
|
10
|
+
width: 100%;
|
|
11
|
+
border-radius: var(--ds-radius-200, 10px);
|
|
12
|
+
box-shadow: var(--ds-shadow-200, 0 5px 10px rgba(0, 0, 0, 0.12));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.inputLoading svg {
|
|
16
|
+
animation: search-loader-spinning 1280ms
|
|
17
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0.1, 0.2, 0.9)) infinite;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@keyframes search-loader-spinning {
|
|
21
|
+
100% {
|
|
22
|
+
transform: rotate(1turn);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.suggestion {
|
|
27
|
+
position: absolute;
|
|
28
|
+
top: 50%;
|
|
29
|
+
left: 54px;
|
|
30
|
+
height: 21px;
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
line-height: 1;
|
|
34
|
+
color: var(--ds-grey-500, #484848);
|
|
35
|
+
transform: translateY(-50%);
|
|
36
|
+
pointer-events: none;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.buttons {
|
|
40
|
+
position: relative;
|
|
41
|
+
height: inherit;
|
|
42
|
+
color: inherit;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.button {
|
|
46
|
+
position: absolute;
|
|
47
|
+
top: 0;
|
|
48
|
+
left: 0;
|
|
49
|
+
width: max-content;
|
|
50
|
+
height: 100%;
|
|
51
|
+
padding: 2px 5px 4px 5px;
|
|
52
|
+
color: inherit;
|
|
53
|
+
line-height: 1;
|
|
54
|
+
font-weight: 700;
|
|
55
|
+
cursor: pointer;
|
|
56
|
+
pointer-events: auto;
|
|
57
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
58
|
+
transform: translateY(100%);
|
|
59
|
+
opacity: 0;
|
|
60
|
+
pointer-events: none;
|
|
61
|
+
transition: background-color 160ms
|
|
62
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0.1, 0.2, 0.9)),
|
|
63
|
+
opacity 320ms var(--ds-transition-easing, cubic-bezier(0.4, 0.1, 0.2, 0.9)),
|
|
64
|
+
transform 320ms
|
|
65
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0.1, 0.2, 0.9));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.buttonPrev {
|
|
69
|
+
transform: translateY(-100%);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.buttonCurrent {
|
|
73
|
+
opacity: 1;
|
|
74
|
+
transform: none;
|
|
75
|
+
pointer-events: auto;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.button:is(:hover, :focus) {
|
|
79
|
+
background-color: var(--ds-grey-100, #f5f5f5);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.close {
|
|
83
|
+
position: absolute;
|
|
84
|
+
top: 17px;
|
|
85
|
+
right: 14px;
|
|
86
|
+
color: inherit;
|
|
87
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { SearchOptions } from "./hooks/use-search";
|
|
3
|
+
declare type Props = {
|
|
4
|
+
lang: string;
|
|
5
|
+
className?: string;
|
|
6
|
+
ubloContext: any;
|
|
7
|
+
suggestions?: {
|
|
8
|
+
fr?: string[];
|
|
9
|
+
en?: string[];
|
|
10
|
+
};
|
|
11
|
+
options?: SearchOptions;
|
|
12
|
+
};
|
|
13
|
+
export default function InstantSearch({ lang, suggestions, className, ubloContext, options, }: Props): JSX.Element;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=instant-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instant-search.d.ts","sourceRoot":"","sources":["../../../../src/common/components/instant-search/instant-search.tsx"],"names":[],"mappings":";AAUA,OAAkB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAK9D,aAAK,KAAK,GAAG;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,GAAG,CAAC;IACjB,WAAW,CAAC,EAAE;QACZ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;QACd,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;KACf,CAAC;IACF,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACpC,IAAI,EACJ,WAAW,EACX,SAAS,EACT,WAAW,EACX,OAAY,GACb,EAAE,KAAK,eAsIP"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import classNames from "classnames";
|
|
4
|
+
import Router from "next/router";
|
|
5
|
+
import Button from "dt-design-system/es/button";
|
|
6
|
+
import * as Icons from "dt-design-system/es/icons";
|
|
7
|
+
import Dialog from "dt-design-system/es/dialog";
|
|
8
|
+
// import PeriodPicker from "ublo-lib/es/esf/components/period-picker";
|
|
9
|
+
import PeriodPicker from "../../../esf/components/period-picker";
|
|
10
|
+
import Input from "./input";
|
|
11
|
+
import Results from "./results";
|
|
12
|
+
import useSearch from "./hooks/use-search";
|
|
13
|
+
// import * as Plausible from "ublo-lib/es/common/components/plausible";
|
|
14
|
+
import * as Plausible from "../plausible";
|
|
15
|
+
import css from "./instant-search.module.css";
|
|
16
|
+
export default function InstantSearch({ lang, suggestions, className, ubloContext, options = {}, }) {
|
|
17
|
+
const [opened, setOpened] = React.useState(false);
|
|
18
|
+
const [closing, setClosing] = React.useState(false);
|
|
19
|
+
const [periodPickerOpened, setPeriodPickerOpened] = React.useState(false);
|
|
20
|
+
const [weekNumber, setWeekNumber] = React.useState(null);
|
|
21
|
+
const { text, setText, search } = useSearch(lang, options);
|
|
22
|
+
const classes = classNames(css.search, className);
|
|
23
|
+
const dialogClasses = classNames(css.dialog, {
|
|
24
|
+
[css.dialogClosing]: closing,
|
|
25
|
+
});
|
|
26
|
+
const openSearch = () => {
|
|
27
|
+
setOpened(true);
|
|
28
|
+
};
|
|
29
|
+
const closeSearch = (skipEvent) => {
|
|
30
|
+
setClosing(true);
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
setClosing(false);
|
|
33
|
+
setOpened(false);
|
|
34
|
+
setText("");
|
|
35
|
+
document.body.style.removeProperty("overflow");
|
|
36
|
+
if (!skipEvent) {
|
|
37
|
+
const event = search?.result?.length === 0 ? "Search failed" : "Search canceled";
|
|
38
|
+
if (text.length > 0) {
|
|
39
|
+
Plausible.sendGoal(event, {
|
|
40
|
+
Terms: text,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}, 320);
|
|
45
|
+
};
|
|
46
|
+
const sendPlausibleGoal = (path) => () => {
|
|
47
|
+
Plausible.sendGoal("Search", {
|
|
48
|
+
"Terms - Destination": `${text} - ${path}`,
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
const openPeriodPicker = () => {
|
|
52
|
+
setPeriodPickerOpened(true);
|
|
53
|
+
};
|
|
54
|
+
const closePeriodPicker = () => {
|
|
55
|
+
setPeriodPickerOpened(false);
|
|
56
|
+
};
|
|
57
|
+
const updateWeek = () => {
|
|
58
|
+
const storedWeekNumber = window.sessionStorage.getItem("stayWeek");
|
|
59
|
+
if (storedWeekNumber !== null) {
|
|
60
|
+
setWeekNumber(Number(storedWeekNumber));
|
|
61
|
+
setPeriodPickerOpened(false);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
Router.ready(() => {
|
|
65
|
+
Router.events.on("routeChangeStart", () => {
|
|
66
|
+
if (opened) {
|
|
67
|
+
closeSearch(true);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
const handleKeyPresses = React.useCallback((e) => {
|
|
72
|
+
const allowedKeys = ["k"];
|
|
73
|
+
const { code, ctrlKey, key, metaKey } = e;
|
|
74
|
+
const isAllowed = (ctrlKey || metaKey) &&
|
|
75
|
+
(allowedKeys.includes(code) || allowedKeys.includes(key));
|
|
76
|
+
if (!isAllowed)
|
|
77
|
+
return;
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
if (key === "k" && (ctrlKey || metaKey)) {
|
|
80
|
+
openSearch();
|
|
81
|
+
}
|
|
82
|
+
}, []);
|
|
83
|
+
React.useEffect(() => {
|
|
84
|
+
if (opened) {
|
|
85
|
+
updateWeek();
|
|
86
|
+
}
|
|
87
|
+
}, [opened]);
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
window.addEventListener("keydown", handleKeyPresses);
|
|
90
|
+
return () => {
|
|
91
|
+
window.removeEventListener("keydown", handleKeyPresses);
|
|
92
|
+
};
|
|
93
|
+
}, [handleKeyPresses]);
|
|
94
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, { className: classes, onClick: openSearch, children: _jsx(Icons.Search, { className: css.searchIcon }) }), _jsxs(Dialog, { className: dialogClasses, isOpened: closing || opened, showCloseButton: false, close: closeSearch, container: "body", children: [_jsx(Input, { lang: lang, text: text, setText: setText, closeSearch: closeSearch, loading: search.loading === true, suggestions: suggestions }), _jsx(Results, { lang: lang, text: text, results: search.result, weekNumber: weekNumber, openPeriodPicker: openPeriodPicker, loading: search.loading === true && search.result === undefined, sendPlausibleGoal: sendPlausibleGoal })] }), _jsx(Dialog, { isOpened: periodPickerOpened, close: closePeriodPicker, container: "body", children: _jsx(PeriodPicker, { onChange: updateWeek, ubloContext: ubloContext }) })] }));
|
|
95
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
.dialog {
|
|
2
|
+
width: 1100px;
|
|
3
|
+
max-width: calc(100% - 6px);
|
|
4
|
+
max-height: calc(100% - 6px);
|
|
5
|
+
background-color: transparent;
|
|
6
|
+
box-shadow: none;
|
|
7
|
+
transition: opacity 320ms
|
|
8
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0.1, 0.2, 0.9));
|
|
9
|
+
pointer-events: auto;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.dialog::backdrop {
|
|
13
|
+
transition: opacity 320ms
|
|
14
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0.1, 0.2, 0.9));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.dialog[open]:not(.loading) {
|
|
18
|
+
animation: dialog-appearance 320ms
|
|
19
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0.1, 0.2, 0.9));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@keyframes dialog-appearance {
|
|
23
|
+
0% {
|
|
24
|
+
opacity: 0;
|
|
25
|
+
transform: translateY(20px);
|
|
26
|
+
}
|
|
27
|
+
70% {
|
|
28
|
+
transform: translateY(-4px);
|
|
29
|
+
}
|
|
30
|
+
100% {
|
|
31
|
+
transform: none;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.dialogClosing,
|
|
36
|
+
.dialogClosing::backdrop {
|
|
37
|
+
opacity: 0;
|
|
38
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { Hit } from "./results";
|
|
3
|
+
declare type Props = {
|
|
4
|
+
linksTitle: string;
|
|
5
|
+
icon: "FileText" | "Info" | "Question" | "User";
|
|
6
|
+
links: Hit[];
|
|
7
|
+
pageTitleAsTitle?: boolean;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
sendPlausibleGoal: (path: string) => void;
|
|
10
|
+
};
|
|
11
|
+
export default function Links({ icon, linksTitle, links, pageTitleAsTitle, loading, sendPlausibleGoal, }: Props): JSX.Element | null;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=links.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../../../src/common/components/instant-search/links.tsx"],"names":[],"mappings":";AAMA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGrC,aAAK,KAAK,GAAG;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IAChD,KAAK,EAAE,GAAG,EAAE,CAAC;IACb,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C,CAAC;AAIF,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,EAC5B,IAAI,EACJ,UAAU,EACV,KAAK,EACL,gBAAgB,EAChB,OAAO,EACP,iBAAiB,GAClB,EAAE,KAAK,sBAoEP"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import Link from "ublo/link";
|
|
3
|
+
import Loader from "dt-design-system/es/loader";
|
|
4
|
+
import * as Icons from "dt-design-system/es/icons";
|
|
5
|
+
import * as Ripple from "dt-design-system/es/ripple";
|
|
6
|
+
import * as Utils from "./services/utils";
|
|
7
|
+
import css from "./links.module.css";
|
|
8
|
+
const PLACEHOLDERS = [...new Array(3)];
|
|
9
|
+
export default function Links({ icon, linksTitle, links, pageTitleAsTitle, loading, sendPlausibleGoal, }) {
|
|
10
|
+
const Icon = Icons[icon];
|
|
11
|
+
const noResult = !loading && !links.length;
|
|
12
|
+
const createRipple = (e) => {
|
|
13
|
+
Ripple.create(e);
|
|
14
|
+
};
|
|
15
|
+
if (noResult)
|
|
16
|
+
return null;
|
|
17
|
+
return (_jsxs("div", { className: css.linksSection, children: [_jsx("div", { className: css.linksTitle, children: linksTitle }), _jsxs("div", { className: css.links, children: [loading &&
|
|
18
|
+
PLACEHOLDERS.map((_, i) => {
|
|
19
|
+
return (_jsx("div", { className: css.loaderContainer, children: _jsx(Loader, { className: css.loader, variant: "overlay" }) }, i));
|
|
20
|
+
}), !loading &&
|
|
21
|
+
links.map((link) => {
|
|
22
|
+
const { id, text, parentTitle, path } = link.document;
|
|
23
|
+
const decodedPath = decodeURIComponent(path);
|
|
24
|
+
const titleProperty = pageTitleAsTitle ? "pageTitle" : "title";
|
|
25
|
+
const linkTitle = Utils.getHighlight(titleProperty, link.highlight) ||
|
|
26
|
+
link.document[titleProperty];
|
|
27
|
+
const linkText = Utils.getHighlight("text", link.highlight, true) || text;
|
|
28
|
+
return (
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
_jsxs(Link, { href: decodedPath, className: css.link, onClick: sendPlausibleGoal(decodedPath), onMouseDown: createRipple, children: [_jsx(Icon, { className: css.linkIcon }), _jsxs("div", { className: css.linkContent, children: [parentTitle && (_jsx("div", { className: css.linkParent, dangerouslySetInnerHTML: { __html: parentTitle } })), _jsx("div", { className: css.linkTitle, dangerouslySetInnerHTML: { __html: linkTitle } }), linkText && (_jsx("div", { className: css.linkText, dangerouslySetInnerHTML: { __html: linkText } }))] })] }, id));
|
|
31
|
+
})] })] }));
|
|
32
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
.linksSection {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: 6px;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.linksTitle {
|
|
8
|
+
position: sticky;
|
|
9
|
+
top: 16px;
|
|
10
|
+
font-size: 17px;
|
|
11
|
+
font-weight: 700;
|
|
12
|
+
padding: 3px 8px;
|
|
13
|
+
background-color: var(--ds-grey-200, #efefef);
|
|
14
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
15
|
+
z-index: 1;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@media (min-width: 730px) {
|
|
19
|
+
.linksTitle {
|
|
20
|
+
top: 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.links {
|
|
25
|
+
display: flex;
|
|
26
|
+
flex-direction: column;
|
|
27
|
+
gap: 6px;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.loaderContainer {
|
|
31
|
+
position: relative;
|
|
32
|
+
width: 100%;
|
|
33
|
+
height: 44px;
|
|
34
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.link {
|
|
38
|
+
position: relative;
|
|
39
|
+
display: flex;
|
|
40
|
+
gap: 10px;
|
|
41
|
+
padding: 6px;
|
|
42
|
+
color: var(--ds-grey-500, #484848);
|
|
43
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
44
|
+
transition: background-color 160ms
|
|
45
|
+
var(--ds-transition-easing, cubic-bezier(0.4, 0, 0.2, 1));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.link:is(:hover, :focus) {
|
|
49
|
+
background-color: var(--ds-grey-100, #f5f5f5);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.linkIcon {
|
|
53
|
+
flex: 0 0 16px;
|
|
54
|
+
width: 16px;
|
|
55
|
+
height: 16px;
|
|
56
|
+
margin-top: 2px;
|
|
57
|
+
fill: currentColor;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.linkContent {
|
|
61
|
+
flex: 1 1 auto;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.linkTitle {
|
|
65
|
+
font-size: 15px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.link mark {
|
|
69
|
+
position: relative;
|
|
70
|
+
display: inline-block;
|
|
71
|
+
font-weight: 700;
|
|
72
|
+
color: var(--ds-secondary, var(--ds-blue-400, #4177f6));
|
|
73
|
+
background-color: transparent;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.link mark::before {
|
|
77
|
+
content: "";
|
|
78
|
+
position: absolute;
|
|
79
|
+
top: 0;
|
|
80
|
+
left: 0;
|
|
81
|
+
width: 100%;
|
|
82
|
+
height: 100%;
|
|
83
|
+
background-color: var(--ds-secondary, var(--ds-blue-400, #4177f6));
|
|
84
|
+
border-radius: calc(var(--ds-radius-100, 6px) / 3);
|
|
85
|
+
opacity: 0.15;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.linkParent {
|
|
89
|
+
text-transform: uppercase;
|
|
90
|
+
font-size: 10px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.linkText {
|
|
94
|
+
font-size: 12px;
|
|
95
|
+
white-space: pre-wrap;
|
|
96
|
+
}
|