kupos-ui-components-lib 10.4.11 → 10.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/assets/images/filter/bus.svg +19 -0
  2. package/dist/assets/images/filter/clock.svg +4 -0
  3. package/dist/assets/images/filter/filter.svg +3 -0
  4. package/dist/assets/images/filter/money.svg +12 -0
  5. package/dist/assets/images/filter/night.svg +5 -0
  6. package/dist/assets/images/filter/rise.svg +20 -0
  7. package/dist/assets/images/filter/search.svg +4 -0
  8. package/dist/assets/images/filter/seat.svg +4 -0
  9. package/dist/assets/images/filter/star.svg +10 -0
  10. package/dist/assets/images/filter/sun.svg +20 -0
  11. package/dist/assets/images/filter/sunset.svg +15 -0
  12. package/dist/assets/images/filter/track.svg +4 -0
  13. package/dist/components/FilterSortBar/FilterChip.d.ts +21 -0
  14. package/dist/components/FilterSortBar/FilterChip.js +42 -0
  15. package/dist/components/FilterSortBar/FilterSortBarMobile.d.ts +10 -0
  16. package/dist/components/FilterSortBar/FilterSortBarMobile.js +121 -0
  17. package/dist/components/FilterSortBar/FiltersMasterSheet.d.ts +16 -0
  18. package/dist/components/FilterSortBar/FiltersMasterSheet.js +111 -0
  19. package/dist/components/FilterSortBar/OptionCheckboxList.d.ts +13 -0
  20. package/dist/components/FilterSortBar/OptionCheckboxList.js +30 -0
  21. package/dist/components/FilterSortBar/PriceRangeSlider.d.ts +12 -0
  22. package/dist/components/FilterSortBar/PriceRangeSlider.js +113 -0
  23. package/dist/components/FilterSortBar/SheetFooter.d.ts +9 -0
  24. package/dist/components/FilterSortBar/SheetFooter.js +36 -0
  25. package/dist/components/FilterSortBar/SingleFilterSheet.d.ts +15 -0
  26. package/dist/components/FilterSortBar/SingleFilterSheet.js +21 -0
  27. package/dist/components/FilterSortBar/defaultIcons.d.ts +2 -0
  28. package/dist/components/FilterSortBar/defaultIcons.js +18 -0
  29. package/dist/components/FilterSortBar/icons.d.ts +50 -0
  30. package/dist/components/FilterSortBar/icons.js +67 -0
  31. package/dist/components/FilterSortBar/index.d.ts +4 -0
  32. package/dist/components/FilterSortBar/index.js +3 -0
  33. package/dist/components/FilterSortBar/types.d.ts +57 -0
  34. package/dist/components/FilterSortBar/types.js +8 -0
  35. package/dist/components/PaymentSideBar/PaymentSideBarMobile.js +15 -8
  36. package/dist/components/PaymentSideBar/types.d.ts +3 -0
  37. package/dist/index.d.ts +4 -1
  38. package/dist/index.js +5 -0
  39. package/dist/styles.css +64 -0
  40. package/dist/ui/BottomSheet/BottomSheet.d.ts +2 -1
  41. package/dist/ui/BottomSheet/BottomSheet.js +2 -1
  42. package/package.json +1 -1
  43. package/patch.py +35 -0
  44. package/src/assets/images/filter/bus.svg +19 -0
  45. package/src/assets/images/filter/clock.svg +4 -0
  46. package/src/assets/images/filter/filter.svg +3 -0
  47. package/src/assets/images/filter/money.svg +12 -0
  48. package/src/assets/images/filter/night.svg +5 -0
  49. package/src/assets/images/filter/rise.svg +20 -0
  50. package/src/assets/images/filter/search.svg +4 -0
  51. package/src/assets/images/filter/seat.svg +4 -0
  52. package/src/assets/images/filter/star.svg +10 -0
  53. package/src/assets/images/filter/sun.svg +20 -0
  54. package/src/assets/images/filter/sunset.svg +15 -0
  55. package/src/assets/images/filter/track.svg +4 -0
  56. package/src/components/FilterSortBar/FilterChip.tsx +113 -0
  57. package/src/components/FilterSortBar/FilterSortBarMobile.tsx +236 -0
  58. package/src/components/FilterSortBar/FiltersMasterSheet.tsx +208 -0
  59. package/src/components/FilterSortBar/OptionCheckboxList.tsx +86 -0
  60. package/src/components/FilterSortBar/PriceRangeSlider.tsx +184 -0
  61. package/src/components/FilterSortBar/SheetFooter.tsx +61 -0
  62. package/src/components/FilterSortBar/SingleFilterSheet.tsx +82 -0
  63. package/src/components/FilterSortBar/defaultIcons.ts +29 -0
  64. package/src/components/FilterSortBar/icons.tsx +237 -0
  65. package/src/components/FilterSortBar/index.ts +15 -0
  66. package/src/components/FilterSortBar/types.ts +79 -0
  67. package/src/components/PaymentSideBar/PaymentSideBarMobile.tsx +553 -526
  68. package/src/components/PaymentSideBar/types.ts +3 -0
  69. package/src/index.ts +18 -0
  70. package/src/ui/BottomSheet/BottomSheet.tsx +4 -1
  71. package/src/ui/PaymentCardMobile/PaymentCardMobile.tsx +982 -0
  72. package/src/ui/PaymentCardMobile/index.ts +1 -0
  73. package/src/ui/SlidingModal/SlidingModal.tsx +145 -0
  74. package/src/ui/SlidingModal/index.ts +2 -0
  75. package/dist/assets/images/anims/service_list/60_percent_anim.json +0 -1
  76. package/dist/components/PaymentOptions/PaymentOptionsDesktop.d.ts +0 -4
  77. package/dist/components/PaymentOptions/PaymentOptionsDesktop.js +0 -5
  78. package/dist/components/PaymentOptions/PaymentOptionsMobile.d.ts +0 -4
  79. package/dist/components/PaymentOptions/PaymentOptionsMobile.js +0 -5
  80. package/dist/components/PaymentOptions/ResponsivePaymentOptions.d.ts +0 -3
  81. package/dist/components/PaymentOptions/ResponsivePaymentOptions.js +0 -10
  82. package/dist/components/PaymentOptions/index.d.ts +0 -5
  83. package/dist/components/PaymentOptions/index.js +0 -4
  84. package/dist/components/PaymentOptions/mobileTypes.d.ts +0 -0
  85. package/dist/components/PaymentOptions/mobileTypes.js +0 -0
  86. package/dist/components/PaymentOptions/type.d.ts +0 -4
  87. package/dist/components/PaymentOptions/type.js +0 -1
  88. package/dist/components/ServiceItem/ExpandedDropdown.d.ts +0 -19
  89. package/dist/components/ServiceItem/ExpandedDropdown.js +0 -28
  90. package/dist/components/ServiceItem/ServiceItemDesktopPeru.d.ts +0 -6
  91. package/dist/components/ServiceItem/ServiceItemDesktopPeru.js +0 -612
  92. package/dist/ui/TopAmenities/PromoCountdown.d.ts +0 -18
  93. package/dist/ui/TopAmenities/PromoCountdown.js +0 -55
@@ -0,0 +1,236 @@
1
+ import React, { useEffect, useRef, useState } from "react";
2
+ import FilterChip from "./FilterChip";
3
+ import SingleFilterSheet from "./SingleFilterSheet";
4
+ import FiltersMasterSheet from "./FiltersMasterSheet";
5
+ import { DEFAULT_ICONS } from "./defaultIcons";
6
+ import { FilterCategory, FilterCategoryKey, FilterSortBarMobileProps } from "./types";
7
+
8
+ const DEFAULT_COLORS = {
9
+ primaryColor: "#FF5C60",
10
+ primaryTextColor: "#FFFFFF",
11
+ chipBorderColor: "#C0C0C0",
12
+ chipTextColor: "#464647",
13
+ badgeBackgroundColor: "#FFCBCC",
14
+ badgeTextColor: "#FF5C60",
15
+ // Falls back to primaryColor/primaryTextColor below when the consumer
16
+ // doesn't pass a dedicated CTA button color.
17
+ buttonColor: "#FF5C60",
18
+ buttonTextColor: "#FFFFFF",
19
+ };
20
+
21
+ const getCategoryActiveCount = (category: FilterCategory) => {
22
+ if (category.kind === "price" && category.priceRange) {
23
+ return category.priceRange.currentMin > category.priceRange.min ||
24
+ category.priceRange.currentMax < category.priceRange.max
25
+ ? 1
26
+ : 0;
27
+ }
28
+ return category.options?.filter((o) => o.selected).length || 0;
29
+ };
30
+
31
+ const isCategoryActive = (category: FilterCategory) => getCategoryActiveCount(category) > 0;
32
+
33
+ /**
34
+ * Checkbox/slider interactions inside an open sheet only ever touch this
35
+ * local draft. Nothing outside the sheet — chip color, badge counts, the
36
+ * consumer's list — sees a change until Aplicar commits the draft via
37
+ * onApply. Closing the sheet without applying discards it.
38
+ */
39
+ const FilterSortBarMobile = ({
40
+ categories,
41
+ onApply,
42
+ onClearFilter,
43
+ currencySign = "$",
44
+ colors,
45
+ icons,
46
+ }: FilterSortBarMobileProps) => {
47
+ const [openSheet, setOpenSheet] = useState<FilterCategoryKey | "all" | null>(null);
48
+ const [isScrolled, setIsScrolled] = useState(false);
49
+ const [draftCategories, setDraftCategories] = useState<FilterCategory[]>(categories);
50
+ const scrollRef = useRef<HTMLDivElement>(null);
51
+ const resolvedColors = { ...DEFAULT_COLORS, ...colors };
52
+ const resolvedIcons = {
53
+ ...DEFAULT_ICONS,
54
+ ...icons,
55
+ categoryIcons: { ...DEFAULT_ICONS.categoryIcons, ...icons?.categoryIcons },
56
+ categoryIconsActive: { ...DEFAULT_ICONS.categoryIconsActive, ...icons?.categoryIconsActive },
57
+ timeOfDayIcons: { ...DEFAULT_ICONS.timeOfDayIcons, ...icons?.timeOfDayIcons },
58
+ };
59
+
60
+ // Re-sync the draft whenever the committed categories change from the
61
+ // outside (a real Aplicar/Borrar filtro commit, or fresh search results).
62
+ useEffect(() => {
63
+ setDraftCategories(categories);
64
+ }, [categories]);
65
+
66
+ const openCategory = draftCategories.find((c) => c.key === openSheet) || null;
67
+ const isMasterOpen = openSheet === "all";
68
+ const totalActiveCategories = categories.filter(isCategoryActive).length;
69
+
70
+ const handleScroll = () => {
71
+ setIsScrolled((scrollRef.current?.scrollLeft ?? 0) > 4);
72
+ };
73
+
74
+ const openSheetFor = (key: FilterCategoryKey | "all") => {
75
+ setDraftCategories(categories); // discard any leftover draft from a cancelled interaction
76
+ setOpenSheet(key);
77
+ };
78
+
79
+ const closeSheet = () => setOpenSheet(null);
80
+
81
+ const handleDraftToggleOption = (categoryKey: FilterCategoryKey, optionId: string) => {
82
+ setDraftCategories((prev) =>
83
+ prev.map((cat) =>
84
+ cat.key === categoryKey
85
+ ? {
86
+ ...cat,
87
+ options: (cat.options || []).map((o) =>
88
+ o.id === optionId ? { ...o, selected: !o.selected } : o,
89
+ ),
90
+ }
91
+ : cat,
92
+ ),
93
+ );
94
+ };
95
+
96
+ const handleDraftPriceRangeChange = (categoryKey: FilterCategoryKey, currentMin: number, currentMax: number) => {
97
+ setDraftCategories((prev) =>
98
+ prev.map((cat) =>
99
+ cat.key === categoryKey && cat.priceRange
100
+ ? { ...cat, priceRange: { ...cat.priceRange, currentMin, currentMax } }
101
+ : cat,
102
+ ),
103
+ );
104
+ };
105
+
106
+ const handleApplySingle = () => {
107
+ if (openSheet && openSheet !== "all") onApply(openSheet, draftCategories);
108
+ closeSheet();
109
+ };
110
+
111
+ const handleClearSingle = () => {
112
+ if (openSheet && openSheet !== "all") onClearFilter(openSheet);
113
+ };
114
+
115
+ const handleApplyAll = () => {
116
+ onApply("all", draftCategories);
117
+ closeSheet();
118
+ };
119
+
120
+ const handleClearAll = () => {
121
+ onClearFilter("all");
122
+ };
123
+
124
+ // Inline option chips (e.g. the four Horarios buckets) skip the sheet
125
+ // entirely — tapping one toggles it and applies immediately.
126
+ const handleInlineOptionToggle = (categoryKey: FilterCategoryKey, optionId: string) => {
127
+ const updated = categories.map((cat) =>
128
+ cat.key === categoryKey
129
+ ? {
130
+ ...cat,
131
+ options: (cat.options || []).map((o) =>
132
+ o.id === optionId ? { ...o, selected: !o.selected } : o,
133
+ ),
134
+ }
135
+ : cat,
136
+ );
137
+ onApply(categoryKey, updated);
138
+ };
139
+
140
+ return (
141
+ <div>
142
+ <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
143
+ <FilterChip
144
+ label="Filtros"
145
+ active={totalActiveCategories > 0}
146
+ isOpen={isMasterOpen}
147
+ onClick={() => openSheetFor("all")}
148
+ isFiltersButton
149
+ badgeCount={totalActiveCategories}
150
+ collapsed={isScrolled}
151
+ colors={resolvedColors}
152
+ icons={resolvedIcons}
153
+ />
154
+ <div
155
+ style={{
156
+ width: 1,
157
+ alignSelf: "stretch",
158
+ backgroundColor: resolvedColors.chipBorderColor,
159
+ opacity: 0.5,
160
+ flexShrink: 0,
161
+ }}
162
+ />
163
+ <div
164
+ ref={scrollRef}
165
+ onScroll={handleScroll}
166
+ style={{
167
+ display: "flex",
168
+ alignItems: "center",
169
+ gap: 8,
170
+ overflowX: "auto",
171
+ scrollbarWidth: "none",
172
+ paddingBottom: 2,
173
+ }}
174
+ >
175
+ {categories.map((category) => (
176
+ <FilterChip
177
+ key={category.key}
178
+ label={category.label}
179
+ active={isCategoryActive(category)}
180
+ isOpen={openSheet === category.key}
181
+ onClick={() => openSheetFor(category.key)}
182
+ iconSrc={resolvedIcons.categoryIcons?.[category.key]}
183
+ activeIconSrc={resolvedIcons.categoryIconsActive?.[category.key]}
184
+ badgeCount={getCategoryActiveCount(category)}
185
+ colors={resolvedColors}
186
+ icons={resolvedIcons}
187
+ />
188
+ ))}
189
+ {categories
190
+ .filter((category) => category.inlineOptions && category.options?.length)
191
+ .flatMap((category) =>
192
+ (category.options || []).map((option) => (
193
+ <FilterChip
194
+ key={`${category.key}:${option.id}`}
195
+ label={option.label}
196
+ active={option.selected}
197
+ isOpen={false}
198
+ onClick={() => handleInlineOptionToggle(category.key, option.id)}
199
+ iconSrc={option.icon ? resolvedIcons.timeOfDayIcons?.[option.icon] : undefined}
200
+ colors={resolvedColors}
201
+ icons={resolvedIcons}
202
+ />
203
+ )),
204
+ )}
205
+ </div>
206
+ </div>
207
+
208
+ <SingleFilterSheet
209
+ category={openCategory}
210
+ onClose={closeSheet}
211
+ onToggleOption={(optionId) => openSheet && openSheet !== "all" && handleDraftToggleOption(openSheet, optionId)}
212
+ onPriceRangeChange={(min, max) => openSheet && openSheet !== "all" && handleDraftPriceRangeChange(openSheet, min, max)}
213
+ onClear={handleClearSingle}
214
+ onApply={handleApplySingle}
215
+ currencySign={currencySign}
216
+ colors={resolvedColors}
217
+ icons={resolvedIcons}
218
+ />
219
+
220
+ <FiltersMasterSheet
221
+ isOpen={isMasterOpen}
222
+ categories={draftCategories}
223
+ onClose={closeSheet}
224
+ onToggleOption={handleDraftToggleOption}
225
+ onPriceRangeChange={handleDraftPriceRangeChange}
226
+ onClearAll={handleClearAll}
227
+ onApplyAll={handleApplyAll}
228
+ currencySign={currencySign}
229
+ colors={resolvedColors}
230
+ icons={resolvedIcons}
231
+ />
232
+ </div>
233
+ );
234
+ };
235
+
236
+ export default FilterSortBarMobile;
@@ -0,0 +1,208 @@
1
+ import React, { useMemo, useState } from "react";
2
+ import { BottomSheet } from "../../ui/BottomSheet";
3
+ import { CloseIcon, SearchIcon } from "./icons";
4
+ import OptionCheckboxList from "./OptionCheckboxList";
5
+ import PriceRangeSlider from "./PriceRangeSlider";
6
+ import SheetFooter from "./SheetFooter";
7
+ import { FilterCategory, FilterCategoryKey, FilterSortBarColors, FilterSortBarIcons } from "./types";
8
+
9
+ interface FiltersMasterSheetProps {
10
+ isOpen: boolean;
11
+ categories: FilterCategory[];
12
+ onClose: () => void;
13
+ onToggleOption: (categoryKey: FilterCategoryKey, optionId: string) => void;
14
+ onPriceRangeChange: (categoryKey: FilterCategoryKey, currentMin: number, currentMax: number) => void;
15
+ onClearAll: () => void;
16
+ onApplyAll: () => void;
17
+ currencySign: string;
18
+ colors: Required<FilterSortBarColors>;
19
+ icons?: FilterSortBarIcons;
20
+ }
21
+
22
+ const FiltersMasterSheet = ({
23
+ isOpen,
24
+ categories,
25
+ onClose,
26
+ onToggleOption,
27
+ onPriceRangeChange,
28
+ onClearAll,
29
+ onApplyAll,
30
+ currencySign,
31
+ colors,
32
+ icons,
33
+ }: FiltersMasterSheetProps) => {
34
+ const [activeKey, setActiveKey] = useState<FilterCategoryKey>(categories[0]?.key ?? "time");
35
+ const [searchQuery, setSearchQuery] = useState("");
36
+
37
+ const activeCategory = categories.find((c) => c.key === activeKey) || categories[0];
38
+
39
+ const filteredOptions = useMemo(() => {
40
+ if (!activeCategory?.options) return [];
41
+ if (!searchQuery.trim()) return activeCategory.options;
42
+ const q = searchQuery.trim().toLowerCase();
43
+ return activeCategory.options.filter((o) => o.label.toLowerCase().includes(q));
44
+ }, [activeCategory, searchQuery]);
45
+
46
+ if (!isOpen) return null;
47
+
48
+ return (
49
+ <BottomSheet isOpen={isOpen} onClose={onClose} showBackdrop height="75vh" padding="20px 0 0px">
50
+ <div style={{ display: "flex", flexDirection: "column", height: "calc(75vh - 40px)" }}>
51
+ <div
52
+ style={{
53
+ display: "flex",
54
+ alignItems: "center",
55
+ justifyContent: "space-between",
56
+ padding: "0 20px 14px",
57
+ }}
58
+ >
59
+ <span style={{ fontSize: 16, fontWeight: 700, color: "#464647" }}>Filtros</span>
60
+ <div
61
+ onClick={onClose}
62
+ style={{ cursor: "pointer", padding: 4, WebkitTapHighlightColor: "transparent" }}
63
+ >
64
+ <CloseIcon size={14} src={icons?.closeIcon} />
65
+ </div>
66
+ </div>
67
+
68
+ <div style={{ padding: "0 20px 14px" }}>
69
+ <div
70
+ style={{
71
+ display: "flex",
72
+ alignItems: "center",
73
+ gap: 8,
74
+ border: "1px solid #E5E5E5",
75
+ borderRadius: 10,
76
+ padding: "9px 12px",
77
+ }}
78
+ >
79
+ <SearchIcon size={15} src={icons?.searchIcon} />
80
+ <input
81
+ value={searchQuery}
82
+ onChange={(e) => setSearchQuery(e.target.value)}
83
+ placeholder="Buscar filtro"
84
+ style={{
85
+ border: "none",
86
+ outline: "none",
87
+ fontSize: 13,
88
+ flex: 1,
89
+ color: "#464647",
90
+ background: "transparent",
91
+ }}
92
+ />
93
+ </div>
94
+ </div>
95
+
96
+ <div style={{ display: "flex", flex: 1, overflow: "hidden", position: "relative", borderTop: "1px solid #E5E5E5", borderBottom: "1px solid #E5E5E5" }}>
97
+ <div
98
+ style={{
99
+ position: "absolute",
100
+ top: 0,
101
+ bottom: 0,
102
+ left: 0,
103
+ width: 130,
104
+ backgroundColor: "#F3F4F6",
105
+ zIndex: 0,
106
+ }}
107
+ />
108
+ <div
109
+ style={{
110
+ width: 130,
111
+ flexShrink: 0,
112
+ position: "relative",
113
+ zIndex: 1,
114
+ }}
115
+ >
116
+ {categories.map((cat) => {
117
+ const isActive = cat.key === activeKey;
118
+ return (
119
+ <div
120
+ key={cat.key}
121
+ onClick={() => setActiveKey(cat.key)}
122
+ style={{
123
+ position: "relative",
124
+ display: "flex",
125
+ alignItems: "center",
126
+ minHeight: 44,
127
+ boxSizing: "border-box",
128
+ padding: "8px 8px 8px 20px",
129
+ fontSize: 13,
130
+ lineHeight: 1.25,
131
+ fontWeight: isActive ? 700 : 400,
132
+ color: isActive ? colors.primaryColor : "#464647",
133
+ backgroundColor: isActive ? "#fff" : "transparent",
134
+ cursor: "pointer",
135
+ WebkitTapHighlightColor: "transparent",
136
+ userSelect: "none",
137
+ WebkitUserSelect: "none",
138
+ }}
139
+ >
140
+ {isActive && (
141
+ <div
142
+ style={{
143
+ position: "absolute",
144
+ left: 0,
145
+ top: 8,
146
+ bottom: 8,
147
+ width: 4,
148
+ borderTopRightRadius: 4,
149
+ borderBottomRightRadius: 4,
150
+ backgroundColor: colors.primaryColor,
151
+ }}
152
+ />
153
+ )}
154
+ {cat.label}
155
+ </div>
156
+ );
157
+ })}
158
+ </div>
159
+
160
+ <div
161
+ style={{ flex: 1, overflowY: "auto", paddingLeft: 16, paddingRight: 20, position: "relative", zIndex: 1, backgroundColor: "#fff" }}
162
+ >
163
+ {activeCategory && (
164
+ <div
165
+ style={{
166
+ fontSize: 11,
167
+ fontWeight: 700,
168
+ color: "#9B9B9B",
169
+ textTransform: "uppercase",
170
+ letterSpacing: 0.4,
171
+ marginBottom: 5,
172
+ marginTop: 15,
173
+ }}
174
+ >
175
+ {activeCategory.sectionLabel || activeCategory.label}
176
+ </div>
177
+ )}
178
+ {activeCategory?.kind === "price" && activeCategory.priceRange ? (
179
+ <PriceRangeSlider
180
+ value={activeCategory.priceRange}
181
+ onChange={(min, max) => onPriceRangeChange(activeCategory.key, min, max)}
182
+ currencySign={currencySign}
183
+ primaryColor={colors.primaryColor}
184
+ minLabel="Mínimo"
185
+ maxLabel="Máximo"
186
+ />
187
+ ) : (
188
+ <OptionCheckboxList
189
+ options={filteredOptions}
190
+ kind={activeCategory?.kind || "checkbox"}
191
+ onToggle={(optionId) => onToggleOption(activeCategory.key, optionId)}
192
+ primaryColor={colors.primaryColor}
193
+ starColor={colors.buttonColor || colors.primaryColor}
194
+ icons={icons}
195
+ />
196
+ )}
197
+ </div>
198
+ </div>
199
+
200
+ <div style={{ padding: "0px 20px 0" }}>
201
+ <SheetFooter onClear={onClearAll} onApply={onApplyAll} colors={colors} />
202
+ </div>
203
+ </div>
204
+ </BottomSheet>
205
+ );
206
+ };
207
+
208
+ export default FiltersMasterSheet;
@@ -0,0 +1,86 @@
1
+ import React from "react";
2
+ import { FilterOptionItem, FilterCategoryKind, FilterSortBarIcons } from "./types";
3
+ import { CheckboxIcon, StarIcon, TimeOfDayIcon } from "./icons";
4
+
5
+ interface OptionCheckboxListProps {
6
+ options: FilterOptionItem[];
7
+ kind: FilterCategoryKind;
8
+ onToggle: (optionId: string) => void;
9
+ primaryColor: string;
10
+ /** Filled-star color for rating options — the app's button/CTA color, not the chip accent. */
11
+ starColor: string;
12
+ icons?: FilterSortBarIcons;
13
+ }
14
+
15
+ const RatingLabel = ({
16
+ stars,
17
+ label,
18
+ starColor,
19
+ icons,
20
+ }: {
21
+ stars?: number;
22
+ label: string;
23
+ starColor: string;
24
+ icons?: FilterSortBarIcons;
25
+ }) => (
26
+ <div style={{ display: "flex", alignItems: "center", gap: 1, whiteSpace: "nowrap" }}>
27
+ {Array.from({ length: 5 }, (_, i) => (
28
+ <StarIcon
29
+ key={i}
30
+ size={10}
31
+ filled={stars != null && i < stars}
32
+ color={starColor}
33
+ filledSrc={icons?.starFilledIcon}
34
+ emptySrc={icons?.starEmptyIcon}
35
+ />
36
+ ))}
37
+ <span style={{ marginLeft: 6 }}>{label}</span>
38
+ </div>
39
+ );
40
+
41
+ const OptionCheckboxList = ({ options, kind, onToggle, primaryColor, starColor, icons }: OptionCheckboxListProps) => {
42
+ if (!options?.length) {
43
+ return (
44
+ <div style={{ padding: "24px 0", textAlign: "center", color: "#9B9B9B", fontSize: 13 }}>
45
+ No hay opciones disponibles
46
+ </div>
47
+ );
48
+ }
49
+
50
+ return (
51
+ <div>
52
+ {options.map((option) => (
53
+ <div
54
+ key={option.id}
55
+ onClick={() => onToggle(option.id)}
56
+ style={{
57
+ display: "flex",
58
+ alignItems: "center",
59
+ justifyContent: "space-between",
60
+ padding: "12px 2px",
61
+ cursor: "pointer",
62
+ fontSize: 13,
63
+ color: "#464647",
64
+ WebkitTapHighlightColor: "transparent",
65
+ userSelect: "none",
66
+ WebkitUserSelect: "none",
67
+ }}
68
+ >
69
+ {kind === "rating" ? (
70
+ <RatingLabel stars={option.stars} label={option.label} starColor={starColor} icons={icons} />
71
+ ) : option.icon ? (
72
+ <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
73
+ <TimeOfDayIcon variant={option.icon} size={16} src={icons?.timeOfDayIcons?.[option.icon]} />
74
+ <span>{option.label}</span>
75
+ </div>
76
+ ) : (
77
+ <span>{option.label}</span>
78
+ )}
79
+ <CheckboxIcon checked={option.selected} color={primaryColor} spriteSrc={icons?.checkBoxIcon} />
80
+ </div>
81
+ ))}
82
+ </div>
83
+ );
84
+ };
85
+
86
+ export default OptionCheckboxList;