@hotelcard/ui 0.0.15 → 0.0.16
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.
Potentially problematic release.
This version of @hotelcard/ui might be problematic. Click here for more details.
- package/dist/index.cjs +906 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +539 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +207 -1
- package/dist/index.d.ts +207 -1
- package/dist/index.js +882 -17
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -24,31 +34,45 @@ __export(index_exports, {
|
|
|
24
34
|
Benefits: () => Benefits,
|
|
25
35
|
Block: () => Block,
|
|
26
36
|
Button: () => Button,
|
|
37
|
+
CATEGORY_OPTIONS: () => CATEGORY_OPTIONS,
|
|
27
38
|
Card: () => Card,
|
|
28
39
|
Checkbox: () => Checkbox,
|
|
40
|
+
CheckboxFilter: () => CheckboxFilter,
|
|
29
41
|
ChevronLeftIcon: () => ChevronLeftIcon,
|
|
30
42
|
ChevronRightIcon: () => ChevronRightIcon2,
|
|
31
43
|
Chip: () => Chip,
|
|
44
|
+
CollapsibleFilterSection: () => CollapsibleFilterSection,
|
|
32
45
|
DateSelector: () => DateSelector,
|
|
33
46
|
Divider: () => Divider,
|
|
34
47
|
Dropdown: () => Dropdown,
|
|
35
48
|
DualCalendar: () => DualCalendar,
|
|
49
|
+
ExperienceFilter: () => ExperienceFilter,
|
|
36
50
|
FAQ: () => FAQ,
|
|
51
|
+
FilterCheckboxItem: () => FilterCheckboxItem,
|
|
37
52
|
GuestContent: () => GuestContent,
|
|
38
53
|
HeartIcon: () => HeartIcon3,
|
|
39
54
|
HotelCard: () => HotelCard,
|
|
40
55
|
HotelCardContent: () => HotelCardContent,
|
|
41
56
|
HotelCardImage: () => HotelCardImage,
|
|
42
57
|
HotelCardUIProvider: () => HotelCardUIProvider,
|
|
58
|
+
HotelCategoryFilter: () => HotelCategoryFilter,
|
|
43
59
|
Input: () => Input,
|
|
60
|
+
MealsFilter: () => MealsFilter,
|
|
44
61
|
Modal: () => Modal,
|
|
45
62
|
Pin: () => Pin,
|
|
46
63
|
PinIcon: () => PinIcon2,
|
|
64
|
+
PriceRangeFilter: () => PriceRangeFilter,
|
|
65
|
+
REVIEW_OPTIONS: () => REVIEW_OPTIONS,
|
|
47
66
|
RadioButton: () => RadioButton,
|
|
48
67
|
Rating: () => Rating,
|
|
68
|
+
RegionsFilter: () => RegionsFilter,
|
|
49
69
|
ReviewCard: () => ReviewCard,
|
|
70
|
+
ReviewsFilter: () => ReviewsFilter,
|
|
50
71
|
SectionHeader: () => SectionHeader,
|
|
72
|
+
SelectedFiltersRow: () => SelectedFiltersRow,
|
|
51
73
|
StarIcon: () => StarIcon4,
|
|
74
|
+
TransportFilter: () => TransportFilter,
|
|
75
|
+
WellnessFilter: () => WellnessFilter,
|
|
52
76
|
WhenContent: () => WhenContent,
|
|
53
77
|
calculateDiscount: () => calculateDiscount,
|
|
54
78
|
formatDate: () => formatDate,
|
|
@@ -2973,9 +2997,860 @@ var HotelCard = ({
|
|
|
2973
2997
|
] });
|
|
2974
2998
|
};
|
|
2975
2999
|
|
|
2976
|
-
// src/components/
|
|
3000
|
+
// src/components/Filters/FilterCheckboxItem.module.css
|
|
3001
|
+
var FilterCheckboxItem_default = {};
|
|
3002
|
+
|
|
3003
|
+
// src/components/Filters/FilterCheckboxItem.tsx
|
|
2977
3004
|
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
2978
|
-
var
|
|
3005
|
+
var FilterCheckboxItem = ({
|
|
3006
|
+
id,
|
|
3007
|
+
label,
|
|
3008
|
+
count,
|
|
3009
|
+
checked,
|
|
3010
|
+
disabled = false,
|
|
3011
|
+
onChange,
|
|
3012
|
+
className = "",
|
|
3013
|
+
trackName
|
|
3014
|
+
}) => {
|
|
3015
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(
|
|
3016
|
+
"label",
|
|
3017
|
+
{
|
|
3018
|
+
className: `${FilterCheckboxItem_default.filterRow} ${disabled ? FilterCheckboxItem_default.filterRowDisabled : ""} ${className}`,
|
|
3019
|
+
tabIndex: disabled ? -1 : 0,
|
|
3020
|
+
"data-track": trackName,
|
|
3021
|
+
children: [
|
|
3022
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: FilterCheckboxItem_default.checkboxLabel, children: [
|
|
3023
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: FilterCheckboxItem_default.checkboxButton, children: [
|
|
3024
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
3025
|
+
"input",
|
|
3026
|
+
{
|
|
3027
|
+
id,
|
|
3028
|
+
type: "checkbox",
|
|
3029
|
+
className: FilterCheckboxItem_default.checkbox,
|
|
3030
|
+
checked,
|
|
3031
|
+
disabled,
|
|
3032
|
+
onChange
|
|
3033
|
+
}
|
|
3034
|
+
),
|
|
3035
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: `${FilterCheckboxItem_default.checkboxBox} ${disabled ? FilterCheckboxItem_default.checkboxBoxDisabled : ""}`, children: checked && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("svg", { viewBox: "0 0 12 10", fill: "none", className: FilterCheckboxItem_default.checkIcon, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
3036
|
+
"path",
|
|
3037
|
+
{
|
|
3038
|
+
d: "M1 5L4.5 8.5L11 1",
|
|
3039
|
+
stroke: "currentColor",
|
|
3040
|
+
strokeWidth: "2",
|
|
3041
|
+
strokeLinecap: "round",
|
|
3042
|
+
strokeLinejoin: "round"
|
|
3043
|
+
}
|
|
3044
|
+
) }) })
|
|
3045
|
+
] }),
|
|
3046
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: `${FilterCheckboxItem_default.filterLabel} ${disabled ? FilterCheckboxItem_default.filterLabelDisabled : ""}`, children: label })
|
|
3047
|
+
] }),
|
|
3048
|
+
count !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: `${FilterCheckboxItem_default.filterCount} ${disabled ? FilterCheckboxItem_default.filterCountDisabled : ""}`, children: count })
|
|
3049
|
+
]
|
|
3050
|
+
}
|
|
3051
|
+
);
|
|
3052
|
+
};
|
|
3053
|
+
|
|
3054
|
+
// src/components/Filters/CollapsibleFilterSection.tsx
|
|
3055
|
+
var import_react15 = __toESM(require("react"), 1);
|
|
3056
|
+
|
|
3057
|
+
// src/components/Filters/CollapsibleFilterSection.module.css
|
|
3058
|
+
var CollapsibleFilterSection_default = {};
|
|
3059
|
+
|
|
3060
|
+
// src/components/Filters/CollapsibleFilterSection.tsx
|
|
3061
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
3062
|
+
var CollapsibleFilterSection = ({
|
|
3063
|
+
title,
|
|
3064
|
+
children,
|
|
3065
|
+
defaultExpanded = true,
|
|
3066
|
+
showAllText,
|
|
3067
|
+
showLessText,
|
|
3068
|
+
hasShowAll = false,
|
|
3069
|
+
initialItemsToShow = 5,
|
|
3070
|
+
className = ""
|
|
3071
|
+
}) => {
|
|
3072
|
+
const { t } = useUIContext();
|
|
3073
|
+
const [isExpanded, setIsExpanded] = (0, import_react15.useState)(defaultExpanded);
|
|
3074
|
+
const [showAll, setShowAll] = (0, import_react15.useState)(false);
|
|
3075
|
+
const resolvedShowAllText = showAllText ?? t("filter.show-all", "Show all");
|
|
3076
|
+
const resolvedShowLessText = showLessText ?? t("filter.show-less", "Show less");
|
|
3077
|
+
(0, import_react15.useEffect)(() => {
|
|
3078
|
+
if (defaultExpanded && !isExpanded) {
|
|
3079
|
+
setIsExpanded(true);
|
|
3080
|
+
}
|
|
3081
|
+
}, [defaultExpanded]);
|
|
3082
|
+
const toggleExpanded = () => {
|
|
3083
|
+
setIsExpanded(!isExpanded);
|
|
3084
|
+
};
|
|
3085
|
+
const toggleShowAll = () => {
|
|
3086
|
+
setShowAll(!showAll);
|
|
3087
|
+
};
|
|
3088
|
+
const childrenArray = import_react15.default.Children.toArray(children);
|
|
3089
|
+
const shouldShowToggle = hasShowAll && childrenArray.length > initialItemsToShow;
|
|
3090
|
+
const displayedChildren = shouldShowToggle && !showAll ? childrenArray.slice(0, initialItemsToShow) : childrenArray;
|
|
3091
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: `${CollapsibleFilterSection_default.section} ${className}`, children: [
|
|
3092
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
3093
|
+
"button",
|
|
3094
|
+
{
|
|
3095
|
+
type: "button",
|
|
3096
|
+
className: CollapsibleFilterSection_default.header,
|
|
3097
|
+
onClick: toggleExpanded,
|
|
3098
|
+
"aria-expanded": isExpanded,
|
|
3099
|
+
children: [
|
|
3100
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: CollapsibleFilterSection_default.title, children: title }),
|
|
3101
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
3102
|
+
"svg",
|
|
3103
|
+
{
|
|
3104
|
+
className: `${CollapsibleFilterSection_default.chevron} ${isExpanded ? CollapsibleFilterSection_default.chevronExpanded : ""}`,
|
|
3105
|
+
viewBox: "0 0 24 24",
|
|
3106
|
+
fill: "none",
|
|
3107
|
+
"aria-hidden": "true",
|
|
3108
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
3109
|
+
"path",
|
|
3110
|
+
{
|
|
3111
|
+
d: "M6 9L12 15L18 9",
|
|
3112
|
+
stroke: "currentColor",
|
|
3113
|
+
strokeWidth: "2",
|
|
3114
|
+
strokeLinecap: "round",
|
|
3115
|
+
strokeLinejoin: "round"
|
|
3116
|
+
}
|
|
3117
|
+
)
|
|
3118
|
+
}
|
|
3119
|
+
)
|
|
3120
|
+
]
|
|
3121
|
+
}
|
|
3122
|
+
),
|
|
3123
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
3124
|
+
"div",
|
|
3125
|
+
{
|
|
3126
|
+
className: `${CollapsibleFilterSection_default.content} ${isExpanded ? CollapsibleFilterSection_default.contentExpanded : ""}`,
|
|
3127
|
+
"aria-hidden": !isExpanded,
|
|
3128
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: CollapsibleFilterSection_default.contentInner, children: [
|
|
3129
|
+
displayedChildren,
|
|
3130
|
+
shouldShowToggle && /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
3131
|
+
"button",
|
|
3132
|
+
{
|
|
3133
|
+
type: "button",
|
|
3134
|
+
className: CollapsibleFilterSection_default.showAllButton,
|
|
3135
|
+
onClick: toggleShowAll,
|
|
3136
|
+
children: [
|
|
3137
|
+
showAll ? resolvedShowLessText : resolvedShowAllText,
|
|
3138
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
3139
|
+
"svg",
|
|
3140
|
+
{
|
|
3141
|
+
className: `${CollapsibleFilterSection_default.showAllChevron} ${showAll ? CollapsibleFilterSection_default.showAllChevronUp : ""}`,
|
|
3142
|
+
viewBox: "0 0 24 24",
|
|
3143
|
+
fill: "none",
|
|
3144
|
+
"aria-hidden": "true",
|
|
3145
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
3146
|
+
"path",
|
|
3147
|
+
{
|
|
3148
|
+
d: "M6 9L12 15L18 9",
|
|
3149
|
+
stroke: "currentColor",
|
|
3150
|
+
strokeWidth: "2",
|
|
3151
|
+
strokeLinecap: "round",
|
|
3152
|
+
strokeLinejoin: "round"
|
|
3153
|
+
}
|
|
3154
|
+
)
|
|
3155
|
+
}
|
|
3156
|
+
)
|
|
3157
|
+
]
|
|
3158
|
+
}
|
|
3159
|
+
)
|
|
3160
|
+
] })
|
|
3161
|
+
}
|
|
3162
|
+
)
|
|
3163
|
+
] });
|
|
3164
|
+
};
|
|
3165
|
+
|
|
3166
|
+
// src/components/Filters/PriceRangeFilter.tsx
|
|
3167
|
+
var import_react16 = require("react");
|
|
3168
|
+
|
|
3169
|
+
// src/components/Filters/PriceRangeFilter.module.css
|
|
3170
|
+
var PriceRangeFilter_default = {};
|
|
3171
|
+
|
|
3172
|
+
// src/components/Filters/PriceRangeFilter.tsx
|
|
3173
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
3174
|
+
var DEFAULT_HISTOGRAM = [
|
|
3175
|
+
12,
|
|
3176
|
+
25,
|
|
3177
|
+
38,
|
|
3178
|
+
45,
|
|
3179
|
+
52,
|
|
3180
|
+
48,
|
|
3181
|
+
42,
|
|
3182
|
+
35,
|
|
3183
|
+
28,
|
|
3184
|
+
22,
|
|
3185
|
+
18,
|
|
3186
|
+
15,
|
|
3187
|
+
12,
|
|
3188
|
+
10,
|
|
3189
|
+
8,
|
|
3190
|
+
6,
|
|
3191
|
+
5,
|
|
3192
|
+
4,
|
|
3193
|
+
3,
|
|
3194
|
+
2
|
|
3195
|
+
];
|
|
3196
|
+
var PriceRangeFilter = ({
|
|
3197
|
+
minPrice,
|
|
3198
|
+
maxPrice,
|
|
3199
|
+
value,
|
|
3200
|
+
onChange,
|
|
3201
|
+
onApply,
|
|
3202
|
+
histogram = DEFAULT_HISTOGRAM,
|
|
3203
|
+
currency,
|
|
3204
|
+
onDragStart,
|
|
3205
|
+
onDragEnd,
|
|
3206
|
+
debounceDelay = 500
|
|
3207
|
+
}) => {
|
|
3208
|
+
const { t, currency: contextCurrency } = useUIContext();
|
|
3209
|
+
const displayCurrency = currency ?? contextCurrency;
|
|
3210
|
+
const sliderRef = (0, import_react16.useRef)(null);
|
|
3211
|
+
const [isDragging, setIsDragging] = (0, import_react16.useState)(null);
|
|
3212
|
+
const [isDraggingExternal, setIsDraggingExternal] = (0, import_react16.useState)(false);
|
|
3213
|
+
const lastValueRef = (0, import_react16.useRef)(value);
|
|
3214
|
+
const timeoutRef = (0, import_react16.useRef)(null);
|
|
3215
|
+
const [minInputValue, setMinInputValue] = (0, import_react16.useState)("");
|
|
3216
|
+
const [maxInputValue, setMaxInputValue] = (0, import_react16.useState)("");
|
|
3217
|
+
const [minInputFocused, setMinInputFocused] = (0, import_react16.useState)(false);
|
|
3218
|
+
const [maxInputFocused, setMaxInputFocused] = (0, import_react16.useState)(false);
|
|
3219
|
+
const range = maxPrice - minPrice;
|
|
3220
|
+
const minPercent = range > 0 ? (value.min - minPrice) / range * 100 : 0;
|
|
3221
|
+
const maxPercent = range > 0 ? (value.max - minPrice) / range * 100 : 100;
|
|
3222
|
+
const maxHistogramValue = Math.max(...histogram, 1);
|
|
3223
|
+
const getBarActive = (0, import_react16.useCallback)((index) => {
|
|
3224
|
+
const barStartPercent = index / histogram.length * 100;
|
|
3225
|
+
const barEndPercent = (index + 1) / histogram.length * 100;
|
|
3226
|
+
return barStartPercent < maxPercent && barEndPercent > minPercent;
|
|
3227
|
+
}, [histogram.length, minPercent, maxPercent]);
|
|
3228
|
+
const trackInset = 12;
|
|
3229
|
+
const triggerOnApply = (0, import_react16.useCallback)((newValue) => {
|
|
3230
|
+
if (!onApply) return;
|
|
3231
|
+
if (timeoutRef.current) {
|
|
3232
|
+
clearTimeout(timeoutRef.current);
|
|
3233
|
+
}
|
|
3234
|
+
timeoutRef.current = setTimeout(() => {
|
|
3235
|
+
onApply(newValue);
|
|
3236
|
+
}, debounceDelay);
|
|
3237
|
+
}, [onApply, debounceDelay]);
|
|
3238
|
+
const handleDrag = (0, import_react16.useCallback)((clientX) => {
|
|
3239
|
+
if (!sliderRef.current || !isDragging) return;
|
|
3240
|
+
const rect = sliderRef.current.getBoundingClientRect();
|
|
3241
|
+
const trackWidth = rect.width - trackInset * 2;
|
|
3242
|
+
const trackLeft = rect.left + trackInset;
|
|
3243
|
+
const percent = Math.max(0, Math.min(100, (clientX - trackLeft) / trackWidth * 100));
|
|
3244
|
+
const newValue = Math.round(minPrice + percent / 100 * range);
|
|
3245
|
+
let newMin = value.min;
|
|
3246
|
+
let newMax = value.max;
|
|
3247
|
+
if (isDragging === "min") {
|
|
3248
|
+
const clampedValue = Math.min(newValue, value.max - 1);
|
|
3249
|
+
newMin = Math.max(minPrice, clampedValue);
|
|
3250
|
+
} else {
|
|
3251
|
+
const clampedValue = Math.max(newValue, value.min + 1);
|
|
3252
|
+
newMax = Math.min(maxPrice, clampedValue);
|
|
3253
|
+
}
|
|
3254
|
+
const newRange = { min: newMin, max: newMax };
|
|
3255
|
+
onChange(newRange);
|
|
3256
|
+
lastValueRef.current = newRange;
|
|
3257
|
+
if (!isDraggingExternal) {
|
|
3258
|
+
triggerOnApply(newRange);
|
|
3259
|
+
}
|
|
3260
|
+
}, [isDragging, minPrice, maxPrice, range, value, onChange, isDraggingExternal, triggerOnApply]);
|
|
3261
|
+
const handleMouseDown = (handle) => (e) => {
|
|
3262
|
+
e.preventDefault();
|
|
3263
|
+
setIsDragging(handle);
|
|
3264
|
+
setIsDraggingExternal(true);
|
|
3265
|
+
onDragStart?.();
|
|
3266
|
+
};
|
|
3267
|
+
const handleTouchStart = (handle) => (e) => {
|
|
3268
|
+
e.preventDefault();
|
|
3269
|
+
setIsDragging(handle);
|
|
3270
|
+
setIsDraggingExternal(true);
|
|
3271
|
+
onDragStart?.();
|
|
3272
|
+
};
|
|
3273
|
+
const handleDragEnd = (0, import_react16.useCallback)(() => {
|
|
3274
|
+
if (isDraggingExternal && onApply) {
|
|
3275
|
+
onApply(lastValueRef.current);
|
|
3276
|
+
}
|
|
3277
|
+
setIsDragging(null);
|
|
3278
|
+
setIsDraggingExternal(false);
|
|
3279
|
+
onDragEnd?.();
|
|
3280
|
+
}, [isDraggingExternal, onApply, onDragEnd]);
|
|
3281
|
+
(0, import_react16.useEffect)(() => {
|
|
3282
|
+
if (!isDragging) return;
|
|
3283
|
+
const handleMouseMove = (e) => {
|
|
3284
|
+
handleDrag(e.clientX);
|
|
3285
|
+
};
|
|
3286
|
+
const handleTouchMove = (e) => {
|
|
3287
|
+
if (e.touches.length > 0) {
|
|
3288
|
+
handleDrag(e.touches[0].clientX);
|
|
3289
|
+
}
|
|
3290
|
+
};
|
|
3291
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
3292
|
+
document.addEventListener("mouseup", handleDragEnd);
|
|
3293
|
+
document.addEventListener("touchmove", handleTouchMove, { passive: false });
|
|
3294
|
+
document.addEventListener("touchend", handleDragEnd);
|
|
3295
|
+
return () => {
|
|
3296
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
3297
|
+
document.removeEventListener("mouseup", handleDragEnd);
|
|
3298
|
+
document.removeEventListener("touchmove", handleTouchMove);
|
|
3299
|
+
document.removeEventListener("touchend", handleDragEnd);
|
|
3300
|
+
if (timeoutRef.current) {
|
|
3301
|
+
clearTimeout(timeoutRef.current);
|
|
3302
|
+
}
|
|
3303
|
+
};
|
|
3304
|
+
}, [isDragging, handleDrag, handleDragEnd]);
|
|
3305
|
+
const handleMinInputFocus = () => {
|
|
3306
|
+
setMinInputFocused(true);
|
|
3307
|
+
setMinInputValue(value.min.toString());
|
|
3308
|
+
};
|
|
3309
|
+
const handleMaxInputFocus = () => {
|
|
3310
|
+
setMaxInputFocused(true);
|
|
3311
|
+
setMaxInputValue(value.max.toString());
|
|
3312
|
+
};
|
|
3313
|
+
const handleMinInputChange = (e) => {
|
|
3314
|
+
const val = e.target.value.replace(/[^0-9]/g, "");
|
|
3315
|
+
setMinInputValue(val);
|
|
3316
|
+
};
|
|
3317
|
+
const handleMaxInputChange = (e) => {
|
|
3318
|
+
const val = e.target.value.replace(/[^0-9]/g, "");
|
|
3319
|
+
setMaxInputValue(val);
|
|
3320
|
+
};
|
|
3321
|
+
const handleMinInputBlur = () => {
|
|
3322
|
+
setMinInputFocused(false);
|
|
3323
|
+
const numValue = parseInt(minInputValue, 10);
|
|
3324
|
+
if (!isNaN(numValue)) {
|
|
3325
|
+
const clampedValue = Math.max(minPrice, Math.min(numValue, value.max - 1));
|
|
3326
|
+
const newRange = { min: clampedValue, max: value.max };
|
|
3327
|
+
onChange(newRange);
|
|
3328
|
+
if (onApply) {
|
|
3329
|
+
triggerOnApply(newRange);
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
};
|
|
3333
|
+
const handleMaxInputBlur = () => {
|
|
3334
|
+
setMaxInputFocused(false);
|
|
3335
|
+
const numValue = parseInt(maxInputValue, 10);
|
|
3336
|
+
if (!isNaN(numValue)) {
|
|
3337
|
+
const clampedValue = Math.min(maxPrice, Math.max(numValue, value.min + 1));
|
|
3338
|
+
const newRange = { min: value.min, max: clampedValue };
|
|
3339
|
+
onChange(newRange);
|
|
3340
|
+
if (onApply) {
|
|
3341
|
+
triggerOnApply(newRange);
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
3345
|
+
(0, import_react16.useEffect)(() => {
|
|
3346
|
+
return () => {
|
|
3347
|
+
if (timeoutRef.current) {
|
|
3348
|
+
clearTimeout(timeoutRef.current);
|
|
3349
|
+
}
|
|
3350
|
+
};
|
|
3351
|
+
}, []);
|
|
3352
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: PriceRangeFilter_default.priceRangeWrapper, children: [
|
|
3353
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: PriceRangeFilter_default.priceInputs, children: [
|
|
3354
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: PriceRangeFilter_default.priceInputWrapper, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3355
|
+
"input",
|
|
3356
|
+
{
|
|
3357
|
+
type: "text",
|
|
3358
|
+
inputMode: "numeric",
|
|
3359
|
+
className: PriceRangeFilter_default.priceInput,
|
|
3360
|
+
value: minInputFocused ? minInputValue : `${displayCurrency} ${value.min}`,
|
|
3361
|
+
onChange: handleMinInputChange,
|
|
3362
|
+
onFocus: handleMinInputFocus,
|
|
3363
|
+
onBlur: handleMinInputBlur,
|
|
3364
|
+
"aria-label": t("filter.min-price", "Minimum price")
|
|
3365
|
+
}
|
|
3366
|
+
) }),
|
|
3367
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: PriceRangeFilter_default.priceInputDivider, children: "\u2014" }),
|
|
3368
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: PriceRangeFilter_default.priceInputWrapper, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3369
|
+
"input",
|
|
3370
|
+
{
|
|
3371
|
+
type: "text",
|
|
3372
|
+
inputMode: "numeric",
|
|
3373
|
+
className: PriceRangeFilter_default.priceInput,
|
|
3374
|
+
value: maxInputFocused ? maxInputValue : `${displayCurrency} ${value.max}`,
|
|
3375
|
+
onChange: handleMaxInputChange,
|
|
3376
|
+
onFocus: handleMaxInputFocus,
|
|
3377
|
+
onBlur: handleMaxInputBlur,
|
|
3378
|
+
"aria-label": t("filter.max-price", "Maximum price")
|
|
3379
|
+
}
|
|
3380
|
+
) })
|
|
3381
|
+
] }),
|
|
3382
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: PriceRangeFilter_default.histogram, children: histogram.map((histValue, index) => {
|
|
3383
|
+
const height = histValue / maxHistogramValue * 100;
|
|
3384
|
+
const isActive = getBarActive(index);
|
|
3385
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3386
|
+
"div",
|
|
3387
|
+
{
|
|
3388
|
+
className: `${PriceRangeFilter_default.histogramBar} ${isActive ? PriceRangeFilter_default.histogramBarActive : ""}`,
|
|
3389
|
+
style: { height: `${Math.max(height, 10)}%` }
|
|
3390
|
+
},
|
|
3391
|
+
index
|
|
3392
|
+
);
|
|
3393
|
+
}) }),
|
|
3394
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: PriceRangeFilter_default.sliderContainer, ref: sliderRef, children: [
|
|
3395
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: PriceRangeFilter_default.sliderTrack }),
|
|
3396
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3397
|
+
"div",
|
|
3398
|
+
{
|
|
3399
|
+
className: PriceRangeFilter_default.sliderActiveTrack,
|
|
3400
|
+
style: {
|
|
3401
|
+
left: `calc(12px + (100% - 24px) * ${minPercent / 100})`,
|
|
3402
|
+
width: `calc((100% - 24px) * ${(maxPercent - minPercent) / 100})`
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
),
|
|
3406
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3407
|
+
"div",
|
|
3408
|
+
{
|
|
3409
|
+
className: `${PriceRangeFilter_default.sliderHandle} ${isDragging === "min" ? PriceRangeFilter_default.sliderHandleActive : ""}`,
|
|
3410
|
+
style: { left: `calc(12px + (100% - 24px) * ${minPercent / 100})` },
|
|
3411
|
+
onMouseDown: handleMouseDown("min"),
|
|
3412
|
+
onTouchStart: handleTouchStart("min"),
|
|
3413
|
+
role: "slider",
|
|
3414
|
+
"aria-label": t("filter.min-price", "Minimum price"),
|
|
3415
|
+
"aria-valuenow": value.min,
|
|
3416
|
+
"aria-valuemin": minPrice,
|
|
3417
|
+
"aria-valuemax": value.max,
|
|
3418
|
+
tabIndex: 0
|
|
3419
|
+
}
|
|
3420
|
+
),
|
|
3421
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
3422
|
+
"div",
|
|
3423
|
+
{
|
|
3424
|
+
className: `${PriceRangeFilter_default.sliderHandle} ${isDragging === "max" ? PriceRangeFilter_default.sliderHandleActive : ""}`,
|
|
3425
|
+
style: { left: `calc(12px + (100% - 24px) * ${maxPercent / 100})` },
|
|
3426
|
+
onMouseDown: handleMouseDown("max"),
|
|
3427
|
+
onTouchStart: handleTouchStart("max"),
|
|
3428
|
+
role: "slider",
|
|
3429
|
+
"aria-label": t("filter.max-price", "Maximum price"),
|
|
3430
|
+
"aria-valuenow": value.max,
|
|
3431
|
+
"aria-valuemin": value.min,
|
|
3432
|
+
"aria-valuemax": maxPrice,
|
|
3433
|
+
tabIndex: 0
|
|
3434
|
+
}
|
|
3435
|
+
)
|
|
3436
|
+
] })
|
|
3437
|
+
] });
|
|
3438
|
+
};
|
|
3439
|
+
|
|
3440
|
+
// src/components/Filters/HotelCategoryFilter.module.css
|
|
3441
|
+
var HotelCategoryFilter_default = {};
|
|
3442
|
+
|
|
3443
|
+
// src/components/Filters/HotelCategoryFilter.tsx
|
|
3444
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
3445
|
+
var CATEGORY_OPTIONS = [
|
|
3446
|
+
{ value: "5", stars: 5, label: null },
|
|
3447
|
+
{ value: "4", stars: 4, label: null },
|
|
3448
|
+
{ value: "3", stars: 3, label: null },
|
|
3449
|
+
{ value: "2", stars: 2, label: null },
|
|
3450
|
+
{ value: "swiss_lodge", stars: 0, label: "Swiss Lodge" },
|
|
3451
|
+
{ value: "not_classified", stars: 0, label: "No category" }
|
|
3452
|
+
];
|
|
3453
|
+
var HotelCategoryFilter = ({
|
|
3454
|
+
selected,
|
|
3455
|
+
counts = {},
|
|
3456
|
+
onChange,
|
|
3457
|
+
className = ""
|
|
3458
|
+
}) => {
|
|
3459
|
+
const { t } = useUIContext();
|
|
3460
|
+
const handleToggle = (value) => {
|
|
3461
|
+
if (selected.includes(value)) {
|
|
3462
|
+
onChange(selected.filter((v) => v !== value));
|
|
3463
|
+
} else {
|
|
3464
|
+
onChange([...selected, value]);
|
|
3465
|
+
}
|
|
3466
|
+
};
|
|
3467
|
+
const getLabel = (option) => {
|
|
3468
|
+
if (option.stars > 0) return null;
|
|
3469
|
+
if (option.value === "swiss_lodge") return t("filter.swiss-lodge", "Swiss Lodge");
|
|
3470
|
+
if (option.value === "not_classified") return t("filter.no-category", "No category");
|
|
3471
|
+
return option.label;
|
|
3472
|
+
};
|
|
3473
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className, children: [
|
|
3474
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: HotelCategoryFilter_default.filterList, children: CATEGORY_OPTIONS.map((option) => {
|
|
3475
|
+
const count = counts[option.value];
|
|
3476
|
+
const isDisabled = count === 0 && !selected.includes(option.value);
|
|
3477
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
3478
|
+
FilterCheckboxItem,
|
|
3479
|
+
{
|
|
3480
|
+
id: `category-${option.value}`,
|
|
3481
|
+
label: option.stars > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: HotelCategoryFilter_default.starsContainer, children: Array.from({ length: option.stars }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("svg", { className: HotelCategoryFilter_default.starIcon, viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("path", { d: "M12 0L14.6942 8.2918H23.4127L16.3593 13.4164L19.0534 21.7082L12 16.5836L4.94658 21.7082L7.64074 13.4164L0.587322 8.2918H9.30583L12 0Z" }) }, i)) }) : getLabel(option),
|
|
3482
|
+
count,
|
|
3483
|
+
checked: selected.includes(option.value),
|
|
3484
|
+
disabled: isDisabled,
|
|
3485
|
+
onChange: () => handleToggle(option.value),
|
|
3486
|
+
trackName: `filter-category-${option.value}`
|
|
3487
|
+
},
|
|
3488
|
+
option.value
|
|
3489
|
+
);
|
|
3490
|
+
}) }),
|
|
3491
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: HotelCategoryFilter_default.footer, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("span", { className: HotelCategoryFilter_default.footerText, children: t("filter.hotellerie-suisse", "Classification HotellerieSuisse") }) })
|
|
3492
|
+
] });
|
|
3493
|
+
};
|
|
3494
|
+
|
|
3495
|
+
// src/components/Filters/ReviewsFilter.module.css
|
|
3496
|
+
var ReviewsFilter_default = {};
|
|
3497
|
+
|
|
3498
|
+
// src/components/Filters/ReviewsFilter.tsx
|
|
3499
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
3500
|
+
var REVIEW_OPTIONS = [
|
|
3501
|
+
{ value: "excellent", labelKey: "rating-excellent", range: "(4.4+)" },
|
|
3502
|
+
{ value: "very_good", labelKey: "rating-very-good", range: "(4.1 \u2013 4.3)" },
|
|
3503
|
+
{ value: "good", labelKey: "rating-good", range: "(3.8 \u2013 4.0)" },
|
|
3504
|
+
{ value: "fair", labelKey: "rating-fair", range: "(3.5 \u2013 3.7)" },
|
|
3505
|
+
{ value: "no_rating", labelKey: "rating-none", range: "" }
|
|
3506
|
+
];
|
|
3507
|
+
var ReviewsFilter = ({
|
|
3508
|
+
selected,
|
|
3509
|
+
counts = {},
|
|
3510
|
+
onChange,
|
|
3511
|
+
className = "",
|
|
3512
|
+
isPlaceSearchActive = false
|
|
3513
|
+
}) => {
|
|
3514
|
+
const { t } = useUIContext();
|
|
3515
|
+
const handleToggle = (value) => {
|
|
3516
|
+
if (selected.includes(value)) {
|
|
3517
|
+
onChange(selected.filter((v) => v !== value));
|
|
3518
|
+
} else {
|
|
3519
|
+
onChange([...selected, value]);
|
|
3520
|
+
}
|
|
3521
|
+
};
|
|
3522
|
+
const getRatingLabel = (labelKey) => {
|
|
3523
|
+
const labelMap = {
|
|
3524
|
+
"rating-excellent": t("label.rating-excellent", "Excellent"),
|
|
3525
|
+
"rating-very-good": t("label.rating-very-good", "Very Good"),
|
|
3526
|
+
"rating-good": t("label.rating-good", "Good"),
|
|
3527
|
+
"rating-fair": t("label.rating-fair", "Fair"),
|
|
3528
|
+
"rating-none": t("label.rating-none", "No rating")
|
|
3529
|
+
};
|
|
3530
|
+
return labelMap[labelKey] ?? labelKey;
|
|
3531
|
+
};
|
|
3532
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className, children: [
|
|
3533
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: ReviewsFilter_default.filterList, children: REVIEW_OPTIONS.map((option) => {
|
|
3534
|
+
const count = counts[option.value];
|
|
3535
|
+
const isDisabled = !isPlaceSearchActive && count === 0 && !selected.includes(option.value);
|
|
3536
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
3537
|
+
FilterCheckboxItem,
|
|
3538
|
+
{
|
|
3539
|
+
id: `review-${option.value}`,
|
|
3540
|
+
label: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
3541
|
+
getRatingLabel(option.labelKey),
|
|
3542
|
+
option.range && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: ReviewsFilter_default.filterRange, children: [
|
|
3543
|
+
" ",
|
|
3544
|
+
option.range
|
|
3545
|
+
] })
|
|
3546
|
+
] }),
|
|
3547
|
+
count,
|
|
3548
|
+
checked: selected.includes(option.value),
|
|
3549
|
+
disabled: isDisabled,
|
|
3550
|
+
onChange: () => handleToggle(option.value),
|
|
3551
|
+
trackName: `filter-review-${option.value}`
|
|
3552
|
+
},
|
|
3553
|
+
option.value
|
|
3554
|
+
);
|
|
3555
|
+
}) }),
|
|
3556
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: ReviewsFilter_default.footer, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: ReviewsFilter_default.footerText, children: t("filter.trustyou", "Reviews from TrustYou\xAE") }) })
|
|
3557
|
+
] });
|
|
3558
|
+
};
|
|
3559
|
+
|
|
3560
|
+
// src/components/Filters/ExperienceFilter.tsx
|
|
3561
|
+
var import_react17 = require("react");
|
|
3562
|
+
|
|
3563
|
+
// src/components/Filters/ExperienceFilter.module.css
|
|
3564
|
+
var ExperienceFilter_default = {};
|
|
3565
|
+
|
|
3566
|
+
// src/components/Filters/ExperienceFilter.tsx
|
|
3567
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
3568
|
+
var ExperienceFilter = ({
|
|
3569
|
+
selected,
|
|
3570
|
+
themes = [],
|
|
3571
|
+
onChange,
|
|
3572
|
+
className = ""
|
|
3573
|
+
}) => {
|
|
3574
|
+
const { t } = useUIContext();
|
|
3575
|
+
const [expanded, setExpanded] = (0, import_react17.useState)(false);
|
|
3576
|
+
const visibleThemes = expanded ? themes : themes.slice(0, 5);
|
|
3577
|
+
const hasMoreThemes = themes.length > 5;
|
|
3578
|
+
const handleToggle = (value) => {
|
|
3579
|
+
if (selected.includes(value)) {
|
|
3580
|
+
onChange(selected.filter((v) => v !== value));
|
|
3581
|
+
} else {
|
|
3582
|
+
onChange([...selected, value]);
|
|
3583
|
+
}
|
|
3584
|
+
};
|
|
3585
|
+
if (themes.length === 0) {
|
|
3586
|
+
return null;
|
|
3587
|
+
}
|
|
3588
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: `${ExperienceFilter_default.filterList} ${className}`, children: [
|
|
3589
|
+
visibleThemes.map((theme) => {
|
|
3590
|
+
const themeIdStr = String(theme.id);
|
|
3591
|
+
const isDisabled = theme.count === 0 && !selected.includes(themeIdStr);
|
|
3592
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
3593
|
+
FilterCheckboxItem,
|
|
3594
|
+
{
|
|
3595
|
+
id: `experience-${theme.id}`,
|
|
3596
|
+
label: theme.name,
|
|
3597
|
+
count: theme.count > 0 ? theme.count : void 0,
|
|
3598
|
+
checked: selected.includes(themeIdStr),
|
|
3599
|
+
disabled: isDisabled,
|
|
3600
|
+
onChange: () => handleToggle(themeIdStr),
|
|
3601
|
+
trackName: `filter-experience-${theme.id}`
|
|
3602
|
+
},
|
|
3603
|
+
theme.id
|
|
3604
|
+
);
|
|
3605
|
+
}),
|
|
3606
|
+
hasMoreThemes && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
3607
|
+
"button",
|
|
3608
|
+
{
|
|
3609
|
+
type: "button",
|
|
3610
|
+
className: ExperienceFilter_default.showMoreBtn,
|
|
3611
|
+
onClick: () => setExpanded(!expanded),
|
|
3612
|
+
children: [
|
|
3613
|
+
expanded ? t("filter.show-less", "Show less") : t("filter.show-all", "Show all"),
|
|
3614
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: `${ExperienceFilter_default.arrowIcon} ${expanded ? ExperienceFilter_default.arrowUp : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("path", { d: "M9.36927 13.9061C9.71908 14.2535 10.2847 14.2535 10.6308 13.9061L16.4069 8.1803C16.7567 7.83285 16.7567 7.27102 16.4069 6.92727C16.0571 6.58352 15.4915 6.57983 15.1454 6.92727L10.0019 12.0247L4.85845 6.92727C4.50864 6.57983 3.94299 6.57983 3.5969 6.92727C3.25081 7.27472 3.24709 7.83655 3.5969 8.1803L9.37299 13.9061H9.36927Z", fill: "currentColor" }) }) })
|
|
3615
|
+
]
|
|
3616
|
+
}
|
|
3617
|
+
)
|
|
3618
|
+
] });
|
|
3619
|
+
};
|
|
3620
|
+
|
|
3621
|
+
// src/components/Filters/RegionsFilter.tsx
|
|
3622
|
+
var import_react18 = require("react");
|
|
3623
|
+
|
|
3624
|
+
// src/components/Filters/RegionsFilter.module.css
|
|
3625
|
+
var RegionsFilter_default = {};
|
|
3626
|
+
|
|
3627
|
+
// src/components/Filters/RegionsFilter.tsx
|
|
3628
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
3629
|
+
var RegionsFilter = ({
|
|
3630
|
+
regions,
|
|
3631
|
+
selected,
|
|
3632
|
+
onChange,
|
|
3633
|
+
className = "",
|
|
3634
|
+
isPlaceSearchActive = false
|
|
3635
|
+
}) => {
|
|
3636
|
+
const { t } = useUIContext();
|
|
3637
|
+
const [expandedCountries, setExpandedCountries] = (0, import_react18.useState)(/* @__PURE__ */ new Set(["country_215"]));
|
|
3638
|
+
const hasInitializedRef = (0, import_react18.useRef)(false);
|
|
3639
|
+
(0, import_react18.useEffect)(() => {
|
|
3640
|
+
if (hasInitializedRef.current) return;
|
|
3641
|
+
if (regions.length === 0 || selected.length === 0) return;
|
|
3642
|
+
const countriesWithSelectedRegions = /* @__PURE__ */ new Set();
|
|
3643
|
+
regions.forEach((region) => {
|
|
3644
|
+
if (region.subRegions?.some((sub) => selected.includes(sub.value))) {
|
|
3645
|
+
countriesWithSelectedRegions.add(region.value);
|
|
3646
|
+
}
|
|
3647
|
+
});
|
|
3648
|
+
if (countriesWithSelectedRegions.size > 0) {
|
|
3649
|
+
setExpandedCountries(countriesWithSelectedRegions);
|
|
3650
|
+
hasInitializedRef.current = true;
|
|
3651
|
+
}
|
|
3652
|
+
}, [regions, selected]);
|
|
3653
|
+
const handleCountryToggle = (countryValue) => {
|
|
3654
|
+
setExpandedCountries((prev) => {
|
|
3655
|
+
const next = new Set(prev);
|
|
3656
|
+
if (next.has(countryValue)) {
|
|
3657
|
+
next.delete(countryValue);
|
|
3658
|
+
} else {
|
|
3659
|
+
next.add(countryValue);
|
|
3660
|
+
}
|
|
3661
|
+
return next;
|
|
3662
|
+
});
|
|
3663
|
+
};
|
|
3664
|
+
const handleRegionToggle = (regionValue) => {
|
|
3665
|
+
if (selected.includes(regionValue)) {
|
|
3666
|
+
onChange(selected.filter((r) => r !== regionValue));
|
|
3667
|
+
} else {
|
|
3668
|
+
onChange([...selected, regionValue]);
|
|
3669
|
+
}
|
|
3670
|
+
};
|
|
3671
|
+
if (regions.length === 0) {
|
|
3672
|
+
return null;
|
|
3673
|
+
}
|
|
3674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: `${RegionsFilter_default.filterList} ${className}`, children: regions.map((region) => {
|
|
3675
|
+
const hasSubRegions = region.subRegions && region.subRegions.length > 0;
|
|
3676
|
+
const isExpanded = expandedCountries.has(region.value);
|
|
3677
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: RegionsFilter_default.regionItem, children: [
|
|
3678
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
3679
|
+
"button",
|
|
3680
|
+
{
|
|
3681
|
+
type: "button",
|
|
3682
|
+
className: `${RegionsFilter_default.countryRow} ${isExpanded ? RegionsFilter_default.countryRowExpanded : ""}`,
|
|
3683
|
+
onClick: () => handleCountryToggle(region.value),
|
|
3684
|
+
"aria-expanded": isExpanded,
|
|
3685
|
+
disabled: isPlaceSearchActive,
|
|
3686
|
+
children: [
|
|
3687
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: RegionsFilter_default.countryInfo, children: [
|
|
3688
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: RegionsFilter_default.countryName, children: region.label }),
|
|
3689
|
+
region.count !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: RegionsFilter_default.countBadge, children: region.count })
|
|
3690
|
+
] }),
|
|
3691
|
+
hasSubRegions && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3692
|
+
"svg",
|
|
3693
|
+
{
|
|
3694
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3695
|
+
width: "20",
|
|
3696
|
+
height: "20",
|
|
3697
|
+
viewBox: "0 0 20 20",
|
|
3698
|
+
fill: "none",
|
|
3699
|
+
className: `${RegionsFilter_default.chevron} ${isExpanded ? RegionsFilter_default.chevronExpanded : ""}`,
|
|
3700
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("path", { d: "M9.36927 13.9061C9.71908 14.2535 10.2847 14.2535 10.6308 13.9061L16.4069 8.18027C16.7567 7.83283 16.7567 7.271 16.4069 6.92725C16.0571 6.5835 15.4915 6.5798 15.1454 6.92725L10.0019 12.0247L4.85845 6.92725C4.50864 6.5798 3.94299 6.5798 3.5969 6.92725C3.25081 7.2747 3.24709 7.83652 3.5969 8.18027L9.37299 13.9061H9.36927Z", fill: "#6B7280" })
|
|
3701
|
+
}
|
|
3702
|
+
)
|
|
3703
|
+
]
|
|
3704
|
+
}
|
|
3705
|
+
),
|
|
3706
|
+
hasSubRegions && isExpanded && (() => {
|
|
3707
|
+
const hasSelectableRegions = region.subRegions.some(
|
|
3708
|
+
(sr) => sr.count === void 0 || sr.count > 0
|
|
3709
|
+
);
|
|
3710
|
+
const subValues = region.subRegions.map((sr) => sr.value);
|
|
3711
|
+
const hasSelectedRegions = subValues.some((v) => selected.includes(v));
|
|
3712
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: RegionsFilter_default.mobileSelectAllActions, children: [
|
|
3713
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3714
|
+
"button",
|
|
3715
|
+
{
|
|
3716
|
+
type: "button",
|
|
3717
|
+
className: RegionsFilter_default.selectAllBtn,
|
|
3718
|
+
disabled: !hasSelectableRegions,
|
|
3719
|
+
onClick: () => {
|
|
3720
|
+
const selectableSubValues = region.subRegions.filter((sr) => sr.count === void 0 || sr.count > 0).map((sr) => sr.value);
|
|
3721
|
+
const merged = Array.from(/* @__PURE__ */ new Set([...selected, ...selectableSubValues]));
|
|
3722
|
+
onChange(merged);
|
|
3723
|
+
},
|
|
3724
|
+
children: t("filter.select-all", "Select all")
|
|
3725
|
+
}
|
|
3726
|
+
),
|
|
3727
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3728
|
+
"button",
|
|
3729
|
+
{
|
|
3730
|
+
type: "button",
|
|
3731
|
+
className: RegionsFilter_default.resetAllBtn,
|
|
3732
|
+
disabled: !hasSelectedRegions,
|
|
3733
|
+
onClick: () => {
|
|
3734
|
+
onChange(selected.filter((v) => !subValues.includes(v)));
|
|
3735
|
+
},
|
|
3736
|
+
children: t("filter.reset", "Reset")
|
|
3737
|
+
}
|
|
3738
|
+
)
|
|
3739
|
+
] });
|
|
3740
|
+
})(),
|
|
3741
|
+
hasSubRegions && isExpanded && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: RegionsFilter_default.subRegions, children: region.subRegions.map((subRegion) => {
|
|
3742
|
+
const isDisabled = subRegion.count === 0 && !selected.includes(subRegion.value) || isPlaceSearchActive && !selected.includes(subRegion.value);
|
|
3743
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
3744
|
+
FilterCheckboxItem,
|
|
3745
|
+
{
|
|
3746
|
+
id: `region-${subRegion.value}`,
|
|
3747
|
+
label: subRegion.label,
|
|
3748
|
+
count: subRegion.count,
|
|
3749
|
+
checked: selected.includes(subRegion.value),
|
|
3750
|
+
disabled: isDisabled,
|
|
3751
|
+
onChange: () => handleRegionToggle(subRegion.value),
|
|
3752
|
+
trackName: `filter-region-${subRegion.value}`
|
|
3753
|
+
},
|
|
3754
|
+
subRegion.value
|
|
3755
|
+
);
|
|
3756
|
+
}) })
|
|
3757
|
+
] }, region.value);
|
|
3758
|
+
}) });
|
|
3759
|
+
};
|
|
3760
|
+
|
|
3761
|
+
// src/components/Filters/CheckboxFilter.module.css
|
|
3762
|
+
var CheckboxFilter_default = {};
|
|
3763
|
+
|
|
3764
|
+
// src/components/Filters/CheckboxFilter.tsx
|
|
3765
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
3766
|
+
var CheckboxFilter = ({
|
|
3767
|
+
selected,
|
|
3768
|
+
counts = {},
|
|
3769
|
+
options = [],
|
|
3770
|
+
onChange,
|
|
3771
|
+
className = "",
|
|
3772
|
+
trackPrefix = "filter"
|
|
3773
|
+
}) => {
|
|
3774
|
+
const handleToggle = (value) => {
|
|
3775
|
+
if (selected.includes(value)) {
|
|
3776
|
+
onChange(selected.filter((v) => v !== value));
|
|
3777
|
+
} else {
|
|
3778
|
+
onChange([...selected, value]);
|
|
3779
|
+
}
|
|
3780
|
+
};
|
|
3781
|
+
if (options.length === 0) {
|
|
3782
|
+
return null;
|
|
3783
|
+
}
|
|
3784
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: `${CheckboxFilter_default.filterList} ${className}`, children: options.map((option) => {
|
|
3785
|
+
const count = counts[option.key];
|
|
3786
|
+
const isDisabled = count === 0 && !selected.includes(option.key);
|
|
3787
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
3788
|
+
FilterCheckboxItem,
|
|
3789
|
+
{
|
|
3790
|
+
id: `${trackPrefix}-${option.key}`,
|
|
3791
|
+
label: option.name,
|
|
3792
|
+
count,
|
|
3793
|
+
checked: selected.includes(option.key),
|
|
3794
|
+
disabled: isDisabled,
|
|
3795
|
+
onChange: () => handleToggle(option.key),
|
|
3796
|
+
trackName: `filter-${trackPrefix}-${option.key}`
|
|
3797
|
+
},
|
|
3798
|
+
option.key
|
|
3799
|
+
);
|
|
3800
|
+
}) });
|
|
3801
|
+
};
|
|
3802
|
+
var MealsFilter = (props) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CheckboxFilter, { ...props, trackPrefix: "meals" });
|
|
3803
|
+
var TransportFilter = (props) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CheckboxFilter, { ...props, trackPrefix: "transport" });
|
|
3804
|
+
var WellnessFilter = (props) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(CheckboxFilter, { ...props, trackPrefix: "wellness" });
|
|
3805
|
+
|
|
3806
|
+
// src/components/Filters/SelectedFiltersRow.module.css
|
|
3807
|
+
var SelectedFiltersRow_default = {};
|
|
3808
|
+
|
|
3809
|
+
// src/components/Filters/SelectedFiltersRow.tsx
|
|
3810
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
3811
|
+
var SelectedFiltersRow = ({
|
|
3812
|
+
filters,
|
|
3813
|
+
onRemove,
|
|
3814
|
+
onClearAll,
|
|
3815
|
+
className = ""
|
|
3816
|
+
}) => {
|
|
3817
|
+
const { t } = useUIContext();
|
|
3818
|
+
if (filters.length === 0) {
|
|
3819
|
+
return null;
|
|
3820
|
+
}
|
|
3821
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: `${SelectedFiltersRow_default.container} ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: SelectedFiltersRow_default.chipsWrapper, children: [
|
|
3822
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("p", { className: SelectedFiltersRow_default.selectedText, children: [
|
|
3823
|
+
t("filter.selected", "Selected:"),
|
|
3824
|
+
" "
|
|
3825
|
+
] }),
|
|
3826
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: SelectedFiltersRow_default.chips, children: [
|
|
3827
|
+
filters.map((filter) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3828
|
+
Chip,
|
|
3829
|
+
{
|
|
3830
|
+
label: filter.label,
|
|
3831
|
+
size: "small",
|
|
3832
|
+
state: "idle",
|
|
3833
|
+
removable: true,
|
|
3834
|
+
onRemove: () => onRemove(filter)
|
|
3835
|
+
},
|
|
3836
|
+
filter.id
|
|
3837
|
+
)),
|
|
3838
|
+
onClearAll && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
3839
|
+
Button,
|
|
3840
|
+
{
|
|
3841
|
+
variant: "link",
|
|
3842
|
+
className: SelectedFiltersRow_default.clearAllBtn,
|
|
3843
|
+
onClick: onClearAll,
|
|
3844
|
+
children: t("filter.clear-all", "Clear all")
|
|
3845
|
+
}
|
|
3846
|
+
)
|
|
3847
|
+
] })
|
|
3848
|
+
] }) });
|
|
3849
|
+
};
|
|
3850
|
+
|
|
3851
|
+
// src/components/icons/HeartIcon.tsx
|
|
3852
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
3853
|
+
var HeartIcon3 = ({ filled = false, className = "", size = 24 }) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2979
3854
|
"svg",
|
|
2980
3855
|
{
|
|
2981
3856
|
width: size,
|
|
@@ -2987,14 +3862,14 @@ var HeartIcon3 = ({ filled = false, className = "", size = 24 }) => /* @__PURE__
|
|
|
2987
3862
|
strokeWidth: 2,
|
|
2988
3863
|
strokeLinecap: "round",
|
|
2989
3864
|
strokeLinejoin: "round",
|
|
2990
|
-
children: /* @__PURE__ */ (0,
|
|
3865
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("path", { d: "M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" })
|
|
2991
3866
|
}
|
|
2992
3867
|
);
|
|
2993
3868
|
HeartIcon3.displayName = "HeartIcon";
|
|
2994
3869
|
|
|
2995
3870
|
// src/components/icons/StarIcon.tsx
|
|
2996
|
-
var
|
|
2997
|
-
var StarIcon4 = ({ filled = true, className = "", size = 9 }) => /* @__PURE__ */ (0,
|
|
3871
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
3872
|
+
var StarIcon4 = ({ filled = true, className = "", size = 9 }) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
2998
3873
|
"svg",
|
|
2999
3874
|
{
|
|
3000
3875
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -3004,22 +3879,22 @@ var StarIcon4 = ({ filled = true, className = "", size = 9 }) => /* @__PURE__ */
|
|
|
3004
3879
|
fill: "none",
|
|
3005
3880
|
className,
|
|
3006
3881
|
children: [
|
|
3007
|
-
/* @__PURE__ */ (0,
|
|
3882
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("g", { clipPath: "url(#clip0_star_icon)", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
3008
3883
|
"path",
|
|
3009
3884
|
{
|
|
3010
3885
|
d: "M4.80018 0.366577C4.93104 0.366577 5.05173 0.440968 5.11135 0.557659L6.18011 2.66102L8.50521 3.03152C8.63462 3.05194 8.74222 3.14383 8.78294 3.26927C8.82365 3.39472 8.79021 3.53183 8.6986 3.62518L7.03366 5.29533L7.40155 7.6277C7.42191 7.75752 7.3681 7.88879 7.26195 7.9661C7.15581 8.04341 7.01476 8.05508 6.89843 7.99528L4.80018 6.92463L2.70192 7.99528C2.58559 8.05508 2.44454 8.04341 2.33839 7.9661C2.23225 7.88879 2.17844 7.75898 2.1988 7.6277L2.56523 5.29533L0.901751 3.62518C0.808689 3.53183 0.776699 3.39472 0.817413 3.26927C0.858128 3.14383 0.964277 3.05194 1.09515 3.03152L3.42024 2.66102L4.49045 0.557659C4.55007 0.440968 4.67076 0.366577 4.80163 0.366577H4.80018Z",
|
|
3011
3886
|
fill: filled ? "#1F2937" : "#D1D5DB"
|
|
3012
3887
|
}
|
|
3013
3888
|
) }),
|
|
3014
|
-
/* @__PURE__ */ (0,
|
|
3889
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("clipPath", { id: "clip0_star_icon", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("rect", { width: "8", height: "8", fill: "white", transform: "translate(0.800049 0.199951)" }) }) })
|
|
3015
3890
|
]
|
|
3016
3891
|
}
|
|
3017
3892
|
);
|
|
3018
3893
|
StarIcon4.displayName = "StarIcon";
|
|
3019
3894
|
|
|
3020
3895
|
// src/components/icons/ChevronLeftIcon.tsx
|
|
3021
|
-
var
|
|
3022
|
-
var ChevronLeftIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ (0,
|
|
3896
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
3897
|
+
var ChevronLeftIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3023
3898
|
"svg",
|
|
3024
3899
|
{
|
|
3025
3900
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -3032,14 +3907,14 @@ var ChevronLeftIcon = ({ className = "", size = 20 }) => /* @__PURE__ */ (0, imp
|
|
|
3032
3907
|
strokeLinecap: "round",
|
|
3033
3908
|
strokeLinejoin: "round",
|
|
3034
3909
|
className,
|
|
3035
|
-
children: /* @__PURE__ */ (0,
|
|
3910
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("polyline", { points: "15 18 9 12 15 6" })
|
|
3036
3911
|
}
|
|
3037
3912
|
);
|
|
3038
3913
|
ChevronLeftIcon.displayName = "ChevronLeftIcon";
|
|
3039
3914
|
|
|
3040
3915
|
// src/components/icons/ChevronRightIcon.tsx
|
|
3041
|
-
var
|
|
3042
|
-
var ChevronRightIcon2 = ({ className = "", size = 20 }) => /* @__PURE__ */ (0,
|
|
3916
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
3917
|
+
var ChevronRightIcon2 = ({ className = "", size = 20 }) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
3043
3918
|
"svg",
|
|
3044
3919
|
{
|
|
3045
3920
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -3052,14 +3927,14 @@ var ChevronRightIcon2 = ({ className = "", size = 20 }) => /* @__PURE__ */ (0, i
|
|
|
3052
3927
|
strokeLinecap: "round",
|
|
3053
3928
|
strokeLinejoin: "round",
|
|
3054
3929
|
className,
|
|
3055
|
-
children: /* @__PURE__ */ (0,
|
|
3930
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("polyline", { points: "9 18 15 12 9 6" })
|
|
3056
3931
|
}
|
|
3057
3932
|
);
|
|
3058
3933
|
ChevronRightIcon2.displayName = "ChevronRightIcon";
|
|
3059
3934
|
|
|
3060
3935
|
// src/components/icons/PinIcon.tsx
|
|
3061
|
-
var
|
|
3062
|
-
var PinIcon2 = ({ className = "", size = 16 }) => /* @__PURE__ */ (0,
|
|
3936
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
3937
|
+
var PinIcon2 = ({ className = "", size = 16 }) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
3063
3938
|
"svg",
|
|
3064
3939
|
{
|
|
3065
3940
|
xmlns: "http://www.w3.org/2000/svg",
|
|
@@ -3069,7 +3944,7 @@ var PinIcon2 = ({ className = "", size = 16 }) => /* @__PURE__ */ (0, import_jsx
|
|
|
3069
3944
|
fill: "none",
|
|
3070
3945
|
className,
|
|
3071
3946
|
children: [
|
|
3072
|
-
/* @__PURE__ */ (0,
|
|
3947
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3073
3948
|
"path",
|
|
3074
3949
|
{
|
|
3075
3950
|
fillRule: "evenodd",
|
|
@@ -3078,7 +3953,7 @@ var PinIcon2 = ({ className = "", size = 16 }) => /* @__PURE__ */ (0, import_jsx
|
|
|
3078
3953
|
fill: "currentColor"
|
|
3079
3954
|
}
|
|
3080
3955
|
),
|
|
3081
|
-
/* @__PURE__ */ (0,
|
|
3956
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3082
3957
|
"path",
|
|
3083
3958
|
{
|
|
3084
3959
|
fillRule: "evenodd",
|
|
@@ -3127,31 +4002,45 @@ var calculateDiscount = (originalPrice, discountedPrice) => {
|
|
|
3127
4002
|
Benefits,
|
|
3128
4003
|
Block,
|
|
3129
4004
|
Button,
|
|
4005
|
+
CATEGORY_OPTIONS,
|
|
3130
4006
|
Card,
|
|
3131
4007
|
Checkbox,
|
|
4008
|
+
CheckboxFilter,
|
|
3132
4009
|
ChevronLeftIcon,
|
|
3133
4010
|
ChevronRightIcon,
|
|
3134
4011
|
Chip,
|
|
4012
|
+
CollapsibleFilterSection,
|
|
3135
4013
|
DateSelector,
|
|
3136
4014
|
Divider,
|
|
3137
4015
|
Dropdown,
|
|
3138
4016
|
DualCalendar,
|
|
4017
|
+
ExperienceFilter,
|
|
3139
4018
|
FAQ,
|
|
4019
|
+
FilterCheckboxItem,
|
|
3140
4020
|
GuestContent,
|
|
3141
4021
|
HeartIcon,
|
|
3142
4022
|
HotelCard,
|
|
3143
4023
|
HotelCardContent,
|
|
3144
4024
|
HotelCardImage,
|
|
3145
4025
|
HotelCardUIProvider,
|
|
4026
|
+
HotelCategoryFilter,
|
|
3146
4027
|
Input,
|
|
4028
|
+
MealsFilter,
|
|
3147
4029
|
Modal,
|
|
3148
4030
|
Pin,
|
|
3149
4031
|
PinIcon,
|
|
4032
|
+
PriceRangeFilter,
|
|
4033
|
+
REVIEW_OPTIONS,
|
|
3150
4034
|
RadioButton,
|
|
3151
4035
|
Rating,
|
|
4036
|
+
RegionsFilter,
|
|
3152
4037
|
ReviewCard,
|
|
4038
|
+
ReviewsFilter,
|
|
3153
4039
|
SectionHeader,
|
|
4040
|
+
SelectedFiltersRow,
|
|
3154
4041
|
StarIcon,
|
|
4042
|
+
TransportFilter,
|
|
4043
|
+
WellnessFilter,
|
|
3155
4044
|
WhenContent,
|
|
3156
4045
|
calculateDiscount,
|
|
3157
4046
|
formatDate,
|