kupos-ui-components-lib 10.4.13 → 10.4.14

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 (56) hide show
  1. package/dist/assets/images/anims/service_list/60_percent_anim.json +1 -0
  2. package/dist/assets/images/filter/arrow-down.png +0 -0
  3. package/dist/assets/images/filter/rapido.svg +14 -0
  4. package/dist/components/FilterSortBar/FilterChip.d.ts +6 -2
  5. package/dist/components/FilterSortBar/FilterChip.js +10 -32
  6. package/dist/components/FilterSortBar/FilterSortBarMobile.d.ts +1 -1
  7. package/dist/components/FilterSortBar/FilterSortBarMobile.js +71 -38
  8. package/dist/components/FilterSortBar/FiltersMasterSheet.d.ts +3 -1
  9. package/dist/components/FilterSortBar/FiltersMasterSheet.js +28 -97
  10. package/dist/components/FilterSortBar/OptionCheckboxList.js +5 -16
  11. package/dist/components/FilterSortBar/PriceRangeSlider.js +17 -55
  12. package/dist/components/FilterSortBar/SheetFooter.js +3 -30
  13. package/dist/components/FilterSortBar/defaultIcons.js +9 -0
  14. package/dist/components/FilterSortBar/icons.d.ts +7 -0
  15. package/dist/components/FilterSortBar/icons.js +8 -1
  16. package/dist/components/FilterSortBar/index.d.ts +1 -1
  17. package/dist/components/FilterSortBar/types.d.ts +34 -0
  18. package/dist/components/PaymentOptions/PaymentOptionsDesktop.d.ts +4 -0
  19. package/dist/components/PaymentOptions/PaymentOptionsDesktop.js +5 -0
  20. package/dist/components/PaymentOptions/PaymentOptionsMobile.d.ts +4 -0
  21. package/dist/components/PaymentOptions/PaymentOptionsMobile.js +5 -0
  22. package/dist/components/PaymentOptions/ResponsivePaymentOptions.d.ts +3 -0
  23. package/dist/components/PaymentOptions/ResponsivePaymentOptions.js +10 -0
  24. package/dist/components/PaymentOptions/index.d.ts +5 -0
  25. package/dist/components/PaymentOptions/index.js +4 -0
  26. package/dist/components/PaymentOptions/mobileTypes.d.ts +0 -0
  27. package/dist/components/PaymentOptions/mobileTypes.js +0 -0
  28. package/dist/components/PaymentOptions/type.d.ts +4 -0
  29. package/dist/components/PaymentOptions/type.js +1 -0
  30. package/dist/components/PaymentSideBar/PaymentSideBarMobile.js +1 -1
  31. package/dist/components/ServiceItem/ExpandedDropdown.d.ts +19 -0
  32. package/dist/components/ServiceItem/ExpandedDropdown.js +28 -0
  33. package/dist/components/ServiceItem/ServiceItemDesktopPeru.d.ts +6 -0
  34. package/dist/components/ServiceItem/ServiceItemDesktopPeru.js +612 -0
  35. package/dist/index.d.ts +1 -1
  36. package/dist/styles.css +233 -0
  37. package/dist/ui/PaymentCardMobile/PaymentCardMobile.js +18 -4
  38. package/dist/ui/TopAmenities/PromoCountdown.d.ts +18 -0
  39. package/dist/ui/TopAmenities/PromoCountdown.js +55 -0
  40. package/package.json +1 -1
  41. package/src/assets/images/filter/arrow-down.png +0 -0
  42. package/src/assets/images/filter/rapido.svg +14 -0
  43. package/src/components/FilterSortBar/FilterChip.tsx +31 -35
  44. package/src/components/FilterSortBar/FilterSortBarMobile.tsx +106 -66
  45. package/src/components/FilterSortBar/FiltersMasterSheet.tsx +34 -118
  46. package/src/components/FilterSortBar/OptionCheckboxList.tsx +5 -16
  47. package/src/components/FilterSortBar/PriceRangeSlider.tsx +20 -61
  48. package/src/components/FilterSortBar/SheetFooter.tsx +4 -32
  49. package/src/components/FilterSortBar/defaultIcons.ts +11 -0
  50. package/src/components/FilterSortBar/icons.tsx +38 -2
  51. package/src/components/FilterSortBar/index.ts +2 -0
  52. package/src/components/FilterSortBar/types.ts +36 -0
  53. package/src/components/PaymentSideBar/PaymentSideBarMobile.tsx +4 -1
  54. package/src/index.ts +2 -0
  55. package/src/ui/PaymentCardMobile/PaymentCardMobile.tsx +101 -31
  56. package/src/components/FilterSortBar/SingleFilterSheet.tsx +0 -82
@@ -72,66 +72,37 @@ const PriceRangeSlider = ({
72
72
  };
73
73
 
74
74
  return (
75
- <div style={{ padding: "8px 4px 4px" }}>
76
- <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 28 }}>
77
- <div
78
- style={{
79
- flex: 1,
80
- border: "1px solid #E5E5E5",
81
- borderRadius: 8,
82
- padding: "6px 10px 8px",
83
- }}
84
- >
85
- <div style={{ fontSize: 11, color: "#9B9B9B", marginBottom: 2 }}>{minLabel}</div>
86
- <div style={{ fontSize: 13, fontWeight: 700, color: "#464647" }}>
75
+ <div className="p-[8px_4px_4px]">
76
+ <div className="flex items-center gap-[8px] mb-[28px]">
77
+ <div className="flex-1 border border-[#E5E5E5] rounded-[8px] p-[6px_10px_8px]">
78
+ <div className="text-[11px] text-[#464647] mb-[2px]">{minLabel}</div>
79
+ <div className="text-[13px] font-bold text-[#464647]">
87
80
  {formatCurrency(currentMin, currencySign)}
88
81
  </div>
89
82
  </div>
90
- <span style={{ color: "#9B9B9B", fontSize: 13 }}>-</span>
91
- <div
92
- style={{
93
- flex: 1,
94
- border: "1px solid #E5E5E5",
95
- borderRadius: 8,
96
- padding: "6px 10px 8px",
97
- }}
98
- >
99
- <div style={{ fontSize: 11, color: "#9B9B9B", marginBottom: 2 }}>{maxLabel}</div>
100
- <div style={{ fontSize: 13, fontWeight: 700, color: "#464647" }}>
83
+ <span className="text-[#9B9B9B] text-[13px]">-</span>
84
+ <div className="flex-1 border border-[#E5E5E5] rounded-[8px] p-[6px_10px_8px]">
85
+ <div className="text-[11px] text-[#464647] mb-[2px]">{maxLabel}</div>
86
+ <div className="text-[13px] font-bold text-[#464647]">
101
87
  {formatCurrency(currentMax, currencySign)}
102
88
  </div>
103
89
  </div>
104
90
  </div>
105
91
 
106
- <div style={{ padding: `0 ${THUMB_RADIUS}px`, overflow: "visible" }}>
92
+ <div className="overflow-visible" style={{ padding: `0 ${THUMB_RADIUS}px` }}>
107
93
  <div
108
94
  ref={trackRef}
109
- style={{
110
- position: "relative",
111
- height: THUMB_SIZE,
112
- display: "flex",
113
- alignItems: "center",
114
- touchAction: "none",
115
- overflow: "visible",
116
- }}
95
+ className="relative flex items-center overflow-visible [touch-action:none]"
96
+ style={{ height: THUMB_SIZE }}
117
97
  >
98
+ <div className="absolute left-0 right-0 h-[3px] rounded-[2px] bg-[#E5E5E5]" />
99
+ {/* Stops right at each thumb's border instead of reaching its center
100
+ (which would run the line underneath half of the circle). */}
118
101
  <div
102
+ className="absolute h-[3px] rounded-[2px]"
119
103
  style={{
120
- position: "absolute",
121
- left: 0,
122
- right: 0,
123
- height: 3,
124
- borderRadius: 2,
125
- backgroundColor: "#E5E5E5",
126
- }}
127
- />
128
- <div
129
- style={{
130
- position: "absolute",
131
- left: `${minPct}%`,
132
- right: `${100 - maxPct}%`,
133
- height: 3,
134
- borderRadius: 2,
104
+ left: `calc(${minPct}% + ${THUMB_RADIUS}px)`,
105
+ right: `calc(${100 - maxPct}% + ${THUMB_RADIUS}px)`,
135
106
  backgroundColor: primaryColor,
136
107
  }}
137
108
  />
@@ -148,32 +119,20 @@ const PriceRangeSlider = ({
148
119
  aria-valuemin={min}
149
120
  aria-valuemax={max}
150
121
  aria-valuenow={thumb === "min" ? currentMin : currentMax}
122
+ className="absolute rounded-full bg-white cursor-grab shadow-[0_1px_4px_rgba(0,0,0,0.25)]"
151
123
  style={{
152
- position: "absolute",
153
124
  left: `${pct}%`,
154
125
  transform: "translateX(-50%)",
155
126
  width: THUMB_SIZE,
156
127
  height: THUMB_SIZE,
157
- borderRadius: "50%",
158
- backgroundColor: "#fff",
159
128
  border: `2px solid ${primaryColor}`,
160
- boxShadow: "0 1px 4px rgba(0,0,0,0.25)",
161
- cursor: "grab",
162
129
  zIndex: thumb === "min" ? 2 : 3,
163
130
  }}
164
131
  />
165
132
  ))}
166
133
  </div>
167
134
  </div>
168
- <div
169
- style={{
170
- display: "flex",
171
- justifyContent: "space-between",
172
- fontSize: 11,
173
- color: "#9B9B9B",
174
- marginTop: 8,
175
- }}
176
- >
135
+ <div className="flex justify-between text-[11px] text-[#464647] mt-[8px]">
177
136
  <span>{formatCurrency(min, currencySign)}</span>
178
137
  <span>{formatCurrency(max, currencySign)}</span>
179
138
  </div>
@@ -12,45 +12,17 @@ const SheetFooter = ({ onClear, onApply, colors }: SheetFooterProps) => {
12
12
  const buttonTextColor = colors.buttonTextColor || colors.primaryTextColor;
13
13
 
14
14
  return (
15
- <div
16
- style={{
17
- display: "flex",
18
- gap: 12,
19
- paddingTop: 16,
20
- marginBottom: 8,
21
- }}
22
- >
15
+ <div className="flex gap-[12px] pt-[16px] mb-[8px]">
23
16
  <button
24
17
  onClick={onClear}
25
- style={{
26
- flex: 1,
27
- padding: "12px 16px",
28
- borderRadius: 10,
29
- border: "1px solid #E5E5E5",
30
- background: "#fff",
31
- color: "#464647",
32
- fontSize: 14,
33
- fontWeight: 600,
34
- cursor: "pointer",
35
- WebkitTapHighlightColor: "transparent",
36
- }}
18
+ className="flex-1 px-[16px] py-[12px] rounded-[10px] border border-[#E5E5E5] bg-[#fff] text-[#464647] text-[14px] font-semibold cursor-pointer [-webkit-tap-highlight-color:transparent] font-['DMSans-Regular',sans-serif]"
37
19
  >
38
20
  Borrar filtro
39
21
  </button>
40
22
  <button
41
23
  onClick={onApply}
42
- style={{
43
- flex: 1,
44
- padding: "12px 16px",
45
- borderRadius: 10,
46
- border: "none",
47
- background: buttonColor,
48
- color: buttonTextColor,
49
- fontSize: 14,
50
- fontWeight: 600,
51
- cursor: "pointer",
52
- WebkitTapHighlightColor: "transparent",
53
- }}
24
+ className="flex-1 px-[16px] py-[12px] rounded-[10px] border-none text-[14px] font-semibold cursor-pointer [-webkit-tap-highlight-color:transparent] font-['DMSans-Regular',sans-serif]"
25
+ style={{ background: buttonColor, color: buttonTextColor }}
54
26
  >
55
27
  Aplicar
56
28
  </button>
@@ -10,6 +10,11 @@
10
10
  import { FilterSortBarIcons } from "./types";
11
11
 
12
12
  export const DEFAULT_ICONS: FilterSortBarIcons = {
13
+ // Sourced from src/assets/images/filter/arrow-down.png (re-exported without
14
+ // its stray XMP orientation metadata, which browsers don't honor for PNGs —
15
+ // using the file as-is would render sideways).
16
+ sortArrowIcon:
17
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAABDCAYAAADatIboAAADC0lEQVR4nNWaMWsUQRTHf741ixERIqIICopgY4qA2iimM42Q4gqvsxEE8WPYWKRJkc4qnYWNlUqaFJIuVfIFBOUaUQhETvQiA2/gMc7e3uZuc/d+sMUNdzvv/+b/ZuaYgclTAqvADtAHesBLYAEnLABvNPgjYAB8AJba6ExaGoEgYk4/nwIuAWe9CJgzgbeO4BzBOYJzBOcIzhGcIzhHcI7gHME5gnME5wjOEZwjOEdwjuAcwTmCcwTnCM4RnCM4R3CO4BzBOYJzBOcIzhGcIzhHcI7gHME5gnME55wG5oFrwHngB/AN+MXsUQAXgSvAH+BriDcIeAg8AW4Du8Bb4DPwl9mhBG4BXeAe8Bv4CGyjJ+oHeih9qIfSd3VkjsNV4J2+Lz4hMffHyPwisG4Oz8PzBdgMNXBDFQbOAA+A18Aj0z4tSuAm8EKzH8+g0cP0O8FCn4DHwAU9nD6ntgo++zlFOxXAdeA58FTjigQL7QFb6B2GTbXPwAzRgdppuaGdJmGhQjO/lomrr7bvxgskpXo+XND4br48MCJW9KUnIaAwwfcywW9r8PO5Kt9UEbbzkIH3DUZiHAGFFuyrJJlp5suqHy+ZqzKpnbb0HlDZkoBCM7+eCf7Q9G9r4T9KUxPpSPSNncoJCyg08xsV/YbgO6PaONppw6wPuUzMT1DAoukvzfxOTX/U2SmdBaKIqsJuIqAALhvbHGUKtnHwdiQWVUQvefmBFvZK5uWjCoi2WdNVtVHBjkrsxN6BqyvsUQWEgs3Zpq/f70zqomC6TuRqwhZ2nYDCBN+rsE1njL1YpYi4oaoSEb1aJyCOaG6qPFbBTsJOfTMSVQKWawp2R38/dJ4fl6pthy3sZ7pe2AD3tX09KdjBsO1BmyLiSBxm7LSrQdr2nrbntgf7o6ywbdopXezSbUhsy9lu+yRsU7ftiMt/GnTVY22zelK2GTYScRebZrjqibaZWuabFPawzM9E8FW72JyIWODdWQs+tVNudhroPN/07+nU7BRHot/kzwgzgrVTmP/j9qCV4P8B53h9Q9i84bIAAAAASUVORK5CYII=",
13
18
  filterIcon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEyLjM5NTggNy4wMDAyNkg1LjE4ODcxTTUuMTg4NzEgNy4wMDAyNkM1LjE4ODcxIDYuNjYyOTkgNS4wNTQxNSA2LjMzOTU0IDQuODE1NjYgNi4xMDEwNUM0LjU3NzE4IDUuODYyNTcgNC4yNTM3MyA1LjcyODU5IDMuOTE2NDYgNS43Mjg1OUMzLjU3OTE5IDUuNzI4NTkgMy4yNTU3NCA1Ljg2MjU3IDMuMDE3MjYgNi4xMDEwNUMyLjc3ODc3IDYuMzM5NTQgMi42NDQ3OSA2LjY2Mjk5IDIuNjQ0NzkgNy4wMDAyNk01LjE4ODcxIDcuMDAwMjZDNS4xODg3MSA3LjMzNzUyIDUuMDU0MTUgNy42NjA5OCA0LjgxNTY2IDcuODk5NDZDNC41NzcxOCA4LjEzNzk0IDQuMjUzNzMgOC4yNzE5MiAzLjkxNjQ2IDguMjcxOTJDMy41NzkxOSA4LjI3MTkyIDMuMjU1NzQgOC4xMzc5NCAzLjAxNzI2IDcuODk5NDZDMi43Nzg3NyA3LjY2MDk4IDIuNjQ0NzkgNy4zMzc1MiAyLjY0NDc5IDcuMDAwMjZNMi42NDQ3OSA3LjAwMDI2SDEuNjA0MTNNMTIuMzk1OCAxMC44NTQzSDkuMDQyNzlNOS4wNDI3OSAxMC44NTQzQzkuMDQyNzkgMTEuMTkxNiA4LjkwODQ5IDExLjUxNTUgOC42Njk5NSAxMS43NTRDOC40MzE0MiAxMS45OTI1IDguMTA3ODkgMTIuMTI2NiA3Ljc3MDU0IDEyLjEyNjZDNy40MzMyOCAxMi4xMjY2IDcuMTA5ODIgMTEuOTkyIDYuODcxMzQgMTEuNzUzNUM2LjYzMjg2IDExLjUxNSA2LjQ5ODg4IDExLjE5MTYgNi40OTg4OCAxMC44NTQzTTkuMDQyNzkgMTAuODU0M0M5LjA0Mjc5IDEwLjUxNyA4LjkwODQ5IDEwLjE5MzcgOC42Njk5NSA5Ljk1NTIyQzguNDMxNDIgOS43MTY2OCA4LjEwNzg5IDkuNTgyNjcgNy43NzA1NCA5LjU4MjY3QzcuNDMzMjggOS41ODI2NyA3LjEwOTgyIDkuNzE2NjUgNi44NzEzNCA5Ljk1NTE0QzYuNjMyODYgMTAuMTkzNiA2LjQ5ODg4IDEwLjUxNyA2LjQ5ODg4IDEwLjg1NDNNNi40OTg4OCAxMC44NTQzSDEuNjA0MTNNMTIuMzk1OCAzLjE0NjE3SDEwLjU4NDVNMTAuNTg0NSAzLjE0NjE3QzEwLjU4NDUgMi45NzkxOCAxMC41NTExIDIuODEzODEgMTAuNDg3MiAyLjY1OTUzQzEwLjQyMzMgMi41MDUyNCAxMC4zMjk2IDIuMzY1MDYgMTAuMjExNSAyLjI0Njk3QzEwLjA5MzQgMi4xMjg4OCA5Ljk1MzIyIDIuMDM1MjEgOS43OTg5NCAxLjk3MTMxQzkuNjQ0NjUgMS45MDc0IDkuNDc5MjkgMS44NzQ1MSA5LjMxMjI5IDEuODc0NTFDOC45NzUwMyAxLjg3NDUxIDguNjUxNTcgMi4wMDg0OSA4LjQxMzA5IDIuMjQ2OTdDOC4xNzQ2MSAyLjQ4NTQ1IDguMDQwNjMgMi44MDg5MSA4LjA0MDYzIDMuMTQ2MTdNMTAuNTg0NSAzLjE0NjE3QzEwLjU4NDUgMy4zMTMxNyAxMC41NTExIDMuNDc4NTMgMTAuNDg3MiAzLjYzMjgyQzEwLjQyMzMgMy43ODcxMSAxMC4zMjk2IDMuOTI3MjkgMTAuMjExNSA0LjA0NTM4QzEwLjA5MzQgNC4xNjM0NiA5Ljk1MzIyIDQuMjU3MTMgOS43OTg5NCA0LjMyMTA0QzkuNjQ0NjUgNC4zODQ5NSA5LjQ3OTI5IDQuNDE3ODQgOS4zMTIyOSA0LjQxNzg0QzguOTc1MDMgNC40MTc4NCA4LjY1MTU3IDQuMjgzODYgOC40MTMwOSA0LjA0NTM4QzguMTc0NjEgMy44MDY4OSA4LjA0MDYzIDMuNDgzNDQgOC4wNDA2MyAzLjE0NjE3TTguMDQwNjMgMy4xNDYxN0gxLjYwNDEzIiBzdHJva2U9IiM0NjQ2NDciIHN0cm9rZS13aWR0aD0iMC44NzUiIHN0cm9rZS1taXRlcmxpbWl0PSIxMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjwvc3ZnPgo=",
14
19
  searchIcon: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUuNjg3NSAxMC4wNjI1QzguMTAzNzUgMTAuMDYyNSAxMC4wNjI1IDguMTAzNzUgMTAuMDYyNSA1LjY4NzVDMTAuMDYyNSAzLjI3MTI1IDguMTAzNzUgMS4zMTI1IDUuNjg3NSAxLjMxMjVDMy4yNzEyNSAxLjMxMjUgMS4zMTI1IDMuMjcxMjUgMS4zMTI1IDUuNjg3NUMxLjMxMjUgOC4xMDM3NSAzLjI3MTI1IDEwLjA2MjUgNS42ODc1IDEwLjA2MjVaIiBzdHJva2U9IiM0NjQ2NDciLz4KPHBhdGggZD0iTTguNzUgOC43NUwxMS4zNzUgMTEuMzc1IiBzdHJva2U9IiM0NjQ2NDciIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8L3N2Zz4K",
15
20
  categoryIcons: {
@@ -26,4 +31,10 @@ export const DEFAULT_ICONS: FilterSortBarIcons = {
26
31
  sunset: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzI5MF8zOTIpIj4KPG1hc2sgaWQ9Im1hc2swXzI5MF8zOTIiIHN0eWxlPSJtYXNrLXR5cGU6bHVtaW5hbmNlIiBtYXNrVW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4PSIwIiB5PSIwIiB3aWR0aD0iMTQiIGhlaWdodD0iMTQiPgo8cGF0aCBkPSJNMTQgMEgwVjE0SDE0VjBaIiBmaWxsPSJ3aGl0ZSIvPgo8L21hc2s+CjxnIG1hc2s9InVybCgjbWFzazBfMjkwXzM5MikiPgo8cGF0aCBkPSJNMy41MzUyOCAxMi4xOTg5QzQuMjQxMSAxMi41MTk3IDUuMTM5NDQgMTIuNTE5NyA1Ljg0NTMgMTIuMTk4OUM2LjU1MTEzIDExLjg3ODEgNy40NDk0OCAxMS44NzgxIDguMTU1MjkgMTIuMTk4OUM4Ljg2MTEgMTIuNTE5NyA5Ljc1OTUgMTIuNTE5NyAxMC40NjUzIDEyLjE5ODlNNy4wMDAyMiAxLjc1VjIuOTE2NjdNMi4zMzM1OCA3LjU4MzMxSDEuMTY2OU0zLjY4MzQ4IDQuMjY2NTdMMi44NTg1MiAzLjQ0MTYxTTEwLjMxNjggNC4yNjY1N0wxMS4xNDE4IDMuNDQxNjFNMTIuODMzNiA3LjU4MzMxSDExLjY2N000LjA4MzU4IDcuNTgzMzFDNC4wODM1OCA1Ljk3MjUyIDUuMzg5NDEgNC42NjY2NyA3LjAwMDIyIDQuNjY2NjdDOC42MTEwNiA0LjY2NjY3IDkuOTE2OTYgNS45NzI1MiA5LjkxNjk2IDcuNTgzMzFNMS4yMjUzIDEwLjE1NzNDMS45MzExMyA5LjgzNjQ5IDIuODI5NDcgOS44MzY0OSAzLjUzNTMgMTAuMTU3M0M0LjI0MTEzIDEwLjQ3ODIgNS4xMzk0NyAxMC40NzgyIDUuODQ1MyAxMC4xNTczQzYuNTUxMTMgOS44MzY0OSA3LjQ0OTQ4IDkuODM2NDkgOC4xNTUyOSAxMC4xNTczQzguODYxMSAxMC40NzgyIDkuNzU5NSAxMC40NzgyIDEwLjQ2NTMgMTAuMTU3M0MxMS4xNzExIDkuODM2NDkgMTIuMDY5NSA5LjgzNjQ5IDEyLjc3NTMgMTAuMTU3MyIgc3Ryb2tlPSIjNDY0NjQ3IiBzdHJva2Utd2lkdGg9IjEuMDUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8L2c+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfMjkwXzM5MiI+CjxyZWN0IHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K",
27
32
  night: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIHZpZXdCb3g9IjAgMCAxNCAxNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTEyLjI1IDcuNDYwODFDMTIuMTU4MyA4LjQ1Mzc2IDExLjc4NTYgOS40MDAwNyAxMS4xNzU2IDEwLjE4OUMxMC41NjU3IDEwLjk3NzkgOS43NDM3MiAxMS41NzY4IDguODA1ODYgMTEuOTE1NUM3Ljg2NzkzIDEyLjI1NDMgNi44NTI5OSAxMi4zMTg5IDUuODc5NzEgMTIuMTAxOUM0LjkwNjQ0IDExLjg4NDkgNC4wMTUwOCAxMS4zOTUyIDMuMzA5OTcgMTAuNjlDMi42MDQ4NSA5Ljk4NDg5IDIuMTE1MTQgOS4wOTM1NiAxLjg5ODEyIDguMTIwMjhDMS42ODExIDcuMTQ3IDEuNzQ1NzYgNi4xMzIwNSAyLjA4NDUyIDUuMTk0MThDMi40MjMyOSA0LjI1NjI5IDMuMDIyMTYgMy40MzQzIDMuODExMDUgMi44MjQzNUM0LjU5OTk0IDIuMjE0NDIgNS41NDYyMiAxLjg0MTc3IDYuNTM5MTcgMS43NUM1Ljk1Nzg0IDIuNTM2NDkgNS42NzgwOSAzLjUwNTUxIDUuNzUwODEgNC40ODA4MkM1LjgyMzU1IDUuNDU2MTQgNi4yNDM5MiA2LjM3Mjk2IDYuOTM1NDggNy4wNjQ1NEM3LjYyNzA2IDcuNzU2MDcgOC41NDM4NSA4LjE3NjQ5IDkuNTE5MTYgOC4yNDkyMkMxMC40OTQ1IDguMzIxOTUgMTEuNDYzNSA4LjA0MjE2IDEyLjI1IDcuNDYwODFaIiBzdHJva2U9IiM0NjQ2NDciIHN0cm9rZS13aWR0aD0iMC45OTc1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPHBhdGggZD0iTTExLjM3NSAyLjUwODUzQzExLjYzMjcgMi41MDg1MyAxMS44NDE3IDIuMjk5NiAxMS44NDE3IDIuMDQxODZDMTEuODQxNyAxLjc4NDEzIDExLjYzMjcgMS41NzUyIDExLjM3NSAxLjU3NTJDMTEuMTE3MiAxLjU3NTIgMTAuOTA4MyAxLjc4NDEzIDEwLjkwODMgMi4wNDE4NkMxMC45MDgzIDIuMjk5NiAxMS4xMTcyIDIuNTA4NTMgMTEuMzc1IDIuNTA4NTNaIiBmaWxsPSIjNDY0NjQ3Ii8+CjxwYXRoIGQ9Ik0xMi41NDE3IDQuMTQxODlDMTIuNzM1IDQuMTQxODkgMTIuODkxNyAzLjk4NTIgMTIuODkxNyAzLjc5MTg5QzEyLjg5MTcgMy41OTg1OSAxMi43MzUgMy40NDE4OSAxMi41NDE3IDMuNDQxODlDMTIuMzQ4NCAzLjQ0MTg5IDEyLjE5MTcgMy41OTg1OSAxMi4xOTE3IDMuNzkxODlDMTIuMTkxNyAzLjk4NTIgMTIuMzQ4NCA0LjE0MTg5IDEyLjU0MTcgNC4xNDE4OVoiIGZpbGw9IiM0NjQ2NDciLz4KPC9zdmc+Cg==",
28
33
  },
34
+ sortIcons: {
35
+ // Rápido (duration sort) — the only sort chip with no existing category
36
+ // or time-of-day icon to reuse. Sourced from src/assets/images/filter/rapido.svg.
37
+ duration:
38
+ "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTMiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMyAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTcuMjY0NzEgMS40MTUyOEM5LjkyMDg0IDEuNDE1MzIgMTIuMDgyMSAzLjU3NjUzIDEyLjA4MjEgNi4yMzI2N0MxMi4wODIxIDguODg4NzggOS45MjA4MiAxMS4wNSA3LjI2NDcxIDExLjA1QzQuNjA4NTcgMTEuMDUgMi40NDczNSA4Ljg4ODggMi40NDczMyA2LjIzMjY3QzIuNDQ3MzMgMy41NzY1MSA0LjYwODU1IDEuNDE1MjggNy4yNjQ3MSAxLjQxNTI4Wk03LjI2NDcxIDEuOTg5NUM0LjkyMzYyIDEuOTg5NSAzLjAyMTU1IDMuODkxNTggMy4wMjE1NSA2LjIzMjY3QzMuMDIxNTcgOC41NzM3MyA0LjkyMzY0IDEwLjQ3NTggNy4yNjQ3MSAxMC40NzU4QzkuNjA1NzUgMTAuNDc1OCAxMS41MDc4IDguNTczNzEgMTEuNTA3OSA2LjIzMjY3QzExLjUwNzkgMy44OTE2IDkuNjA1NzcgMS45ODk1MyA3LjI2NDcxIDEuOTg5NVoiIGZpbGw9IiM0NjQ2NDciIHN0cm9rZT0iIzQ2NDY0NyIgc3Ryb2tlLXdpZHRoPSIwLjEiLz4KPHBhdGggZD0iTTcuMjY0MjUgMi41MTEyM0M5LjMxNzc5IDIuNTExMzQgMTAuOTg1OSA0LjE3OTM1IDEwLjk4NTkgNi4yMzI5MUMxMC45ODU4IDguMjg2MzggOS4zMTc3MiA5Ljk1NDQ4IDcuMjY0MjUgOS45NTQ1OUM1LjIxNDQ5IDkuOTU0NTkgMy41NDI2OCA4LjI4NjQ4IDMuNTQyNTcgNi4yMzI5MUMzLjU0MjU3IDQuMTc5MjggNS4yMTA2MyAyLjUxMTIzIDcuMjY0MjUgMi41MTEyM1pNNy4yNjQyNSAzLjA4NTQ1QzUuNTI5NDYgMy4wODU0NSA0LjExNjc5IDQuNDk4MTIgNC4xMTY3OSA2LjIzMjkxQzQuMTE2OSA3Ljk2NzYxIDUuNTI5NTMgOS4zODAzNyA3LjI2NDI1IDkuMzgwMzdDOC45OTg4OCA5LjM4MDI2IDEwLjQxMTYgNy45Njc1NCAxMC40MTE3IDYuMjMyOTFDMTAuNDExNyA0LjQ5ODE5IDguOTk4OTUgMy4wODU1NiA3LjI2NDI1IDMuMDg1NDVaIiBmaWxsPSIjNDY0NjQ3IiBzdHJva2U9IiM0NjQ2NDciIHN0cm9rZS13aWR0aD0iMC4xIi8+CjxwYXRoIGQ9Ik03LjI2NDQzIDUuNTY1OTJDNi44OTUzOCA1LjU2NTkyIDYuNTk3OSA1Ljg2MzQxIDYuNTk3OSA2LjIzMjQ2QzYuNTk3OSA2LjYwMTUxIDYuODk1MzggNi44OTkwMSA3LjI2NDQzIDYuODk5MDFDNy42MzM0OSA2Ljg5OTAxIDcuOTMwOTkgNi42MDE1MSA3LjkzMDk5IDYuMjMyNDZDNy45MzA5OSA1Ljg2MzQxIDcuNjMzNDkgNS41NjU5MiA3LjI2NDQzIDUuNTY1OTJaIiBmaWxsPSIjNDY0NjQ3Ii8+CjxwYXRoIGQ9Ik02LjUzMzkxIDEuMTI1OThMNi43NTIzMyAxLjI3Mjg0QzYuNzE0NjcgMS4xODI0NiA2LjYzMTgyIDEuMTI1OTggNi41MzM5MSAxLjEyNTk4WiIgZmlsbD0iIzQ2NDY0NyIvPgo8cGF0aCBkPSJNNy45OTQ0OSAxLjEyNTk4SDcuMjI2MjdMNi44Nzk4MiAxLjM1OTQ2SDYuNTMzMzRINi4yOTk4N1YxLjc1MTFDNi4yOTk4NyAxLjg3OTE0IDYuNDA1MzEgMS45ODQ1OCA2LjUzMzM0IDEuOTg0NThDNi41NjcyNCAxLjk4NDU4IDYuNTk3MzYgMS45NzcwNSA2LjYyNzQ5IDEuOTY1NzVDNi42Njg5MSAxLjk1ODIyIDYuODIzMzMgMS45NDMxNiA3LjI3ODk5IDEuOTQzMTZDNy43MDgzIDEuOTQzMTYgNy44NTUxNSAxLjk1ODIyIDcuODk2NTcgMS45NjU3NUM4LjAxMzMxIDIuMDE4NDcgOC4xNTI2NSAxLjk2NTc1IDguMjA5MTQgMS44NDkwMUM4LjIyNDIgMS44MTg4OSA4LjIzMTczIDEuNzg1IDguMjMxNzMgMS43NTExVjEuMzU5NDZDOC4yMzE3MyAxLjIzMTQyIDguMTI2MjkgMS4xMjU5OCA3Ljk5ODI1IDEuMTI1OThINy45OTQ0OVpNNi43NDQyMyAxLjg1Mjc4QzYuNzQ0MjMgMS44NTI3OCA2Ljc1OTMxIDEuODIyNjUgNi43NjMwOCAxLjgwMzgzQzYuNzU5MzEgMS44MTg4OSA2Ljc1MTc2IDEuODM3NzEgNi43NDQyMyAxLjg1Mjc4Wk02Ljc2MzA4IDEuODAzODNDNi43NjMwOCAxLjgwMzgzIDYuNzYzMDYgMS43ODg3NiA2Ljc2NjgyIDEuNzc3NDZDNi43NjY4MiAxLjc4NDk5IDYuNzY2ODQgMS43OTYyOSA2Ljc2MzA4IDEuODAzODNaTTcuNzY4NTQgMS44MDM4M0M3Ljc2ODU0IDEuODAzODMgNy43NzYwNyAxLjgzNzcxIDcuNzgzNiAxLjg1Mjc4QzcuNzc2MDcgMS44Mzc3MSA3Ljc3MjMxIDEuODIyNjUgNy43Njg1NCAxLjgwMzgzWk03Ljc2NDc4IDEuNzc3NDZDNy43NjQ3OCAxLjc3NzQ2IDcuNzY0NzggMS43OTI1MiA3Ljc2ODU0IDEuODAwMDZDNy43Njg1NCAxLjc5MjUyIDcuNzY4NTQgMS43ODQ5OSA3Ljc2NDc4IDEuNzc3NDZaIiBmaWxsPSIjNDY0NjQ3Ii8+CjxwYXRoIGQ9Ik03Ljk5NDM5IDEuNDY4NjdINi41MzMyNEM2LjEyNjU0IDEuNDY4NjcgNS43OTg5MSAxLjEzMzUxIDUuODAyNjcgMC43MzA1N0M1LjgwMjY3IDAuMzMxMzkzIDYuMTM0MDcgMC4wMDM3NjU4MiA2LjUzMzI0IDBINy45OTgxNUM4LjE5Mzk4IDAgOC4zNzg1IDAuMDc5MDgyOSA4LjUxNzg0IDAuMjE4NDE4QzguNjU3MTcgMC4zNTc3NTQgOC43Mjg3MiAwLjU0MjI4MSA4LjcyODcyIDAuNzM4MTAzQzguNzI4NzIgMS4xMzcyOCA4LjM5NzMzIDEuNDY0OTEgNy45OTgxNSAxLjQ2ODY3SDcuOTk0MzlaTTcuOTk0MzkgMC40NzQ0OTdINi41MzMyNEM2LjM5MzkxIDAuNDc0NDk3IDYuMjc3MTcgMC41OTEyMzUgNi4yNzcxNyAwLjczMDU3QzYuMjc3MTcgMC44NzM2NzEgNi4zOTAxNCAwLjk5MDQxMyA2LjUzMzI0IDAuOTkwNDEzSDcuOTkwNjJDOC4xMjk5NSAwLjk5MDQxMyA4LjI0NjY5IDAuODczNjc1IDguMjQ2NjkgMC43MzQzNEM4LjI0NjY5IDAuNjY2NTU1IDguMjIwMzUgMC41OTg3NjggOC4xNzEzOSAwLjU0OTgxMkM4LjEyMjQzIDAuNTAwODU3IDguMDU4NDIgMC40NzQ0OTMgNy45ODY4NyAwLjQ3MDcyOEw3Ljk5NDM5IDAuNDc0NDk3WiIgZmlsbD0iIzQ2NDY0NyIvPgo8cGF0aCBkPSJNMy42Mzc5NSAzLjcyNDQ5SDEuODY0MjZDMS43MzI0NiAzLjcyNDQ5IDEuNjI3MDEgMy42MTkwNSAxLjYyNzAxIDMuNDg3MjVDMS42MjcwMSAzLjM1NTQ0IDEuNzMyNDYgMy4yNSAxLjg2NDI2IDMuMjVIMy42Mzc5NUMzLjc2OTc1IDMuMjUgMy44NzUyIDMuMzU1NDQgMy44NzUyIDMuNDg3MjVDMy44NzUyIDMuNjE5MDUgMy43Njk3NSAzLjcyNDQ5IDMuNjM3OTUgMy43MjQ0OVoiIGZpbGw9IiM0NjQ2NDciLz4KPHBhdGggZD0iTTIuNjc3MzYgNi41NDg3SDEuNDUzNDdDMS4zMjE2NiA2LjU0ODcgMS4yMTYyMiA2LjQ0MzI2IDEuMjE2MjIgNi4zMTE0NkMxLjIxNjIyIDYuMTc5NjUgMS4zMjE2NiA2LjA3NDIyIDEuNDUzNDcgNi4wNzQyMkgyLjY3NzM2QzIuODA5MTYgNi4wNzQyMiAyLjkxNDYxIDYuMTc5NjUgMi45MTQ2MSA2LjMxMTQ2QzIuOTE0NjEgNi40NDMyNiAyLjgwOTE2IDYuNTQ4NyAyLjY3NzM2IDYuNTQ4N1oiIGZpbGw9IiM0NjQ2NDciLz4KPHBhdGggZD0iTTIuODYxNjUgNy45MzQ5M0gxLjIwODQ3QzEuMDc2NjcgNy45MzQ5MyAwLjk3MTIyMiA3LjgyOTUgMC45NzEyMjIgNy42OTc3QzAuOTcxMjIyIDcuNTY1ODkgMS4wNzY2NyA3LjQ2MDQ1IDEuMjA4NDcgNy40NjA0NUgyLjg2MTY1QzIuOTkzNDUgNy40NjA0NSAzLjA5ODkgNy41NjU4OSAzLjA5ODkgNy42OTc3QzMuMDk4OSA3LjgyOTUgMi45OTM0NSA3LjkzNDkzIDIuODYxNjUgNy45MzQ5M1oiIGZpbGw9IiM0NjQ2NDciLz4KPHBhdGggZD0iTTIuOTE0NzQgNS4xMzYzNkgwLjIzNzI0OUMwLjEwNTQ0NSA1LjEzNjM2IDAgNS4wMzA5MSAwIDQuODk5MTFDMCA0Ljc2NzMgMC4xMDU0NDUgNC42NjE4NyAwLjIzNzI0OSA0LjY2MTg3SDIuOTE0NzRDMy4wNDY1NSA0LjY2MTg3IDMuMTUxOTkgNC43NjczIDMuMTUxOTkgNC44OTkxMUMzLjE1MTk5IDUuMDMwOTEgMy4wNDY1NSA1LjEzNjM2IDIuOTE0NzQgNS4xMzYzNloiIGZpbGw9IiM0NjQ2NDciLz4KPHBhdGggZD0iTTcuMDkxMjEgNi4yMTcyOUM3LjAzMDk2IDYuMjE3MjkgNi45NzA3MSA2LjE5NDY5IDYuOTI1NTIgNi4xNDk1TDUuOTc2NTIgNS4yMTU1OEM1Ljg4MjM4IDUuMTI1MiA1Ljg4MjM4IDQuOTc0NTcgNS45NzY1MiA0Ljg4MDQyQzYuMDY2OSA0Ljc4NjI4IDYuMjE3NTQgNC43ODYyOCA2LjMxMTY5IDQuODgwNDJMNy4yNjA2NiA1LjgxNDM1QzcuMzU0OCA1LjkwNDczIDcuMzU0OCA2LjA1NTM2IDcuMjYwNjYgNi4xNDk1QzcuMjE1NDcgNi4xOTg0NiA3LjE1MTQ3IDYuMjIxMDUgNy4wOTEyMSA2LjIyMTA1VjYuMjE3MjlaIiBmaWxsPSIjNDY0NjQ3Ii8+CjxwYXRoIGQ9Ik03LjUxMzA0IDYuMjgxMzNDNy40NTI3OSA2LjI4MTMzIDcuMzkyNTMgNi4yNTg3NCA3LjM0NzM0IDYuMjEzNTVDNy4yNTMyIDYuMTE5NCA3LjI1MzIgNS45NzI1MyA3LjM0NzM0IDUuODc4MzhMOC45NTkxIDQuMjU5MDlDOS4wNTMyNSA0LjE2NDk0IDkuMjAwMTIgNC4xNjQ5NCA5LjI5NDI3IDQuMjU5MDlDOS4zODg0MSA0LjM1MzIzIDkuMzg4NDEgNC41MDAxIDkuMjk0MjcgNC41OTQyNEw3LjY4MjQ4IDYuMjEzNTVDNy42MzcyOSA2LjI1ODc0IDcuNTc3MDYgNi4yODUxIDcuNTEzMDQgNi4yODUxVjYuMjgxMzNaIiBmaWxsPSIjNDY0NjQ3Ii8+Cjwvc3ZnPgo=",
39
+ },
29
40
  };
@@ -40,7 +40,7 @@ export const ChevronDownIcon = ({
40
40
  width={size}
41
41
  height={size * 0.6}
42
42
  alt=""
43
- style={{ transform: open ? "rotate(180deg)" : "none", transition: "transform 0.2s ease", flexShrink: 0 }}
43
+ className={`flex-shrink-0 transition-transform duration-200 ease-in-out ${open ? "rotate-180" : "rotate-0"}`}
44
44
  />
45
45
  ) : (
46
46
  <svg
@@ -49,12 +49,48 @@ export const ChevronDownIcon = ({
49
49
  viewBox="0 0 10 6"
50
50
  fill="none"
51
51
  xmlns="http://www.w3.org/2000/svg"
52
- style={{ transform: open ? "rotate(180deg)" : "none", transition: "transform 0.2s ease", flexShrink: 0 }}
52
+ className={`flex-shrink-0 transition-transform duration-200 ease-in-out ${open ? "rotate-180" : "rotate-0"}`}
53
53
  >
54
54
  <path d="M1 1L5 5L9 1" stroke={color} strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" />
55
55
  </svg>
56
56
  );
57
57
 
58
+ export const SortArrowIcon = ({
59
+ size = 9,
60
+ color = "#464647",
61
+ direction = "desc",
62
+ src,
63
+ /** True once the chip is highlighted (colored background) — flips the raster arrow to white via CSS filter, since unlike the SVG fallback it can't take `color` directly. */
64
+ active,
65
+ }: {
66
+ size?: number;
67
+ color?: string;
68
+ direction?: "asc" | "desc";
69
+ src?: string;
70
+ active?: boolean;
71
+ }) =>
72
+ src ? (
73
+ // arrow-down.png is taller than wide (48x67) — keep its aspect ratio
74
+ // instead of forcing a square box, which would squash it.
75
+ <img
76
+ src={src}
77
+ width={size}
78
+ height={Math.round(size * 1.4)}
79
+ alt=""
80
+ className={`flex-shrink-0 ${direction === "asc" ? "rotate-180" : "rotate-0"} ${active ? "brightness-0 invert" : ""}`}
81
+ />
82
+ ) : (
83
+ <svg width={size} height={size} viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg" className="flex-shrink-0">
84
+ <path
85
+ d={direction === "asc" ? "M2 6.2L5 3.2L8 6.2" : "M2 3.8L5 6.8L8 3.8"}
86
+ stroke={color}
87
+ strokeWidth="1.4"
88
+ strokeLinecap="round"
89
+ strokeLinejoin="round"
90
+ />
91
+ </svg>
92
+ );
93
+
58
94
  export const SearchIcon = ({
59
95
  size = 16,
60
96
  color = "#9B9B9B",
@@ -12,4 +12,6 @@ export type {
12
12
  FilterSortBarIcons,
13
13
  FilterSortBarMobileProps,
14
14
  TimeOfDayVariant,
15
+ SortDirection,
16
+ SortOption,
15
17
  } from "./types";
@@ -16,6 +16,10 @@ export interface FilterOptionItem {
16
16
  selected: boolean;
17
17
  stars?: number;
18
18
  icon?: TimeOfDayVariant;
19
+ /** Excludes this option from the category's inline top-bar chips (still shown/selectable inside the Filtros sheet) — for an option whose concept already has its own standalone chip elsewhere, e.g. an inline time bucket duplicating a "Después de las 6 PM" sort chip. */
20
+ excludeFromInline?: boolean;
21
+ /** True when `selected` is only a display mirror of an active sort chip (e.g. a cohort sort chip's matching checkbox), not a real filter selection. Renders checked like any selected option, but is excluded from active-filter counts — the sort chip itself already accounts for that selection, so counting both would double it. */
22
+ checkedViaSort?: boolean;
19
23
  }
20
24
 
21
25
  export interface PriceRangeValue {
@@ -34,6 +38,8 @@ export interface FilterCategory {
34
38
  options?: FilterOptionItem[];
35
39
  priceRange?: PriceRangeValue;
36
40
  inlineOptions?: boolean;
41
+ /** When true, this category gets no standalone top-bar chip — it's still a tab inside the Filtros master sheet, just reachable only via the "Filtros" button. */
42
+ hideChip?: boolean;
37
43
  }
38
44
 
39
45
  export const DEFAULT_CATEGORY_LABELS: Record<FilterCategoryKey, string> = {
@@ -45,6 +51,29 @@ export const DEFAULT_CATEGORY_LABELS: Record<FilterCategoryKey, string> = {
45
51
  seatType: "Tipo de asiento",
46
52
  };
47
53
 
54
+ export type SortDirection = "asc" | "desc";
55
+
56
+ export interface SortOption {
57
+ key: string;
58
+ label: string;
59
+ /**
60
+ * Present only on directional (base) sorts — e.g. Horario, Precio, Rápido.
61
+ * These are mutually exclusive: activating one deactivates the others, and
62
+ * tapping the already-active one flips its direction instead. Always
63
+ * renders a directional arrow.
64
+ */
65
+ direction?: SortDirection;
66
+ /** Whether this sort is currently applied. */
67
+ active: boolean;
68
+ /**
69
+ * Result count for this cohort (e.g. buses departing after 6 PM), shown as
70
+ * a small badge. Only meaningful on non-directional (cohort) sorts, which
71
+ * toggle on/off independently and combine with whichever directional sort
72
+ * is active — e.g. "Horario" desc + "Después de las 6 PM" together.
73
+ */
74
+ count?: number;
75
+ }
76
+
48
77
  export interface FilterSortBarColors {
49
78
  primaryColor?: string;
50
79
  primaryTextColor?: string;
@@ -59,6 +88,8 @@ export interface FilterSortBarColors {
59
88
  export interface FilterSortBarIcons {
60
89
  closeIcon?: string;
61
90
  chevronDownIcon?: string;
91
+ /** Directional arrow shown on active base sort chips (Horario/Empresas/Precio/Rápido). Drawn pointing down; rotated 180° for ascending. */
92
+ sortArrowIcon?: string;
62
93
  searchIcon?: string;
63
94
  filterIcon?: string;
64
95
  starFilledIcon?: string;
@@ -67,12 +98,17 @@ export interface FilterSortBarIcons {
67
98
  categoryIcons?: Partial<Record<FilterCategoryKey, string>>;
68
99
  categoryIconsActive?: Partial<Record<FilterCategoryKey, string>>;
69
100
  timeOfDayIcons?: Partial<Record<TimeOfDayVariant, string>>;
101
+ /** Icons for sort chips whose key doesn't already map to a category/time-of-day icon (e.g. "duration" for a Rápido chip). Keyed by SortOption.key. */
102
+ sortIcons?: Partial<Record<string, string>>;
70
103
  }
71
104
 
72
105
  export interface FilterSortBarMobileProps {
73
106
  categories: FilterCategory[];
74
107
  onApply: (categoryKey: FilterCategoryKey | "all", updatedCategories: FilterCategory[]) => void;
75
108
  onClearFilter: (categoryKey: FilterCategoryKey | "all") => void;
109
+ /** Sort chips rendered before the filter category chips — see SortOption. */
110
+ sortOptions?: SortOption[];
111
+ onSortChange?: (updatedSortOptions: SortOption[]) => void;
76
112
  currencySign?: string;
77
113
  colors?: FilterSortBarColors;
78
114
  icons?: FilterSortBarIcons;
@@ -710,7 +710,10 @@ const PaymentSideBarMobile: React.FC<PaymentSideBarProps> = (props) => {
710
710
  style={{ color: "var(--secondary-color)" }}
711
711
  >{`${creditosLabel}(${virtualLimit}%)`}</span>
712
712
  }
713
- amount={CommonService.currency(virtualMoney, currencySign)}
713
+ amount={CommonService.currency(
714
+ ((virtualLimit ?? 0) / 100) * Number(virtualMoney ?? 0),
715
+ currencySign
716
+ )}
714
717
  customStyle={{ color: "var(--secondary-color)" }}
715
718
  className="text-[14px]"
716
719
  currency=""
package/src/index.ts CHANGED
@@ -90,6 +90,8 @@ export type {
90
90
  FilterSortBarIcons,
91
91
  FilterSortBarMobileProps,
92
92
  TimeOfDayVariant,
93
+ SortDirection,
94
+ SortOption,
93
95
  } from "./components/FilterSortBar";
94
96
  export { DEFAULT_CATEGORY_LABELS } from "./components/FilterSortBar";
95
97
  export type { SurveyProps } from "./components/Survey/types";
@@ -65,10 +65,14 @@ export const PaymentCardMobile: React.FC<PaymentSideBarProps> = (props) => {
65
65
  currency,
66
66
  insuranceData,
67
67
  isPeru,
68
+ walletMoney,
69
+ walletLabel,
70
+ virtualMoney,
71
+ virtualLimit,
72
+ creditosLabel,
68
73
  showDetails = false,
69
74
  setShowDetails,
70
75
  } = props;
71
- console.log("🚀 ~ PaymentCardMobile ~ selectSeatOnward:", selectSeatOnward);
72
76
 
73
77
  // We use a Class component wrapper below to hold the state,
74
78
  // avoiding Invalid Hook Call issues when linked in external apps with multiple React instances.
@@ -669,34 +673,37 @@ export const PaymentCardMobile: React.FC<PaymentSideBarProps> = (props) => {
669
673
  {(modalIsReturn
670
674
  ? selectedReturn[0]
671
675
  : selectedOnward[0]
672
- ).map((val: any, idx: number) => (
673
- <span
674
- key={idx}
675
- style={{
676
- display: "inline-flex",
677
- alignItems: "center",
678
- }}
679
- >
680
- {val.number ||
681
- val.seat_number ||
682
- val.seatNumber ||
683
- val.name ||
684
- ""}
685
- {val.type && (
686
- <span
687
- style={{
688
- display: "inline-flex",
689
- alignItems: "center",
690
- justifyContent: "center",
691
- fontSize: "12px",
692
- marginLeft: "4px",
693
- }}
694
- >
695
- {val.type?.charAt(0)?.toUpperCase()}
696
- </span>
697
- )}
698
- </span>
699
- ))}
676
+ ).map((val: any, idx: number) => {
677
+ console.log("🚀 ~ PaymentCardMobile ~ val:", val);
678
+ return (
679
+ <span
680
+ key={idx}
681
+ style={{
682
+ display: "inline-flex",
683
+ alignItems: "center",
684
+ }}
685
+ >
686
+ {val.number ||
687
+ val.seat_number ||
688
+ val.seatNumber ||
689
+ val.name ||
690
+ ""}
691
+ {val.type && (
692
+ <span
693
+ style={{
694
+ display: "inline-flex",
695
+ alignItems: "center",
696
+ justifyContent: "center",
697
+ fontSize: "12px",
698
+ marginLeft: "4px",
699
+ }}
700
+ >
701
+ {val.type?.charAt(0)?.toUpperCase()}
702
+ </span>
703
+ )}
704
+ </span>
705
+ );
706
+ })}
700
707
  </span>
701
708
  </div>
702
709
  )}
@@ -792,12 +799,14 @@ export const PaymentCardMobile: React.FC<PaymentSideBarProps> = (props) => {
792
799
  ) : null}
793
800
 
794
801
  {/* Service fee */}
795
- {props.serviceFeeDataStateValue?.service_fee > 0 && (
802
+ {props.serviceFeeDataStateValue
803
+ ?.total_convenience_fee_amount && (
796
804
  <div style={S.sectionRow}>
797
805
  <span style={{ flex: 1 }}>Cargo por servicio</span>
798
806
  <span style={S.sectionRowValue} className="bold-text">
799
807
  {CommonService.currency(
800
- props.serviceFeeDataStateValue.service_fee,
808
+ props.serviceFeeDataStateValue
809
+ .total_convenience_fee_amount,
801
810
  currencySign,
802
811
  )}
803
812
  </span>
@@ -899,6 +908,67 @@ export const PaymentCardMobile: React.FC<PaymentSideBarProps> = (props) => {
899
908
  </div>
900
909
  ) : null}
901
910
 
911
+ {/* Cupon */}
912
+ {removeDiscountAtomValue && (
913
+ <div style={S.sectionRow}>
914
+ <span style={{ flex: 1 }}>Cupón</span>
915
+ <span
916
+ style={{
917
+ ...S.sectionRowValue,
918
+ color: "var(--secondary-color)",
919
+ }}
920
+ className="bold-text"
921
+ >
922
+ {`- ${CommonService.currency((netFare ?? 0) - (discountAmount ?? 0), currencySign)}`}
923
+ </span>
924
+ </div>
925
+ )}
926
+
927
+ {/* Wallet */}
928
+ {walletMoney ? (
929
+ <div style={S.sectionRow}>
930
+ <span style={{ flex: 1 }}>{walletLabel}</span>
931
+ <span
932
+ style={{
933
+ ...S.sectionRowValue,
934
+ color: "var(--secondary-color)",
935
+ }}
936
+ className="bold-text"
937
+ >
938
+ {isPeru
939
+ ? CommonService.currencyFixed(
940
+ walletMoney,
941
+ currencySign,
942
+ )
943
+ : CommonService.currency(walletMoney, currencySign)}
944
+ </span>
945
+ </div>
946
+ ) : null}
947
+
948
+ {/* Virtual credits */}
949
+ {virtualMoney ? (
950
+ <div style={S.sectionRow}>
951
+ <span style={{ flex: 1 }}>
952
+ <span
953
+ style={{ color: "var(--secondary-color)" }}
954
+ >{`${creditosLabel}(${virtualLimit}%)`}</span>
955
+ </span>
956
+ <span
957
+ style={{
958
+ ...S.sectionRowValue,
959
+ color: "var(--secondary-color)",
960
+ }}
961
+ className="bold-text"
962
+ >
963
+ {CommonService.currency(
964
+ ((virtualLimit ?? 0) / 100) *
965
+ Number(virtualMoney ?? 0),
966
+ currencySign,
967
+ )}
968
+ </span>
969
+ </div>
970
+ ) : null}
971
+
902
972
  {/* Dashed separator */}
903
973
  <div
904
974
  style={{
@@ -1,82 +0,0 @@
1
- import React from "react";
2
- import { BottomSheet } from "../../ui/BottomSheet";
3
- import { CloseIcon } from "./icons";
4
- import OptionCheckboxList from "./OptionCheckboxList";
5
- import PriceRangeSlider from "./PriceRangeSlider";
6
- import SheetFooter from "./SheetFooter";
7
- import { FilterCategory, FilterSortBarColors, FilterSortBarIcons } from "./types";
8
-
9
- interface SingleFilterSheetProps {
10
- category: FilterCategory | null;
11
- onClose: () => void;
12
- onToggleOption: (optionId: string) => void;
13
- onPriceRangeChange: (currentMin: number, currentMax: number) => void;
14
- onClear: () => void;
15
- onApply: () => void;
16
- currencySign: string;
17
- colors: Required<FilterSortBarColors>;
18
- icons?: FilterSortBarIcons;
19
- }
20
-
21
- const SingleFilterSheet = ({
22
- category,
23
- onClose,
24
- onToggleOption,
25
- onPriceRangeChange,
26
- onClear,
27
- onApply,
28
- currencySign,
29
- colors,
30
- icons,
31
- }: SingleFilterSheetProps) => {
32
- return (
33
- <BottomSheet isOpen={!!category} onClose={onClose} showBackdrop maxHeight="75vh">
34
- {category && (
35
- <div>
36
- <div
37
- style={{
38
- display: "flex",
39
- alignItems: "center",
40
- justifyContent: "space-between",
41
- marginBottom: 16,
42
- }}
43
- >
44
- <span style={{ fontSize: 16, fontWeight: 700, color: "#464647" }}>
45
- {category.sheetTitle || category.sectionLabel || category.label}
46
- </span>
47
- <div
48
- onClick={onClose}
49
- style={{ cursor: "pointer", padding: 4, WebkitTapHighlightColor: "transparent" }}
50
- >
51
- <CloseIcon size={14} src={icons?.closeIcon} />
52
- </div>
53
- </div>
54
-
55
- <div style={{ maxHeight: "50vh", overflowY: "auto" }}>
56
- {category.kind === "price" && category.priceRange ? (
57
- <PriceRangeSlider
58
- value={category.priceRange}
59
- onChange={onPriceRangeChange}
60
- currencySign={currencySign}
61
- primaryColor={colors.primaryColor}
62
- />
63
- ) : (
64
- <OptionCheckboxList
65
- options={category.options || []}
66
- kind={category.kind}
67
- onToggle={onToggleOption}
68
- primaryColor={colors.primaryColor}
69
- starColor={colors.buttonColor || colors.primaryColor}
70
- icons={icons}
71
- />
72
- )}
73
- </div>
74
-
75
- <SheetFooter onClear={onClear} onApply={onApply} colors={colors} />
76
- </div>
77
- )}
78
- </BottomSheet>
79
- );
80
- };
81
-
82
- export default SingleFilterSheet;