ublo-lib 1.15.1 → 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/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
|
@@ -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
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-product.d.ts","sourceRoot":"","sources":["../../../../src/common/components/instant-search/no-product.tsx"],"names":[],"mappings":";AAEA,aAAK,KAAK,GAAG;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,KAAK,eA2FrD"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export default function NoProduct({ className }) {
|
|
3
|
+
return (_jsxs("svg", { width: "200", height: "200", viewBox: "0 0 200 200", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className, children: [_jsx("path", { d: "M100.5 184.3a85.8 85.8 0 1 0 0-171.5 85.8 85.8 0 0 0 0 171.5Z", fill: "#EDEDED" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M100.5 11a87.5 87.5 0 1 0 0 175 87.5 87.5 0 0 0 0-175Zm0 3.5a84 84 0 1 1 0 168 84 84 0 0 1 0-168Z", fill: "#D7D7D7" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M161.4 61.5c1 0 1.8-.8 1.8-1.8V46.1a4.7 4.7 0 0 0-4.7-4.7H26a4.7 4.7 0 0 0-4.7 4.7v34.3c0 1 .8 1.8 1.8 1.8h61.5c.4 0 .8-.1 1.1-.4l26.4-20.3h49.3Z", fill: "#D7D7D7" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M138.4 59.7h-25.5L84.6 80.4H45.3V25.7a3 3 0 0 1 2.9-2.9h64.7l25.5 27.5v9.4Z", fill: "#fff" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M112.9 21H48.2a4.7 4.7 0 0 0-4.7 4.7v54.7c0 1 .8 1.8 1.8 1.8h39.3c.4 0 .8-.1 1-.3l27.8-20.4h25c1 0 1.8-.8 1.8-1.8V50l-.1-.3-.2-.3v-.2l-.2-.1L114 21.6l-.1-.2-.4-.2h-.1l-.3-.1H113Zm-1.8 3.6H48.2c-.6 0-1.2.5-1.2 1.1v53h37l27.8-20.4c.3-.2.7-.3 1-.3h23.8v-6H113c-1 0-1.8-.8-1.8-1.7V24.6Zm-51 44.8h25.7a1.8 1.8 0 0 0 0-3.5H60.2a1.8 1.8 0 0 0 0 3.5Zm0-16h22a1.8 1.8 0 0 0 0-3.5H60a1.8 1.8 0 0 0 0 3.5Zm0-8.4h37.4a1.8 1.8 0 0 0 0-3.6H60.2a1.8 1.8 0 0 0 0 3.6Z", fill: "#D7D7D7" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M4.8 84a3 3 0 0 1 2.9-3.6h76.5a3 3 0 0 0 1.7-.5l26.4-19.6a3 3 0 0 1 1.8-.6h64.7a3 3 0 0 1 3 3.5c-2.9 16-14.4 83-16.8 96.7a3 3 0 0 1-2.9 2.4H25.4a3 3 0 0 1-2.9-2.2L4.8 84Z", fill: "#EDEDED" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "m3.1 84.4 17.7 76a4.7 4.7 0 0 0 4.6 3.7h136.7c2.3 0 4.2-1.7 4.6-4l16.7-96.6a4.7 4.7 0 0 0-4.6-5.5h-64.7c-1 0-2 .3-2.8.9a1130716.6 1130716.6 0 0 1-27.1 19.8H7.7a4.7 4.7 0 0 0-4.6 5.7Zm3.5-.8a1.2 1.2 0 0 1 1.1-1.4h76.5c1 0 2-.3 2.8-1a724489.6 724489.6 0 0 1 27-19.7h64.8a1.2 1.2 0 0 1 1.2 1.4l-16.7 96.7c-.1.6-.6 1-1.2 1H25.4c-.5 0-1-.4-1.1-1l-17.7-76Z", fill: "#D7D7D7" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M159.7 161a36.5 36.5 0 0 1-46.5-4 36.6 36.6 0 0 1 .4-51.8 36.6 36.6 0 0 1 51.7-.4 36.5 36.5 0 0 1 4 46.5l25 24.9c2.6 2.6 2.6 7 0 9.6a6.8 6.8 0 0 1-9.7 0l-25-24.9Z", fill: "#fff" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "m159.5 163.2 23.8 23.9a8.6 8.6 0 0 0 12.2 0 8.6 8.6 0 0 0 0-12.2L171.6 151a38.2 38.2 0 0 0-5-47.4 38.3 38.3 0 0 0-54.2.4 38.3 38.3 0 0 0-.4 54.2 38.2 38.2 0 0 0 47.5 5Zm1.4-3.5c-.6-.6-1.5-.7-2.2-.2a34.7 34.7 0 0 1-44.2-3.8 34.8 34.8 0 0 1 .4-49.2 34.8 34.8 0 0 1 49.2-.4c12 11.9 13.1 30.5 3.8 44.2-.5.7-.4 1.6.2 2.2l24.9 25c2 1.9 2 5.1 0 7a5 5 0 0 1-7.2 0L161 159.8Z", fill: "#D7D7D7" }), _jsx("path", { d: "M163.5 130.8a24.4 24.4 0 0 0-24.2-24.6c-13.4 0-24.3 11-24.3 24.6a24.4 24.4 0 0 0 24.3 24.6c13.3 0 24.2-11 24.2-24.6Z", fill: "#EDEDED" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M113.3 130.8a26.2 26.2 0 0 0 26 26.4c14.3 0 26-11.8 26-26.4a26.2 26.2 0 0 0-26-26.4c-14.4 0-26 11.8-26 26.4Zm3.5 0c0-12.6 10-22.8 22.5-22.8a22.7 22.7 0 0 1 22.4 22.8c0 12.6-10 22.9-22.4 22.9a22.7 22.7 0 0 1-22.5-22.9Z", fill: "#D7D7D7" }), _jsx("path", { opacity: ".7", fillRule: "evenodd", clipRule: "evenodd", d: "M117 121c.7-1.4 13.9 1.6 22.3 9.9 8.1 8 11.4 21.4 10.2 22-9.1 4.3-20.2 2.7-27.6-4.7-7.4-7.3-9-18.3-4.8-27.3Z", fill: "#fff" }), _jsx("path", { fillRule: "evenodd", clipRule: "evenodd", d: "M120.9 112.5a26.2 26.2 0 0 0-.3 37 26.2 26.2 0 0 0 37-.2 26.2 26.2 0 0 0 .3-37 26.2 26.2 0 0 0-37 .2Zm2.5 2.5c9-9 23.3-9 32-.3a22.7 22.7 0 0 1-.2 32c-9 9-23.3 9.1-32 .3a22.7 22.7 0 0 1 .2-32Z", fill: "#D7D7D7" }), _jsx("path", { d: "M140.2 142.1c1.4 0 2.4 1 2.4 2.4 0 1.3-1 2.3-2.4 2.3-1.3 0-2.3-1-2.3-2.3 0-1.3 1-2.4 2.3-2.4Zm.1-20.5V117a9.5 9.5 0 0 0-9.4 6.8v.2l4.5 1v.1l-4.6-1.1c-.3 1.2.5 2.5 1.8 2.8 1.2.3 2.5-.5 2.8-1.7l.2-.5.7-1.2c.7-.8 1.8-1.7 4-1.7Zm9.4 5.1a9.3 9.3 0 0 0-9.4-9.8v4.7c.6 0 1.8.2 2.9 1 .9.6 1.8 1.7 1.8 4.1 0 .5-.2 1-.7 1.6-.5.6-1.3 1.2-2.2 1.7a18.4 18.4 0 0 1-3.4 1.5h-.3c-1.2.4-2 1.8-1.6 3 .4 1.3 1.7 2 3 1.6l-.7-2.2-.4-1.4-.2-.6V131.6l.6 2.3.6 2.2h.2a11.6 11.6 0 0 0 1.3-.5c.9-.3 2-.8 3.2-1.5 1.2-.6 2.4-1.5 3.4-2.7a7 7 0 0 0 2-4.7Z", fill: "#D7D7D7" })] }));
|
|
4
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { Hit } from "./results";
|
|
3
|
+
declare type Props = {
|
|
4
|
+
lang: string;
|
|
5
|
+
products: Hit[];
|
|
6
|
+
weekNumber: number | null;
|
|
7
|
+
loading: boolean;
|
|
8
|
+
sendPlausibleGoal: (path: string) => void;
|
|
9
|
+
otherResultsRef: React.RefObject<HTMLDivElement>;
|
|
10
|
+
isOtherResultsEmpty: boolean;
|
|
11
|
+
};
|
|
12
|
+
export default function Products({ lang, products, weekNumber, loading, sendPlausibleGoal, otherResultsRef, isOtherResultsEmpty, }: Props): JSX.Element;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=products.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"products.d.ts","sourceRoot":"","sources":["../../../../src/common/components/instant-search/products.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAU/B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGrC,aAAK,KAAK,GAAG;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,eAAe,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IACjD,mBAAmB,EAAE,OAAO,CAAC;CAC9B,CAAC;AAIF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAC/B,IAAI,EACJ,QAAQ,EACR,UAAU,EACV,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,mBAAmB,GACpB,EAAE,KAAK,eA6HP"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import Image from "next/image";
|
|
3
|
+
import Link from "ublo/link";
|
|
4
|
+
import Loader from "dt-design-system/es/loader";
|
|
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 * as Utils from "./services/utils";
|
|
9
|
+
import message from "./services/messages";
|
|
10
|
+
import NoProduct from "./no-product";
|
|
11
|
+
import css from "./products.module.css";
|
|
12
|
+
const PLACEHOLDERS = [...new Array(5)];
|
|
13
|
+
export default function Products({ lang, products, weekNumber, loading, sendPlausibleGoal, otherResultsRef, isOtherResultsEmpty, }) {
|
|
14
|
+
const filteredProducts = Utils.filterProducts(products, weekNumber);
|
|
15
|
+
const noProduct = !loading && !filteredProducts.length;
|
|
16
|
+
const scrollToOtherResults = () => {
|
|
17
|
+
if (otherResultsRef.current) {
|
|
18
|
+
otherResultsRef.current.scrollIntoView({
|
|
19
|
+
behavior: "smooth",
|
|
20
|
+
block: "start",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const createRipple = (e) => {
|
|
25
|
+
Ripple.create(e);
|
|
26
|
+
};
|
|
27
|
+
return (_jsxs(_Fragment, { children: [noProduct && (_jsxs("div", { className: css.noProduct, children: [_jsx(NoProduct, { className: css.noProductIcon }), message(lang, "noProducts")] })), loading &&
|
|
28
|
+
PLACEHOLDERS.map((_, i) => {
|
|
29
|
+
return (_jsx("div", { className: css.loaderContainer, children: _jsx(Loader, { className: css.loader, variant: "overlay" }) }, i));
|
|
30
|
+
}), !loading &&
|
|
31
|
+
filteredProducts.map((product) => {
|
|
32
|
+
const { id, pageTitle, title, parentTitle, image = "", imagePlaceholder, price, time, text, path, } = product.document;
|
|
33
|
+
const decodedPath = decodeURIComponent(path);
|
|
34
|
+
const productPageTitle = Utils.getHighlight("pageTitle", product.highlight) || pageTitle;
|
|
35
|
+
const productTitle = Utils.getHighlight("title", product.highlight) || title;
|
|
36
|
+
const productTime = Utils.getHighlight("time", product.highlight) || time;
|
|
37
|
+
const productText = Utils.getHighlight("text", product.highlight) || text;
|
|
38
|
+
const showPrice = price !== undefined && weekNumber !== null;
|
|
39
|
+
const imagePlaceholderStategy = imagePlaceholder ? "blur" : "empty";
|
|
40
|
+
return (
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
_jsxs(Link, { className: css.product, href: decodedPath, onClick: sendPlausibleGoal(decodedPath), onMouseDown: createRipple, children: [_jsx(Image, { className: css.productImage, src: image, alt: title, width: 220, height: 180, placeholder: imagePlaceholderStategy, blurDataURL: imagePlaceholder }), _jsxs("div", { className: css.productContent, children: [parentTitle && (_jsx("div", { className: css.productParent, dangerouslySetInnerHTML: { __html: parentTitle } })), _jsx("div", { className: css.productPageTitle, dangerouslySetInnerHTML: { __html: productPageTitle } }), _jsx("div", { className: css.productTitle, dangerouslySetInnerHTML: { __html: productTitle } }), _jsx("div", { className: css.productTime, dangerouslySetInnerHTML: { __html: productTime } }), _jsx("div", { className: css.productText, dangerouslySetInnerHTML: { __html: productText } }), showPrice && (_jsx("div", { className: css.productPrice, dangerouslySetInnerHTML: { __html: price } }))] })] }, id));
|
|
43
|
+
}), !noProduct && !isOtherResultsEmpty && (_jsx("div", { className: css.otherProducts, children: _jsxs(Button, { className: css.otherProductsButton, onClick: scrollToOtherResults, children: [message(lang, "showOtherResults"), _jsx(Icons.ArrowDown, {})] }) }))] }));
|
|
44
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
.noProduct {
|
|
2
|
+
grid-column: 1 / -1;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
gap: 18px;
|
|
8
|
+
color: var(--ds-grey-500, #484848);
|
|
9
|
+
text-align: center;
|
|
10
|
+
font-size: 17px;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.noProductIcon {
|
|
14
|
+
flex: 0 0 140px;
|
|
15
|
+
width: 140px;
|
|
16
|
+
height: 140px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.loaderContainer {
|
|
20
|
+
position: relative;
|
|
21
|
+
width: 100%;
|
|
22
|
+
height: 290px;
|
|
23
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.loader {
|
|
27
|
+
background: linear-gradient(
|
|
28
|
+
-45deg,
|
|
29
|
+
var(--ds-grey-300, #d4d4d4),
|
|
30
|
+
var(--ds-grey-400, #d2d2d2),
|
|
31
|
+
var(--ds-grey-200, #efefef),
|
|
32
|
+
var(--ds-grey-400, #d2d2d2)
|
|
33
|
+
);
|
|
34
|
+
background-size: 400% 400%;
|
|
35
|
+
background-position: 0% 50%;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.product {
|
|
39
|
+
position: relative;
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
color: var(--ds-grey-500, #484848);
|
|
43
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
44
|
+
background-color: var(--ds-grey-000, #fff);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.product mark {
|
|
48
|
+
position: relative;
|
|
49
|
+
display: inline-block;
|
|
50
|
+
font-weight: 700;
|
|
51
|
+
color: var(--ds-secondary, var(--ds-blue-400, #4177f6));
|
|
52
|
+
background-color: transparent;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.product mark::before {
|
|
56
|
+
content: "";
|
|
57
|
+
position: absolute;
|
|
58
|
+
top: 0;
|
|
59
|
+
left: 0;
|
|
60
|
+
width: 100%;
|
|
61
|
+
height: 100%;
|
|
62
|
+
background-color: var(--ds-secondary, var(--ds-blue-400, #4177f6));
|
|
63
|
+
border-radius: calc(var(--ds-radius-100, 6px) / 3);
|
|
64
|
+
opacity: 0.15;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.productImage {
|
|
68
|
+
width: 100%;
|
|
69
|
+
height: 180px;
|
|
70
|
+
object-fit: cover;
|
|
71
|
+
border-radius: var(--ds-radius-100, 6px) var(--ds-radius-100, 6px) 0 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.productContent {
|
|
75
|
+
flex: 1 1 auto;
|
|
76
|
+
display: flex;
|
|
77
|
+
flex-direction: column;
|
|
78
|
+
gap: 6px;
|
|
79
|
+
padding: 8px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.productParent {
|
|
83
|
+
text-transform: uppercase;
|
|
84
|
+
font-size: 10px;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.productPageTitle {
|
|
88
|
+
font-weight: 700;
|
|
89
|
+
font-size: 14px;
|
|
90
|
+
line-height: 1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.productTitle {
|
|
94
|
+
font-weight: 700;
|
|
95
|
+
font-size: 13px;
|
|
96
|
+
line-height: 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.productText {
|
|
100
|
+
font-size: 11px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.productPrice {
|
|
104
|
+
position: absolute;
|
|
105
|
+
top: 6px;
|
|
106
|
+
left: 6px;
|
|
107
|
+
padding: 4px;
|
|
108
|
+
color: var(--ds-grey-100, #f5f5f5);
|
|
109
|
+
background-color: var(--ds-secondary, var(--ds-blue-400, #4177f6));
|
|
110
|
+
font-weight: 700;
|
|
111
|
+
font-size: 12px;
|
|
112
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.productTime {
|
|
116
|
+
font-size: 8px;
|
|
117
|
+
text-transform: uppercase;
|
|
118
|
+
font-weight: 700;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.otherProducts {
|
|
122
|
+
grid-column: 1 / -1;
|
|
123
|
+
position: sticky;
|
|
124
|
+
bottom: 16px;
|
|
125
|
+
display: flex;
|
|
126
|
+
justify-content: center;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@media (min-width: 730px) {
|
|
130
|
+
.otherProducts {
|
|
131
|
+
display: none;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
declare type Document = {
|
|
3
|
+
id: string;
|
|
4
|
+
image?: string;
|
|
5
|
+
imagePlaceholder?: string;
|
|
6
|
+
lang: string;
|
|
7
|
+
pageTitle?: string;
|
|
8
|
+
parentTitle?: string;
|
|
9
|
+
path: string;
|
|
10
|
+
price?: string;
|
|
11
|
+
site: string;
|
|
12
|
+
text?: string;
|
|
13
|
+
time?: string;
|
|
14
|
+
title: string;
|
|
15
|
+
type: "page" | "product" | "information" | "faq";
|
|
16
|
+
weeks?: number[];
|
|
17
|
+
shortTitle?: string;
|
|
18
|
+
seoKeywords?: string[];
|
|
19
|
+
seoTitle?: string;
|
|
20
|
+
};
|
|
21
|
+
declare type Text = {
|
|
22
|
+
matched_tokens: string[];
|
|
23
|
+
snippet: string;
|
|
24
|
+
};
|
|
25
|
+
export declare type PurpleHighlight = {
|
|
26
|
+
text?: Text;
|
|
27
|
+
title?: Text;
|
|
28
|
+
};
|
|
29
|
+
declare type HighlightElement = {
|
|
30
|
+
field: string;
|
|
31
|
+
matched_tokens: string[];
|
|
32
|
+
snippet: string;
|
|
33
|
+
};
|
|
34
|
+
declare type TextMatchInfo = {
|
|
35
|
+
best_field_score: string;
|
|
36
|
+
best_field_weight: number;
|
|
37
|
+
fields_matched: number;
|
|
38
|
+
score: string;
|
|
39
|
+
tokens_matched: number;
|
|
40
|
+
};
|
|
41
|
+
export declare type Hit = {
|
|
42
|
+
document: Document;
|
|
43
|
+
highlight: PurpleHighlight;
|
|
44
|
+
highlights: HighlightElement[];
|
|
45
|
+
text_match: number;
|
|
46
|
+
text_match_info: TextMatchInfo;
|
|
47
|
+
};
|
|
48
|
+
declare type GroupedHit = {
|
|
49
|
+
group_key: string[];
|
|
50
|
+
hits: Hit[];
|
|
51
|
+
};
|
|
52
|
+
declare type RequestParams = {
|
|
53
|
+
collection_name: string;
|
|
54
|
+
per_page: number;
|
|
55
|
+
q: string;
|
|
56
|
+
};
|
|
57
|
+
declare type Props = {
|
|
58
|
+
lang: string;
|
|
59
|
+
text: string;
|
|
60
|
+
results: {
|
|
61
|
+
facet_counts?: any[];
|
|
62
|
+
found?: number;
|
|
63
|
+
grouped_hits?: GroupedHit[];
|
|
64
|
+
out_of?: number;
|
|
65
|
+
page?: number;
|
|
66
|
+
request_params?: RequestParams;
|
|
67
|
+
search_cutoff?: boolean;
|
|
68
|
+
search_time_ms?: number;
|
|
69
|
+
};
|
|
70
|
+
weekNumber: number | null;
|
|
71
|
+
openPeriodPicker: () => void;
|
|
72
|
+
sendPlausibleGoal: (path: string) => void;
|
|
73
|
+
loading: boolean;
|
|
74
|
+
};
|
|
75
|
+
export default function Results({ lang, text, results, weekNumber, openPeriodPicker, loading, sendPlausibleGoal, }: Props): JSX.Element;
|
|
76
|
+
export {};
|
|
77
|
+
//# sourceMappingURL=results.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../../../src/common/components/instant-search/results.tsx"],"names":[],"mappings":";AAaA,aAAK,QAAQ,GAAG;IACd,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,KAAK,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,aAAK,IAAI,GAAG;IACV,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,oBAAY,eAAe,GAAG;IAC5B,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,KAAK,CAAC,EAAE,IAAI,CAAC;CACd,CAAC;AAEF,aAAK,gBAAgB,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,aAAK,aAAa,GAAG;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,oBAAY,GAAG,GAAG;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,eAAe,CAAC;IAC3B,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,aAAa,CAAC;CAChC,CAAC;AAEF,aAAK,UAAU,GAAG;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,IAAI,EAAE,GAAG,EAAE,CAAC;CACb,CAAC;AAEF,aAAK,aAAa,GAAG;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAEF,aAAK,KAAK,GAAG;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QACP,YAAY,CAAC,EAAE,GAAG,EAAE,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,UAAU,EAAE,CAAC;QAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,cAAc,CAAC,EAAE,aAAa,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,EAC9B,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,OAAO,EACP,iBAAiB,GAClB,EAAE,KAAK,eA0HP"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import getConfig from "next/config";
|
|
4
|
+
import Button from "dt-design-system/es/button";
|
|
5
|
+
import * as Icons from "dt-design-system/es/icons";
|
|
6
|
+
import Links from "./links";
|
|
7
|
+
import Products from "./products";
|
|
8
|
+
import * as Dates from "./services/dates";
|
|
9
|
+
import message from "./services/messages";
|
|
10
|
+
import css from "./results.module.css";
|
|
11
|
+
const { publicRuntimeConfig } = getConfig();
|
|
12
|
+
const { season } = publicRuntimeConfig;
|
|
13
|
+
export default function Results({ lang, text, results, weekNumber, openPeriodPicker, loading, sendPlausibleGoal, }) {
|
|
14
|
+
const ref = React.useRef(null);
|
|
15
|
+
const { grouped_hits: groupedHits = [] } = results || {};
|
|
16
|
+
const flattenedHits = groupedHits.reduce((acc, group) => {
|
|
17
|
+
return [...acc, ...group.hits];
|
|
18
|
+
}, []);
|
|
19
|
+
const sortedHits = flattenedHits.reduce((acc, hit) => {
|
|
20
|
+
const { type } = hit.document;
|
|
21
|
+
return { ...acc, [type]: [...acc[type], hit] };
|
|
22
|
+
}, { page: [], product: [], information: [], faq: [], instructor: [] });
|
|
23
|
+
const { page: pages, product: products, information, faq: faqs, instructor: instructors, } = sortedHits;
|
|
24
|
+
const filteredFaqs = faqs.reduce((acc, faq) => {
|
|
25
|
+
const isAlreadyInAcc = acc.some((item) => {
|
|
26
|
+
const hasSameTitle = item.document.title === faq.document.title;
|
|
27
|
+
const hasSameParentTitle = item.document.parentTitle === faq.document.parentTitle;
|
|
28
|
+
return hasSameTitle && hasSameParentTitle;
|
|
29
|
+
});
|
|
30
|
+
if (isAlreadyInAcc)
|
|
31
|
+
return acc;
|
|
32
|
+
return [...acc, faq];
|
|
33
|
+
}, []);
|
|
34
|
+
const weekSystemEnabled = Boolean(season);
|
|
35
|
+
const showProductsTitle = weekNumber !== null;
|
|
36
|
+
const isOtherResultsEmpty = !(pages.length ||
|
|
37
|
+
instructors.length ||
|
|
38
|
+
filteredFaqs.length ||
|
|
39
|
+
information.length);
|
|
40
|
+
return (_jsx("div", { className: css.results, children: (loading || text.length > 0) && (_jsxs("div", { className: css.inner, children: [_jsxs("div", { ref: ref, className: css.left, children: [_jsx(Links, { linksTitle: message(lang, "pages"), icon: "FileText", links: pages, loading: loading, sendPlausibleGoal: sendPlausibleGoal }), _jsx(Links, { linksTitle: message(lang, "instructors"), icon: "User", links: instructors, loading: loading, sendPlausibleGoal: sendPlausibleGoal }), _jsx(Links, { linksTitle: message(lang, "faqs"), icon: "Question", links: filteredFaqs, loading: loading, sendPlausibleGoal: sendPlausibleGoal }), _jsx(Links, { linksTitle: message(lang, "information"), icon: "Info", links: information, loading: loading, sendPlausibleGoal: sendPlausibleGoal, pageTitleAsTitle: true })] }), _jsxs("div", { className: css.right, children: [!loading && products.length > 0 && weekSystemEnabled && (_jsx(_Fragment, { children: showProductsTitle ? (_jsxs("div", { className: css.resultsWithWeekTitle, children: [message(lang, "productTitleWithWeek"), Dates.weekToLongDate(weekNumber, season.end, season.forceSeasonSwitch), _jsxs(Button, { onClick: openPeriodPicker, children: [_jsx(Icons.Calendar, {}), message(lang, "updateWeek")] })] })) : (_jsxs("div", { className: css.resultsWithoutWeekTitle, children: [message(lang, "productTitleWithoutWeek"), _jsxs(Button, { onClick: openPeriodPicker, children: [_jsx(Icons.Calendar, {}), message(lang, "chooseWeek")] })] })) })), _jsx(Products, { lang: lang, products: products, weekNumber: weekNumber, loading: loading, sendPlausibleGoal: sendPlausibleGoal, otherResultsRef: ref, isOtherResultsEmpty: isOtherResultsEmpty })] })] })) }));
|
|
41
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
.results {
|
|
2
|
+
width: calc(100% - 20px);
|
|
3
|
+
height: 600px;
|
|
4
|
+
max-height: 70vh;
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
margin: 0 10px 10px 10px;
|
|
8
|
+
padding: 10px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.results:not(:empty) {
|
|
12
|
+
padding: 0;
|
|
13
|
+
background-color: var(--ds-grey-000, #fff);
|
|
14
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
15
|
+
box-shadow: var(--ds-shadow-200, 0 5px 10px rgba(0, 0, 0, 0.12));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.inner {
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 100%;
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
24
|
+
overflow: auto;
|
|
25
|
+
overscroll-behavior: contain;
|
|
26
|
+
-webkit-overflow-scrolling: touch;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@media (min-width: 730px) {
|
|
30
|
+
.inner {
|
|
31
|
+
flex-direction: row;
|
|
32
|
+
overflow: visible;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.left {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: column;
|
|
39
|
+
gap: 10px;
|
|
40
|
+
padding: 16px;
|
|
41
|
+
order: 1;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.left:empty {
|
|
45
|
+
display: none;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@media (min-width: 730px) {
|
|
49
|
+
.left {
|
|
50
|
+
flex: 0 0 330px;
|
|
51
|
+
order: 0;
|
|
52
|
+
overflow: auto;
|
|
53
|
+
overscroll-behavior: contain;
|
|
54
|
+
-webkit-overflow-scrolling: touch;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.right {
|
|
59
|
+
position: relative;
|
|
60
|
+
flex: 1 1 auto;
|
|
61
|
+
display: grid;
|
|
62
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
63
|
+
align-content: flex-start;
|
|
64
|
+
background-color: var(--ds-grey-100, #f5f5f5);
|
|
65
|
+
border-radius: var(--ds-radius-100, 6px) var(--ds-radius-100, 6px) 0 0;
|
|
66
|
+
gap: 16px;
|
|
67
|
+
padding: 16px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@media (min-width: 730px) {
|
|
71
|
+
.right {
|
|
72
|
+
border-radius: 0 var(--ds-radius-100, 6px) var(--ds-radius-100, 6px) 0;
|
|
73
|
+
overflow: auto;
|
|
74
|
+
overscroll-behavior: contain;
|
|
75
|
+
-webkit-overflow-scrolling: touch;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.resultsWithWeekTitle,
|
|
80
|
+
.resultsWithoutWeekTitle {
|
|
81
|
+
--ds-button-text-transform: none;
|
|
82
|
+
|
|
83
|
+
grid-column: 1 / -1;
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
justify-content: center;
|
|
87
|
+
flex-wrap: wrap;
|
|
88
|
+
gap: 10px;
|
|
89
|
+
padding: 12px 8px;
|
|
90
|
+
text-align: center;
|
|
91
|
+
font-size: 15px;
|
|
92
|
+
background-color: var(--ds-grey-000, #fff);
|
|
93
|
+
border-radius: var(--ds-radius-100, 6px);
|
|
94
|
+
z-index: 1;
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../../src/common/components/instant-search/services/api.ts"],"names":[],"mappings":"AAKA,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,UAAU,GAAE,MAAU,EACtB,OAAO,GAAE,MAAgE,EACzE,cAAc,CAAC,EAAE,MAAM,OAcxB"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// import * as Fetcher from "ublo-lib/es/common/utils/fetcher";
|
|
2
|
+
import * as Fetcher from "../../../utils/fetcher";
|
|
3
|
+
const api = "https://search.ublo.app/api";
|
|
4
|
+
export function fetchResults(site, lang, query, groupLimit = 8, queryBy = "title,text,pageTitle,seoKeywords,parentTitle,seoTitle", queryByWeights) {
|
|
5
|
+
const endpoint = `${api}/search`;
|
|
6
|
+
return Fetcher.post(endpoint, {
|
|
7
|
+
q: query,
|
|
8
|
+
query_by: queryBy,
|
|
9
|
+
query_by_weights: queryByWeights,
|
|
10
|
+
group_limit: groupLimit,
|
|
11
|
+
per_page: 30,
|
|
12
|
+
}, { site, lang });
|
|
13
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
function padStart(value, count, pad) {
|
|
2
|
+
let str = String(value);
|
|
3
|
+
while (str.length < count) {
|
|
4
|
+
str = pad + str;
|
|
5
|
+
}
|
|
6
|
+
return str;
|
|
7
|
+
}
|
|
8
|
+
function createDate(...args) {
|
|
9
|
+
const ref = new Date(...args);
|
|
10
|
+
const yy = ref.getFullYear();
|
|
11
|
+
const mm = padStart(ref.getMonth() + 1, 2, 0);
|
|
12
|
+
const dd = padStart(ref.getDate(), 2, 0);
|
|
13
|
+
const str = `${yy}-${mm}-${dd}`;
|
|
14
|
+
return new Date(str);
|
|
15
|
+
}
|
|
16
|
+
function getPrevSaturday(date) {
|
|
17
|
+
const clone = createDate(date);
|
|
18
|
+
const day = 1 + clone.getDay();
|
|
19
|
+
const prevSaturday = clone.getDate() - day % 7;
|
|
20
|
+
clone.setDate(prevSaturday);
|
|
21
|
+
return clone;
|
|
22
|
+
}
|
|
23
|
+
function addDays(date, days) {
|
|
24
|
+
const clone = createDate(date);
|
|
25
|
+
clone.setDate(clone.getDate() + days);
|
|
26
|
+
return clone;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function christmasWeek(endWeek, forceSeasonSwitch) {
|
|
30
|
+
const now = createDate();
|
|
31
|
+
const year = now.getFullYear();
|
|
32
|
+
const month = now.getMonth();
|
|
33
|
+
const date = now.getDate();
|
|
34
|
+
const isAfterChristmas = month === 11 && date >= 25;
|
|
35
|
+
const lastChristmasYear = isAfterChristmas || !isAfterChristmas && forceSeasonSwitch ? year : year - 1;
|
|
36
|
+
const lastChristmas = createDate(lastChristmasYear, 11, 25);
|
|
37
|
+
const nextChristmas = createDate(lastChristmasYear + 1, 11, 25);
|
|
38
|
+
const dateSeasonEnd = addDays(getPrevSaturday(lastChristmas), endWeek * 7);
|
|
39
|
+
return getPrevSaturday(now <= dateSeasonEnd ? lastChristmas : nextChristmas);
|
|
40
|
+
}
|
|
41
|
+
function weekToDate(week, endWeek, forceSeasonSwitch) {
|
|
42
|
+
return addDays(christmasWeek(endWeek, forceSeasonSwitch), week * 7);
|
|
43
|
+
}
|
|
44
|
+
export function weekToLongDate(week, endWeek, forceSeasonSwitch) {
|
|
45
|
+
const date = weekToDate(week, endWeek, forceSeasonSwitch);
|
|
46
|
+
const dd = padStart(date.getDate(), 2, 0);
|
|
47
|
+
const mm = padStart(date.getMonth() + 1, 2, 0);
|
|
48
|
+
const yy = date.getFullYear();
|
|
49
|
+
return `${dd}/${mm}/${yy}`;
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../../../src/common/components/instant-search/services/messages.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAGvD"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Hit, PurpleHighlight } from "../results";
|
|
2
|
+
export declare function getHighlight(property: string, highlight: PurpleHighlight, withExtractQuotes?: boolean): any;
|
|
3
|
+
export declare function filterProducts(products: Hit[], weekNumber: number | null): Hit[];
|
|
4
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../src/common/components/instant-search/services/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAEvD,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,eAAe,EAC1B,iBAAiB,GAAE,OAAe,OAoBnC;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,SAkBxE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function getHighlight(property, highlight, withExtractQuotes = false) {
|
|
2
|
+
if (!highlight[property]?.snippet)
|
|
3
|
+
return;
|
|
4
|
+
let snippet = highlight[property].snippet;
|
|
5
|
+
if (!withExtractQuotes)
|
|
6
|
+
return snippet;
|
|
7
|
+
const startsWithMaj = /[A-Z]/.test(snippet);
|
|
8
|
+
const endsWithDot = snippet[snippet.length - 1] === ".";
|
|
9
|
+
if (!startsWithMaj) {
|
|
10
|
+
snippet = `...${snippet}`;
|
|
11
|
+
}
|
|
12
|
+
if (!endsWithDot) {
|
|
13
|
+
snippet = `${snippet}...`;
|
|
14
|
+
}
|
|
15
|
+
return snippet;
|
|
16
|
+
}
|
|
17
|
+
export function filterProducts(products, weekNumber) {
|
|
18
|
+
if (weekNumber !== null) {
|
|
19
|
+
return products.filter((product) => {
|
|
20
|
+
const { weeks = [] } = product.document;
|
|
21
|
+
return weeks.includes(weekNumber);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return products.reduce((acc, product) => {
|
|
25
|
+
const { image, pageTitle } = product.document;
|
|
26
|
+
const isAlreadyInAcc = acc.some((item) => {
|
|
27
|
+
return (normalizeImage(item.document.image) === normalizeImage(image) &&
|
|
28
|
+
item.document.pageTitle === pageTitle);
|
|
29
|
+
});
|
|
30
|
+
if (isAlreadyInAcc)
|
|
31
|
+
return acc;
|
|
32
|
+
return [...acc, product];
|
|
33
|
+
}, []);
|
|
34
|
+
}
|
|
35
|
+
function normalizeImage(image) {
|
|
36
|
+
if (!image)
|
|
37
|
+
return "";
|
|
38
|
+
return image.replace(/(-[0-9]+)?.(jpg|jpeg|png|gif)/, "");
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ublo-lib",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"dt-design-system": "^2.
|
|
5
|
+
"dt-design-system": "^2.9.0",
|
|
6
6
|
"leaflet": "^1.9.1",
|
|
7
7
|
"next": "^12.0.0 || ^13.0.0",
|
|
8
8
|
"react": "^18.2.0",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@types/react-dom": "^18.0.6",
|
|
39
39
|
"classnames": "^2.3.1",
|
|
40
40
|
"cpx2": "^4.2.0",
|
|
41
|
-
"dt-design-system": "^2.
|
|
41
|
+
"dt-design-system": "^2.9.0",
|
|
42
42
|
"eslint": "^8.23.0",
|
|
43
43
|
"eslint-config-prettier": "^8.5.0",
|
|
44
44
|
"eslint-config-standard": "^17.0.0",
|