kupos-ui-components-lib 10.4.69 → 10.4.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/DiscountBanners/DiscountBannerDesktop.js +14 -5
- package/dist/components/DiscountBanners/DiscountBannerMobile.js +14 -5
- package/dist/ui/DiscountBanners/DiscountBanners.js +14 -5
- package/dist/ui/DiscountBanners/discountUtils.js +11 -1
- package/package.json +1 -1
- package/src/components/DiscountBanners/DiscountBannerDesktop.tsx +19 -4
- package/src/components/DiscountBanners/DiscountBannerMobile.tsx +19 -4
- package/src/ui/DiscountBanners/DiscountBanners.tsx +19 -4
- package/src/ui/DiscountBanners/discountUtils.ts +9 -0
|
@@ -76,6 +76,9 @@ const OPERATOR_LOGO_LAYOUT = {
|
|
|
76
76
|
};
|
|
77
77
|
const MAX_VISIBLE_OPERATOR_LOGOS = 3;
|
|
78
78
|
const FLAME_CENTER = { left: "76.8%", top: "56.4%" };
|
|
79
|
+
// Lifts operator logos off the green Remate art, where light logos otherwise blend in.
|
|
80
|
+
// drop-shadow follows the logo's shape rather than its box.
|
|
81
|
+
const REMATE_LOGO_SHADOW = "drop-shadow(0 0 1px rgba(0,0,0,0.45)) drop-shadow(0 1px 2px rgba(0,0,0,0.3))";
|
|
79
82
|
const REMATE_KEY = "remate";
|
|
80
83
|
function isRemateCard(card) {
|
|
81
84
|
return card.isWowDeal || card.key === REMATE_KEY;
|
|
@@ -151,8 +154,10 @@ class DiscountBannersDesktop extends React.Component {
|
|
|
151
154
|
const isActive = activeKey === card.key;
|
|
152
155
|
const operatorLogos = (_a = card.operatorLogos) !== null && _a !== void 0 ? _a : [];
|
|
153
156
|
const hasLogos = operatorLogos.length > 0;
|
|
154
|
-
//
|
|
155
|
-
|
|
157
|
+
// Remate lists every operator; other cards show three logos, with a "+N"
|
|
158
|
+
// taking the third slot when there are more
|
|
159
|
+
const visibleLogos = !isRemateCard(card) &&
|
|
160
|
+
operatorLogos.length > MAX_VISIBLE_OPERATOR_LOGOS
|
|
156
161
|
? operatorLogos.slice(0, MAX_VISIBLE_OPERATOR_LOGOS - 1)
|
|
157
162
|
: operatorLogos;
|
|
158
163
|
const isDimmed = activeKey != null && !isActive;
|
|
@@ -187,12 +192,16 @@ class DiscountBannersDesktop extends React.Component {
|
|
|
187
192
|
React.createElement("span", { className: `bold-text ${sizeClasses.subtitle}`, style: Object.assign({ color: accentColor }, (hasLogos ? logoLayout.subtitle : undefined)) }, card.campaignTexts
|
|
188
193
|
? card.campaignTexts.text3
|
|
189
194
|
: card.subtitle)),
|
|
190
|
-
hasLogos && (React.createElement("span", { className: "bold-text flex items-center whitespace-nowrap text-[#464647]",
|
|
195
|
+
hasLogos && (React.createElement("span", { className: "bold-text flex items-center whitespace-nowrap text-[#464647]",
|
|
196
|
+
// Clip sideways only, so the Remate logo shadow isn't cut off above and below
|
|
197
|
+
style: Object.assign(Object.assign({}, logoLayout.row), { overflowX: "clip" }) },
|
|
191
198
|
visibleLogos.map((logo) => {
|
|
192
199
|
var _a;
|
|
193
|
-
return (React.createElement("img", { key: logo.url, src: logo.url, alt: (_a = logo.name) !== null && _a !== void 0 ? _a : "", className: "block object-contain object-left", style: logoLayout.logo
|
|
200
|
+
return (React.createElement("img", { key: logo.url, src: logo.url, alt: (_a = logo.name) !== null && _a !== void 0 ? _a : "", className: "block object-contain object-left", style: Object.assign(Object.assign({}, logoLayout.logo), (isRemateCard(card)
|
|
201
|
+
? { filter: REMATE_LOGO_SHADOW }
|
|
202
|
+
: undefined)) }));
|
|
194
203
|
}),
|
|
195
|
-
visibleLogos.length < operatorLogos.length && (React.createElement("span",
|
|
204
|
+
visibleLogos.length < operatorLogos.length && (React.createElement("span", { style: { flexShrink: 0 } },
|
|
196
205
|
"+",
|
|
197
206
|
operatorLogos.length - visibleLogos.length))))));
|
|
198
207
|
})));
|
|
@@ -76,6 +76,9 @@ const OPERATOR_LOGO_LAYOUT = {
|
|
|
76
76
|
};
|
|
77
77
|
const MAX_VISIBLE_OPERATOR_LOGOS = 3;
|
|
78
78
|
const FLAME_CENTER = { left: "76.8%", top: "56.4%" };
|
|
79
|
+
// Lifts operator logos off the green Remate art, where light logos otherwise blend in.
|
|
80
|
+
// drop-shadow follows the logo's shape rather than its box.
|
|
81
|
+
const REMATE_LOGO_SHADOW = "drop-shadow(0 0 1px rgba(0,0,0,0.45)) drop-shadow(0 1px 2px rgba(0,0,0,0.3))";
|
|
79
82
|
const REMATE_KEY = "remate";
|
|
80
83
|
function isRemateCard(card) {
|
|
81
84
|
return card.isWowDeal || card.key === REMATE_KEY;
|
|
@@ -149,8 +152,10 @@ class DiscountBannersMobile extends React.Component {
|
|
|
149
152
|
const isActive = activeKey === card.key;
|
|
150
153
|
const operatorLogos = (_a = card.operatorLogos) !== null && _a !== void 0 ? _a : [];
|
|
151
154
|
const hasLogos = operatorLogos.length > 0;
|
|
152
|
-
//
|
|
153
|
-
|
|
155
|
+
// Remate lists every operator; other cards show three logos, with a "+N"
|
|
156
|
+
// taking the third slot when there are more
|
|
157
|
+
const visibleLogos = !isRemateCard(card) &&
|
|
158
|
+
operatorLogos.length > MAX_VISIBLE_OPERATOR_LOGOS
|
|
154
159
|
? operatorLogos.slice(0, MAX_VISIBLE_OPERATOR_LOGOS - 1)
|
|
155
160
|
: operatorLogos;
|
|
156
161
|
const isDimmed = activeKey != null && !isActive;
|
|
@@ -185,12 +190,16 @@ class DiscountBannersMobile extends React.Component {
|
|
|
185
190
|
React.createElement("span", { className: `bold-text ${sizeClasses.subtitle}`, style: Object.assign({ color: accentColor }, (hasLogos ? logoLayout.subtitle : undefined)) }, card.campaignTexts
|
|
186
191
|
? card.campaignTexts.text3
|
|
187
192
|
: card.subtitle)),
|
|
188
|
-
hasLogos && (React.createElement("span", { className: "bold-text flex items-center whitespace-nowrap text-[#464647]",
|
|
193
|
+
hasLogos && (React.createElement("span", { className: "bold-text flex items-center whitespace-nowrap text-[#464647]",
|
|
194
|
+
// Clip sideways only, so the Remate logo shadow isn't cut off above and below
|
|
195
|
+
style: Object.assign(Object.assign({}, logoLayout.row), { overflowX: "clip" }) },
|
|
189
196
|
visibleLogos.map((logo) => {
|
|
190
197
|
var _a;
|
|
191
|
-
return (React.createElement("img", { key: logo.url, src: logo.url, alt: (_a = logo.name) !== null && _a !== void 0 ? _a : "", className: "block object-contain object-left", style: logoLayout.logo
|
|
198
|
+
return (React.createElement("img", { key: logo.url, src: logo.url, alt: (_a = logo.name) !== null && _a !== void 0 ? _a : "", className: "block object-contain object-left", style: Object.assign(Object.assign({}, logoLayout.logo), (isRemateCard(card)
|
|
199
|
+
? { filter: REMATE_LOGO_SHADOW }
|
|
200
|
+
: undefined)) }));
|
|
192
201
|
}),
|
|
193
|
-
visibleLogos.length < operatorLogos.length && (React.createElement("span",
|
|
202
|
+
visibleLogos.length < operatorLogos.length && (React.createElement("span", { style: { flexShrink: 0 } },
|
|
194
203
|
"+",
|
|
195
204
|
operatorLogos.length - visibleLogos.length))))));
|
|
196
205
|
})));
|
|
@@ -76,6 +76,9 @@ const OPERATOR_LOGO_LAYOUT = {
|
|
|
76
76
|
};
|
|
77
77
|
const MAX_VISIBLE_OPERATOR_LOGOS = 3;
|
|
78
78
|
const FLAME_CENTER = { left: "76.8%", top: "56.4%" };
|
|
79
|
+
// Lifts operator logos off the green Remate art, where light logos otherwise blend in.
|
|
80
|
+
// drop-shadow follows the logo's shape rather than its box.
|
|
81
|
+
const REMATE_LOGO_SHADOW = "drop-shadow(0 0 1px rgba(0,0,0,0.45)) drop-shadow(0 1px 2px rgba(0,0,0,0.3))";
|
|
79
82
|
const REMATE_KEY = "remate";
|
|
80
83
|
function isRemateCard(card) {
|
|
81
84
|
return card.isWowDeal || card.key === REMATE_KEY;
|
|
@@ -132,8 +135,10 @@ const DiscountBanners = ({ services = [], wowDealData, backgroundImages = [], ar
|
|
|
132
135
|
const isActive = activeKey === card.key;
|
|
133
136
|
const operatorLogos = (_a = card.operatorLogos) !== null && _a !== void 0 ? _a : [];
|
|
134
137
|
const hasLogos = operatorLogos.length > 0;
|
|
135
|
-
//
|
|
136
|
-
|
|
138
|
+
// Remate lists every operator; other cards show three logos, with a "+N"
|
|
139
|
+
// taking the third slot when there are more
|
|
140
|
+
const visibleLogos = !isRemateCard(card) &&
|
|
141
|
+
operatorLogos.length > MAX_VISIBLE_OPERATOR_LOGOS
|
|
137
142
|
? operatorLogos.slice(0, MAX_VISIBLE_OPERATOR_LOGOS - 1)
|
|
138
143
|
: operatorLogos;
|
|
139
144
|
const isDimmed = activeKey != null && !isActive;
|
|
@@ -164,12 +169,16 @@ const DiscountBanners = ({ services = [], wowDealData, backgroundImages = [], ar
|
|
|
164
169
|
React.createElement("span", { className: `flex flex-col ${sizeClasses.column}` },
|
|
165
170
|
React.createElement("span", { className: `bold-text text-[#464647] ${sizeClasses.discount}`, style: hasLogos ? logoLayout.discount : undefined }, card.discountLabel),
|
|
166
171
|
React.createElement("span", { className: `bold-text ${sizeClasses.subtitle}`, style: Object.assign({ color: accentColor }, (hasLogos ? logoLayout.subtitle : undefined)) }, card.subtitle)),
|
|
167
|
-
hasLogos && (React.createElement("span", { className: "bold-text flex items-center whitespace-nowrap text-[#464647]",
|
|
172
|
+
hasLogos && (React.createElement("span", { className: "bold-text flex items-center whitespace-nowrap text-[#464647]",
|
|
173
|
+
// Clip sideways only, so the Remate logo shadow isn't cut off above and below
|
|
174
|
+
style: Object.assign(Object.assign({}, logoLayout.row), { overflowX: "clip" }) },
|
|
168
175
|
visibleLogos.map((logo) => {
|
|
169
176
|
var _a;
|
|
170
|
-
return (React.createElement("img", { key: logo.url, src: logo.url, alt: (_a = logo.name) !== null && _a !== void 0 ? _a : "", className: "block object-contain object-left", style: logoLayout.logo
|
|
177
|
+
return (React.createElement("img", { key: logo.url, src: logo.url, alt: (_a = logo.name) !== null && _a !== void 0 ? _a : "", className: "block object-contain object-left", style: Object.assign(Object.assign({}, logoLayout.logo), (isRemateCard(card)
|
|
178
|
+
? { filter: REMATE_LOGO_SHADOW }
|
|
179
|
+
: undefined)) }));
|
|
171
180
|
}),
|
|
172
|
-
visibleLogos.length < operatorLogos.length && (React.createElement("span",
|
|
181
|
+
visibleLogos.length < operatorLogos.length && (React.createElement("span", { style: { flexShrink: 0 } },
|
|
173
182
|
"+",
|
|
174
183
|
operatorLogos.length - visibleLogos.length))))));
|
|
175
184
|
})));
|
|
@@ -115,7 +115,7 @@ function toDiscountLabel(discount, currencySign) {
|
|
|
115
115
|
}
|
|
116
116
|
// Groups services into one card per discount category. Remate/wow-deal is always the first card.
|
|
117
117
|
export function deriveDiscountCards(services = [], wowDealData, currencySign = "$") {
|
|
118
|
-
var _a, _b, _c, _d;
|
|
118
|
+
var _a, _b, _c, _d, _e;
|
|
119
119
|
const cards = new Map();
|
|
120
120
|
const wowServices = wowDealData === null || wowDealData === void 0 ? void 0 : wowDealData.services;
|
|
121
121
|
const hasWowDeal = !!wowDealData &&
|
|
@@ -207,5 +207,15 @@ export function deriveDiscountCards(services = [], wowDealData, currencySign = "
|
|
|
207
207
|
if (showsLogo)
|
|
208
208
|
addOperatorLogo(cards.get(key), service);
|
|
209
209
|
}
|
|
210
|
+
// Operators sharing a discount are listed alphabetically; logos without a name go last.
|
|
211
|
+
for (const card of cards.values()) {
|
|
212
|
+
(_e = card.operatorLogos) === null || _e === void 0 ? void 0 : _e.sort((a, b) => {
|
|
213
|
+
if (!a.name)
|
|
214
|
+
return b.name ? 1 : 0;
|
|
215
|
+
if (!b.name)
|
|
216
|
+
return -1;
|
|
217
|
+
return a.name.localeCompare(b.name, "es", { sensitivity: "base" });
|
|
218
|
+
});
|
|
219
|
+
}
|
|
210
220
|
return Array.from(cards.values());
|
|
211
221
|
}
|
package/package.json
CHANGED
|
@@ -89,6 +89,11 @@ const MAX_VISIBLE_OPERATOR_LOGOS = 3;
|
|
|
89
89
|
|
|
90
90
|
const FLAME_CENTER = { left: "76.8%", top: "56.4%" };
|
|
91
91
|
|
|
92
|
+
// Lifts operator logos off the green Remate art, where light logos otherwise blend in.
|
|
93
|
+
// drop-shadow follows the logo's shape rather than its box.
|
|
94
|
+
const REMATE_LOGO_SHADOW =
|
|
95
|
+
"drop-shadow(0 0 1px rgba(0,0,0,0.45)) drop-shadow(0 1px 2px rgba(0,0,0,0.3))";
|
|
96
|
+
|
|
92
97
|
const REMATE_KEY = "remate";
|
|
93
98
|
|
|
94
99
|
function isRemateCard(card: DiscountBannerCard): boolean {
|
|
@@ -207,8 +212,10 @@ class DiscountBannersDesktop extends React.Component<
|
|
|
207
212
|
const isActive = activeKey === card.key;
|
|
208
213
|
const operatorLogos = card.operatorLogos ?? [];
|
|
209
214
|
const hasLogos = operatorLogos.length > 0;
|
|
210
|
-
//
|
|
215
|
+
// Remate lists every operator; other cards show three logos, with a "+N"
|
|
216
|
+
// taking the third slot when there are more
|
|
211
217
|
const visibleLogos =
|
|
218
|
+
!isRemateCard(card) &&
|
|
212
219
|
operatorLogos.length > MAX_VISIBLE_OPERATOR_LOGOS
|
|
213
220
|
? operatorLogos.slice(0, MAX_VISIBLE_OPERATOR_LOGOS - 1)
|
|
214
221
|
: operatorLogos;
|
|
@@ -307,7 +314,8 @@ class DiscountBannersDesktop extends React.Component<
|
|
|
307
314
|
{hasLogos && (
|
|
308
315
|
<span
|
|
309
316
|
className="bold-text flex items-center whitespace-nowrap text-[#464647]"
|
|
310
|
-
|
|
317
|
+
// Clip sideways only, so the Remate logo shadow isn't cut off above and below
|
|
318
|
+
style={{ ...logoLayout.row, overflowX: "clip" }}
|
|
311
319
|
>
|
|
312
320
|
{visibleLogos.map((logo) => (
|
|
313
321
|
<img
|
|
@@ -315,11 +323,18 @@ class DiscountBannersDesktop extends React.Component<
|
|
|
315
323
|
src={logo.url}
|
|
316
324
|
alt={logo.name ?? ""}
|
|
317
325
|
className="block object-contain object-left"
|
|
318
|
-
style={
|
|
326
|
+
style={{
|
|
327
|
+
...logoLayout.logo,
|
|
328
|
+
...(isRemateCard(card)
|
|
329
|
+
? { filter: REMATE_LOGO_SHADOW }
|
|
330
|
+
: undefined),
|
|
331
|
+
}}
|
|
319
332
|
/>
|
|
320
333
|
))}
|
|
321
334
|
{visibleLogos.length < operatorLogos.length && (
|
|
322
|
-
<span
|
|
335
|
+
<span style={{ flexShrink: 0 }}>
|
|
336
|
+
+{operatorLogos.length - visibleLogos.length}
|
|
337
|
+
</span>
|
|
323
338
|
)}
|
|
324
339
|
</span>
|
|
325
340
|
)}
|
|
@@ -89,6 +89,11 @@ const MAX_VISIBLE_OPERATOR_LOGOS = 3;
|
|
|
89
89
|
|
|
90
90
|
const FLAME_CENTER = { left: "76.8%", top: "56.4%" };
|
|
91
91
|
|
|
92
|
+
// Lifts operator logos off the green Remate art, where light logos otherwise blend in.
|
|
93
|
+
// drop-shadow follows the logo's shape rather than its box.
|
|
94
|
+
const REMATE_LOGO_SHADOW =
|
|
95
|
+
"drop-shadow(0 0 1px rgba(0,0,0,0.45)) drop-shadow(0 1px 2px rgba(0,0,0,0.3))";
|
|
96
|
+
|
|
92
97
|
const REMATE_KEY = "remate";
|
|
93
98
|
|
|
94
99
|
function isRemateCard(card: DiscountBannerCard): boolean {
|
|
@@ -200,8 +205,10 @@ class DiscountBannersMobile extends React.Component<
|
|
|
200
205
|
const isActive = activeKey === card.key;
|
|
201
206
|
const operatorLogos = card.operatorLogos ?? [];
|
|
202
207
|
const hasLogos = operatorLogos.length > 0;
|
|
203
|
-
//
|
|
208
|
+
// Remate lists every operator; other cards show three logos, with a "+N"
|
|
209
|
+
// taking the third slot when there are more
|
|
204
210
|
const visibleLogos =
|
|
211
|
+
!isRemateCard(card) &&
|
|
205
212
|
operatorLogos.length > MAX_VISIBLE_OPERATOR_LOGOS
|
|
206
213
|
? operatorLogos.slice(0, MAX_VISIBLE_OPERATOR_LOGOS - 1)
|
|
207
214
|
: operatorLogos;
|
|
@@ -300,7 +307,8 @@ class DiscountBannersMobile extends React.Component<
|
|
|
300
307
|
{hasLogos && (
|
|
301
308
|
<span
|
|
302
309
|
className="bold-text flex items-center whitespace-nowrap text-[#464647]"
|
|
303
|
-
|
|
310
|
+
// Clip sideways only, so the Remate logo shadow isn't cut off above and below
|
|
311
|
+
style={{ ...logoLayout.row, overflowX: "clip" }}
|
|
304
312
|
>
|
|
305
313
|
{visibleLogos.map((logo) => (
|
|
306
314
|
<img
|
|
@@ -308,11 +316,18 @@ class DiscountBannersMobile extends React.Component<
|
|
|
308
316
|
src={logo.url}
|
|
309
317
|
alt={logo.name ?? ""}
|
|
310
318
|
className="block object-contain object-left"
|
|
311
|
-
style={
|
|
319
|
+
style={{
|
|
320
|
+
...logoLayout.logo,
|
|
321
|
+
...(isRemateCard(card)
|
|
322
|
+
? { filter: REMATE_LOGO_SHADOW }
|
|
323
|
+
: undefined),
|
|
324
|
+
}}
|
|
312
325
|
/>
|
|
313
326
|
))}
|
|
314
327
|
{visibleLogos.length < operatorLogos.length && (
|
|
315
|
-
<span
|
|
328
|
+
<span style={{ flexShrink: 0 }}>
|
|
329
|
+
+{operatorLogos.length - visibleLogos.length}
|
|
330
|
+
</span>
|
|
316
331
|
)}
|
|
317
332
|
</span>
|
|
318
333
|
)}
|
|
@@ -88,6 +88,11 @@ const MAX_VISIBLE_OPERATOR_LOGOS = 3;
|
|
|
88
88
|
|
|
89
89
|
const FLAME_CENTER = { left: "76.8%", top: "56.4%" };
|
|
90
90
|
|
|
91
|
+
// Lifts operator logos off the green Remate art, where light logos otherwise blend in.
|
|
92
|
+
// drop-shadow follows the logo's shape rather than its box.
|
|
93
|
+
const REMATE_LOGO_SHADOW =
|
|
94
|
+
"drop-shadow(0 0 1px rgba(0,0,0,0.45)) drop-shadow(0 1px 2px rgba(0,0,0,0.3))";
|
|
95
|
+
|
|
91
96
|
const REMATE_KEY = "remate";
|
|
92
97
|
|
|
93
98
|
function isRemateCard(card: DiscountBannerCard): boolean {
|
|
@@ -182,8 +187,10 @@ const DiscountBanners: React.FC<DiscountBannersProps> = ({
|
|
|
182
187
|
const isActive = activeKey === card.key;
|
|
183
188
|
const operatorLogos = card.operatorLogos ?? [];
|
|
184
189
|
const hasLogos = operatorLogos.length > 0;
|
|
185
|
-
//
|
|
190
|
+
// Remate lists every operator; other cards show three logos, with a "+N"
|
|
191
|
+
// taking the third slot when there are more
|
|
186
192
|
const visibleLogos =
|
|
193
|
+
!isRemateCard(card) &&
|
|
187
194
|
operatorLogos.length > MAX_VISIBLE_OPERATOR_LOGOS
|
|
188
195
|
? operatorLogos.slice(0, MAX_VISIBLE_OPERATOR_LOGOS - 1)
|
|
189
196
|
: operatorLogos;
|
|
@@ -278,7 +285,8 @@ const DiscountBanners: React.FC<DiscountBannersProps> = ({
|
|
|
278
285
|
{hasLogos && (
|
|
279
286
|
<span
|
|
280
287
|
className="bold-text flex items-center whitespace-nowrap text-[#464647]"
|
|
281
|
-
|
|
288
|
+
// Clip sideways only, so the Remate logo shadow isn't cut off above and below
|
|
289
|
+
style={{ ...logoLayout.row, overflowX: "clip" }}
|
|
282
290
|
>
|
|
283
291
|
{visibleLogos.map((logo) => (
|
|
284
292
|
<img
|
|
@@ -286,11 +294,18 @@ const DiscountBanners: React.FC<DiscountBannersProps> = ({
|
|
|
286
294
|
src={logo.url}
|
|
287
295
|
alt={logo.name ?? ""}
|
|
288
296
|
className="block object-contain object-left"
|
|
289
|
-
style={
|
|
297
|
+
style={{
|
|
298
|
+
...logoLayout.logo,
|
|
299
|
+
...(isRemateCard(card)
|
|
300
|
+
? { filter: REMATE_LOGO_SHADOW }
|
|
301
|
+
: undefined),
|
|
302
|
+
}}
|
|
290
303
|
/>
|
|
291
304
|
))}
|
|
292
305
|
{visibleLogos.length < operatorLogos.length && (
|
|
293
|
-
<span
|
|
306
|
+
<span style={{ flexShrink: 0 }}>
|
|
307
|
+
+{operatorLogos.length - visibleLogos.length}
|
|
308
|
+
</span>
|
|
294
309
|
)}
|
|
295
310
|
</span>
|
|
296
311
|
)}
|
|
@@ -267,5 +267,14 @@ export function deriveDiscountCards(
|
|
|
267
267
|
if (showsLogo) addOperatorLogo(cards.get(key)!, service);
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
// Operators sharing a discount are listed alphabetically; logos without a name go last.
|
|
271
|
+
for (const card of cards.values()) {
|
|
272
|
+
card.operatorLogos?.sort((a, b) => {
|
|
273
|
+
if (!a.name) return b.name ? 1 : 0;
|
|
274
|
+
if (!b.name) return -1;
|
|
275
|
+
return a.name.localeCompare(b.name, "es", { sensitivity: "base" });
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
270
279
|
return Array.from(cards.values());
|
|
271
280
|
}
|