@calo-design/cli 0.4.2 → 0.4.4
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/bin/cli.js +195 -4
- package/bin/login.js +18 -2
- package/bin/share.js +11 -2
- package/bin/templates/gallery-assets/component-gallery/meal-photo.png +0 -0
- package/bin/templates/gallery-assets/component-gallery/smoothie-photo.png +0 -0
- package/bin/templates/gallery-assets/live-flows/active-subscriber-home/ingredient-penne.png +0 -0
- package/bin/templates/gallery-index.tsx +1678 -0
- package/bin/templates/gallery-suppress.ts +50 -0
- package/package.json +1 -1
|
@@ -0,0 +1,1678 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import {
|
|
3
|
+
CaloBadge,
|
|
4
|
+
CaloBanner,
|
|
5
|
+
CaloBottomSheet,
|
|
6
|
+
CaloBottomNavigation,
|
|
7
|
+
CaloBottomActionBar,
|
|
8
|
+
CaloButton,
|
|
9
|
+
CaloCardDetailsInput,
|
|
10
|
+
CaloCartItemCard,
|
|
11
|
+
CaloCafeProductCard,
|
|
12
|
+
CaloCategoryTileGrid,
|
|
13
|
+
CaloCarousel,
|
|
14
|
+
CaloCenteredTitleTopBar,
|
|
15
|
+
CaloCheckbox,
|
|
16
|
+
CaloCompactProductRowCard,
|
|
17
|
+
CaloCompactVariantTile,
|
|
18
|
+
CaloContextScreen,
|
|
19
|
+
CaloContextTopBar,
|
|
20
|
+
CaloCustomizationOptionCard,
|
|
21
|
+
CaloDateBar,
|
|
22
|
+
CaloDateBarItem,
|
|
23
|
+
CaloDeliveryStatusCard,
|
|
24
|
+
CaloDietaryCard,
|
|
25
|
+
CaloDivider,
|
|
26
|
+
CaloFoodPreferenceWarning,
|
|
27
|
+
CaloIcon,
|
|
28
|
+
CaloIconButton,
|
|
29
|
+
CaloInput,
|
|
30
|
+
CaloInline,
|
|
31
|
+
CaloLogo,
|
|
32
|
+
CaloMacroSummary,
|
|
33
|
+
CaloMarketProductCard,
|
|
34
|
+
CaloMealRatingCard,
|
|
35
|
+
CaloMealRow,
|
|
36
|
+
CaloMenuMealRow,
|
|
37
|
+
CaloPaymentMethodList,
|
|
38
|
+
CaloPhoneInput,
|
|
39
|
+
CaloPriceText,
|
|
40
|
+
CaloProductTile,
|
|
41
|
+
CaloPromoCodeInput,
|
|
42
|
+
CaloRadio,
|
|
43
|
+
CaloRatingChip,
|
|
44
|
+
CaloScreen,
|
|
45
|
+
CaloSelectableMealTile,
|
|
46
|
+
CaloSelectionCard,
|
|
47
|
+
CaloSelectionCardMacros,
|
|
48
|
+
CaloSelectionCardPrice,
|
|
49
|
+
CaloSelectionCardRadioGroup,
|
|
50
|
+
CaloSelectionCardRadioItem,
|
|
51
|
+
CaloStack,
|
|
52
|
+
CaloStepper,
|
|
53
|
+
CaloInlineHeader,
|
|
54
|
+
CaloLargeTitleHeader,
|
|
55
|
+
CaloSummaryHeader,
|
|
56
|
+
CaloSwapMealCard,
|
|
57
|
+
CaloSurface,
|
|
58
|
+
CaloTabMenuGroup,
|
|
59
|
+
CaloText,
|
|
60
|
+
CaloTextArea,
|
|
61
|
+
CaloToast,
|
|
62
|
+
CaloToggle,
|
|
63
|
+
CaloTopActionBar,
|
|
64
|
+
CaloVerificationCodeInput,
|
|
65
|
+
CaloVisual,
|
|
66
|
+
CaloVoucherCard,
|
|
67
|
+
caloVisualAssets
|
|
68
|
+
} from "@calo/design-system";
|
|
69
|
+
import {
|
|
70
|
+
CaloFaqAccordion,
|
|
71
|
+
CaloTestimonialCard,
|
|
72
|
+
CaloSupportPromptCard,
|
|
73
|
+
CaloUgcCard,
|
|
74
|
+
CaloFeatureCard,
|
|
75
|
+
CaloPartnerCard,
|
|
76
|
+
CaloPlanCard,
|
|
77
|
+
CaloPlanCardGrid,
|
|
78
|
+
CaloQuantityRow,
|
|
79
|
+
CaloPagination,
|
|
80
|
+
CaloMealMacroCard,
|
|
81
|
+
CaloDishCard,
|
|
82
|
+
CaloCheckoutSummaryCard
|
|
83
|
+
} from "@calo/design-system";
|
|
84
|
+
import { router } from "expo-router";
|
|
85
|
+
|
|
86
|
+
const mealImage = require("../assets/component-gallery/meal-photo.png");
|
|
87
|
+
const smoothieImage = require("../assets/component-gallery/smoothie-photo.png");
|
|
88
|
+
const ingredientPenneImage = require("../assets/live-flows/active-subscriber-home/ingredient-penne.png");
|
|
89
|
+
const marketImage = caloVisualAssets["salad-bowl-1"].source;
|
|
90
|
+
const sauceImage = caloVisualAssets["caramel"].source;
|
|
91
|
+
|
|
92
|
+
const cafeCategoryItems = [
|
|
93
|
+
{ id: "popular", label: "Popular", visual: "fire" },
|
|
94
|
+
{ id: "bone-broth", label: "Bone Broth", visual: "bone-broth" },
|
|
95
|
+
{ id: "cold-drinks", label: "Cold Drinks", visual: "cold-drinks-l" },
|
|
96
|
+
{ id: "froyo", label: "Froyo", visual: "froyo-l" },
|
|
97
|
+
{ id: "hot-drinks", label: "Hot Drinks", visual: "hot-drinks-l" },
|
|
98
|
+
{ id: "iced-tea", label: "Iced Tea", visual: "iced-tea-l" },
|
|
99
|
+
{ id: "meals", label: "Meals", visual: "meal-grid" },
|
|
100
|
+
{ id: "salads", label: "Salads", visual: "salads" },
|
|
101
|
+
{ id: "sandwiches", label: "Sandwiches", visual: "sandwich" },
|
|
102
|
+
{ id: "smoothies", label: "Smoothies", visual: "smoothie-1" },
|
|
103
|
+
{ id: "snacks", label: "Snacks", visual: "savory-snacks" },
|
|
104
|
+
{ id: "soups", label: "Soups", visual: "soups" },
|
|
105
|
+
{ id: "juices", label: "Juices", visual: "beverages" },
|
|
106
|
+
{ id: "sweet-snacks", label: "Snacks", visual: "sweet-tooth" }
|
|
107
|
+
] as const;
|
|
108
|
+
|
|
109
|
+
const macros = [
|
|
110
|
+
{ label: "P", value: "37g", tone: "protein" as const },
|
|
111
|
+
{ label: "C", value: "42g", tone: "carbs" as const },
|
|
112
|
+
{ label: "F", value: "14g", tone: "fat" as const }
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
export type GalleryCategory = "all" | "foundations" | "navigation" | "actions" | "selection" | "forms" | "commerce" | "food" | "feedback" | "marketing";
|
|
116
|
+
|
|
117
|
+
export const galleryFilters: Array<{ id: GalleryCategory; label: string }> = [
|
|
118
|
+
{ id: "all", label: "All" },
|
|
119
|
+
{ id: "foundations", label: "Foundations" },
|
|
120
|
+
{ id: "navigation", label: "Navigation" },
|
|
121
|
+
{ id: "actions", label: "Actions" },
|
|
122
|
+
{ id: "selection", label: "Selection" },
|
|
123
|
+
{ id: "forms", label: "Forms" },
|
|
124
|
+
{ id: "commerce", label: "Commerce" },
|
|
125
|
+
{ id: "food", label: "Food" },
|
|
126
|
+
{ id: "feedback", label: "Feedback" },
|
|
127
|
+
{ id: "marketing", label: "Marketing" }
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
const gallerySections: Array<{ id: string; category: Exclude<GalleryCategory, "all"> }> = [
|
|
131
|
+
{ id: "identity", category: "foundations" },
|
|
132
|
+
{ id: "typography", category: "foundations" },
|
|
133
|
+
{ id: "carousel", category: "foundations" },
|
|
134
|
+
{ id: "bottom-navigation", category: "navigation" },
|
|
135
|
+
{ id: "actions", category: "actions" },
|
|
136
|
+
{ id: "badges", category: "actions" },
|
|
137
|
+
{ id: "selection", category: "selection" },
|
|
138
|
+
{ id: "quantity", category: "selection" },
|
|
139
|
+
{ id: "inputs", category: "forms" },
|
|
140
|
+
{ id: "feedback", category: "feedback" },
|
|
141
|
+
{ id: "offers", category: "commerce" },
|
|
142
|
+
{ id: "delivery-status", category: "commerce" },
|
|
143
|
+
{ id: "payment", category: "commerce" },
|
|
144
|
+
{ id: "dietary-cards", category: "food" },
|
|
145
|
+
{ id: "category-tile-grid", category: "food" },
|
|
146
|
+
{ id: "meal-cards", category: "food" },
|
|
147
|
+
{ id: "cart-items", category: "commerce" },
|
|
148
|
+
{ id: "faq", category: "marketing" },
|
|
149
|
+
{ id: "marketing-cards", category: "marketing" },
|
|
150
|
+
{ id: "partner-cards", category: "marketing" },
|
|
151
|
+
{ id: "plan-cards", category: "food" },
|
|
152
|
+
{ id: "quantity-row", category: "selection" },
|
|
153
|
+
{ id: "pagination", category: "navigation" },
|
|
154
|
+
{ id: "meal-feature-cards", category: "food" },
|
|
155
|
+
{ id: "checkout-summary", category: "commerce" }
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
const tabMenuOptions = [
|
|
159
|
+
{ id: "options", label: "Options" },
|
|
160
|
+
{ id: "details", label: "Options" },
|
|
161
|
+
{ id: "delivery", label: "Options" },
|
|
162
|
+
{ id: "billing", label: "Options" }
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
const chipMenuOptions = [
|
|
166
|
+
{ id: "menu", label: "Menu" },
|
|
167
|
+
{ id: "plan", label: "Menu" },
|
|
168
|
+
{ id: "snacks", label: "Menu" },
|
|
169
|
+
{ id: "drinks", label: "Menu" }
|
|
170
|
+
];
|
|
171
|
+
|
|
172
|
+
export function getVisibleGallerySectionCount(selectedFilter: GalleryCategory) {
|
|
173
|
+
return gallerySections.filter((section) => selectedFilter === "all" || selectedFilter === section.category).length;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export default function ComponentsScreen() {
|
|
177
|
+
return (
|
|
178
|
+
<CaloScreen title="Component Gallery" subtitle="A living Expo preview for Calo design-system components." contentWidth="wide" gutter="wide">
|
|
179
|
+
<ComponentGalleryContent />
|
|
180
|
+
</CaloScreen>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function ComponentGalleryContent({
|
|
185
|
+
showIntro = true,
|
|
186
|
+
selectedFilter: controlledSelectedFilter,
|
|
187
|
+
onFilterChange
|
|
188
|
+
}: {
|
|
189
|
+
showIntro?: boolean;
|
|
190
|
+
selectedFilter?: GalleryCategory;
|
|
191
|
+
onFilterChange?: (filter: GalleryCategory) => void;
|
|
192
|
+
}) {
|
|
193
|
+
const [localSelectedFilter, setLocalSelectedFilter] = React.useState<GalleryCategory>("all");
|
|
194
|
+
const selectedFilter = controlledSelectedFilter ?? localSelectedFilter;
|
|
195
|
+
const setSelectedFilter = onFilterChange ?? setLocalSelectedFilter;
|
|
196
|
+
const [stepperValue, setStepperValue] = React.useState(2);
|
|
197
|
+
const [webMeals, setWebMeals] = React.useState(3);
|
|
198
|
+
const [paginationPage, setPaginationPage] = React.useState(4);
|
|
199
|
+
const [actionBarQuantity, setActionBarQuantity] = React.useState(5);
|
|
200
|
+
const [bottomNavigationTab, setBottomNavigationTab] = React.useState("meals");
|
|
201
|
+
const [compactValue, setCompactValue] = React.useState(0);
|
|
202
|
+
const [toggleSelected, setToggleSelected] = React.useState(true);
|
|
203
|
+
const [selectedDate, setSelectedDate] = React.useState("may-25");
|
|
204
|
+
const [outlineTab, setOutlineTab] = React.useState("options");
|
|
205
|
+
const [chipTab, setChipTab] = React.useState("menu");
|
|
206
|
+
const [bannerSelected, setBannerSelected] = React.useState(false);
|
|
207
|
+
const [modalSheetOpen, setModalSheetOpen] = React.useState(() => {
|
|
208
|
+
if (typeof window === "undefined") return false;
|
|
209
|
+
return new URLSearchParams(window.location.search).get("sheet") === "open";
|
|
210
|
+
});
|
|
211
|
+
const [radioSelected, setRadioSelected] = React.useState("regular");
|
|
212
|
+
const [selectionCardOption, setSelectionCardOption] = React.useState("weekly");
|
|
213
|
+
const [selectionCardChoice, setSelectionCardChoice] = React.useState("value");
|
|
214
|
+
const [checkboxSelected, setCheckboxSelected] = React.useState(true);
|
|
215
|
+
const [rating, setRating] = React.useState("Loved it");
|
|
216
|
+
const [rowRating, setRowRating] = React.useState("okay");
|
|
217
|
+
const [cardRating, setCardRating] = React.useState("awesome");
|
|
218
|
+
const [mealSelected, setMealSelected] = React.useState(true);
|
|
219
|
+
const [selectedVariant, setSelectedVariant] = React.useState("rose");
|
|
220
|
+
const [dietaryPlan, setDietaryPlan] = React.useState("balanced");
|
|
221
|
+
const [marketQuantity, setMarketQuantity] = React.useState(0);
|
|
222
|
+
const [cafeQuantity, setCafeQuantity] = React.useState(1);
|
|
223
|
+
const [paymentMethodId, setPaymentMethodId] = React.useState("wallet");
|
|
224
|
+
const [fieldValue, setFieldValue] = React.useState("Sufiyan Yusuf");
|
|
225
|
+
const [noteValue, setNoteValue] = React.useState("No onions, extra lemon.");
|
|
226
|
+
const [codeValue, setCodeValue] = React.useState("24");
|
|
227
|
+
const [phoneValue, setPhoneValue] = React.useState("501234567");
|
|
228
|
+
const [promoValue, setPromoValue] = React.useState("SAVE20");
|
|
229
|
+
const [cardNumber, setCardNumber] = React.useState("4242 4242 4242 4242");
|
|
230
|
+
const [expiry, setExpiry] = React.useState("04/28");
|
|
231
|
+
const [cvv, setCvv] = React.useState("123");
|
|
232
|
+
const [healthySansAvailable, setHealthySansAvailable] = React.useState(false);
|
|
233
|
+
const previewOnly = () => undefined;
|
|
234
|
+
const filterVisible = (category: GalleryCategory) => selectedFilter === "all" || selectedFilter === category;
|
|
235
|
+
const visibleSectionCount = getVisibleGallerySectionCount(selectedFilter);
|
|
236
|
+
|
|
237
|
+
React.useEffect(() => {
|
|
238
|
+
if (typeof window === "undefined") return;
|
|
239
|
+
if (new URLSearchParams(window.location.search).get("sheet") === "open") {
|
|
240
|
+
setModalSheetOpen(true);
|
|
241
|
+
}
|
|
242
|
+
}, []);
|
|
243
|
+
|
|
244
|
+
React.useEffect(() => {
|
|
245
|
+
setHealthySansAvailable(isHealthySansAvailable());
|
|
246
|
+
}, []);
|
|
247
|
+
|
|
248
|
+
return (
|
|
249
|
+
<CaloStack gap="size40">
|
|
250
|
+
{showIntro ? (
|
|
251
|
+
<>
|
|
252
|
+
<CaloSurface padding="size16" elevation="raised">
|
|
253
|
+
<CaloStack gap="size12">
|
|
254
|
+
<CaloInline align="center" justify="between" wrap>
|
|
255
|
+
<CaloInline align="center" gap="size10">
|
|
256
|
+
<CaloLogo variant="mark" size="sm" />
|
|
257
|
+
<CaloBadge label="Expo gallery" variant="brand" />
|
|
258
|
+
</CaloInline>
|
|
259
|
+
<CaloButton label="Back home" variant="tertiary" size="small" onPress={() => router.push("/")} />
|
|
260
|
+
</CaloInline>
|
|
261
|
+
<CaloText role="bodyMuted">
|
|
262
|
+
This is not Storybook yet. It is a runnable component showroom inside the same prototype app agents use to build Calo UI.
|
|
263
|
+
</CaloText>
|
|
264
|
+
</CaloStack>
|
|
265
|
+
</CaloSurface>
|
|
266
|
+
|
|
267
|
+
<ComponentGalleryFilters selectedFilter={selectedFilter} visibleSectionCount={visibleSectionCount} onFilterChange={setSelectedFilter} />
|
|
268
|
+
</>
|
|
269
|
+
) : null}
|
|
270
|
+
|
|
271
|
+
{filterVisible("foundations") ? (
|
|
272
|
+
<GallerySection title="Identity" label="Foundations">
|
|
273
|
+
<CaloInline align="center" gap="size16" wrap>
|
|
274
|
+
<CaloLogo variant="wordmark" size="md" />
|
|
275
|
+
<CaloLogo variant="mark" size="md" />
|
|
276
|
+
<CaloVisual name="green-gift" size="lg" />
|
|
277
|
+
<CaloVisual name="calo-truck" size="lg" />
|
|
278
|
+
<CaloVisual name="club-wallet" size="lg" />
|
|
279
|
+
</CaloInline>
|
|
280
|
+
</GallerySection>
|
|
281
|
+
) : null}
|
|
282
|
+
|
|
283
|
+
{filterVisible("foundations") ? (
|
|
284
|
+
<GallerySection title="Typography" label="Foundations">
|
|
285
|
+
<VariantGroup title="Screen hierarchy">
|
|
286
|
+
<CaloStack gap="size10">
|
|
287
|
+
<TypographySpecimen roleName="display" role="display" sample="Aa" usage="High-impact moments only" />
|
|
288
|
+
<CaloDivider />
|
|
289
|
+
<TypographySpecimen roleName="title" role="title" sample="Tomorrow's lunch" usage="Major card titles" />
|
|
290
|
+
<CaloDivider />
|
|
291
|
+
<TypographySpecimen roleName="section" role="section" sample="Choose your meals" usage="Product modules" />
|
|
292
|
+
<CaloDivider />
|
|
293
|
+
<TypographySpecimen roleName="body" role="body" sample="Normal content copy" usage="Primary reading text" />
|
|
294
|
+
<CaloDivider />
|
|
295
|
+
<TypographySpecimen roleName="bodyMuted" role="bodyMuted" sample="Supporting copy" usage="Secondary explanations" />
|
|
296
|
+
<CaloDivider />
|
|
297
|
+
<TypographySpecimen roleName="caption" role="caption" sample="Compact label" usage="Component-owned labels" />
|
|
298
|
+
</CaloStack>
|
|
299
|
+
</VariantGroup>
|
|
300
|
+
<VariantGroup title="Desktop utility roles">
|
|
301
|
+
<CaloStack gap="size10">
|
|
302
|
+
<TypographySpecimen roleName="heading4" role="heading4" sample="Preview" usage="Pane headings and compact chrome" />
|
|
303
|
+
<CaloDivider />
|
|
304
|
+
<TypographySpecimen roleName="paragraphMedium" role="paragraphMedium" sample="Starter Prototype" usage="Sidebar rows and desktop copy" />
|
|
305
|
+
<CaloDivider />
|
|
306
|
+
<TypographySpecimen roleName="paragraphMediumStrong" role="paragraphMediumStrong" sample="Selected value" usage="Important compact values" />
|
|
307
|
+
<CaloDivider />
|
|
308
|
+
<TypographySpecimen roleName="captionSmall" role="captionSmall" sample="queued" usage="Metadata and quiet labels" />
|
|
309
|
+
</CaloStack>
|
|
310
|
+
</VariantGroup>
|
|
311
|
+
<VariantGroup title="Arabic roles">
|
|
312
|
+
{healthySansAvailable ? (
|
|
313
|
+
<CaloStack gap="size10">
|
|
314
|
+
<TypographySpecimen roleName="title · ar" role="title" language="ar" sample="وجبة الغداء" usage="Arabic major titles" />
|
|
315
|
+
<CaloDivider />
|
|
316
|
+
<TypographySpecimen roleName="body · ar" role="body" language="ar" sample="نص عربي لتفاصيل قصيرة." usage="Arabic content copy" />
|
|
317
|
+
<CaloDivider />
|
|
318
|
+
<TypographySpecimen roleName="captionSmall · ar" role="captionSmall" language="ar" sample="حالة مختصرة" usage="Arabic metadata" />
|
|
319
|
+
</CaloStack>
|
|
320
|
+
) : (
|
|
321
|
+
<CaloText role="paragraphMedium" tone="danger">
|
|
322
|
+
Healthy Sans is not loaded in this runtime, so Arabic specimens are hidden until the font assets are available.
|
|
323
|
+
</CaloText>
|
|
324
|
+
)}
|
|
325
|
+
</VariantGroup>
|
|
326
|
+
</GallerySection>
|
|
327
|
+
) : null}
|
|
328
|
+
|
|
329
|
+
{filterVisible("foundations") ? (
|
|
330
|
+
<GallerySection title="Carousel" label="Containers">
|
|
331
|
+
<VariantGroup title="Generic item container">
|
|
332
|
+
<CaloCarousel pageControl>
|
|
333
|
+
<CaloSurface padding="size20" elevation="raised" radius="radius6">
|
|
334
|
+
<CaloStack gap="size12">
|
|
335
|
+
<CaloInline align="center" justify="between">
|
|
336
|
+
<CaloBadge label="Plan" variant="brand" />
|
|
337
|
+
<CaloIcon common="calendar" size="md" tone="brand" />
|
|
338
|
+
</CaloInline>
|
|
339
|
+
<CaloStack gap="size8">
|
|
340
|
+
<CaloText role="section">Your next delivery</CaloText>
|
|
341
|
+
<CaloText role="bodyMuted">Breakfast and lunch arrive tomorrow between 8:00 - 9:00 AM.</CaloText>
|
|
342
|
+
</CaloStack>
|
|
343
|
+
</CaloStack>
|
|
344
|
+
</CaloSurface>
|
|
345
|
+
<CaloSurface padding="size20" elevation="raised" radius="radius6">
|
|
346
|
+
<CaloStack gap="size12">
|
|
347
|
+
<CaloInline align="center" justify="between">
|
|
348
|
+
<CaloBadge label="Cafe" variant="neutral" />
|
|
349
|
+
<CaloVisual name="smoothie-1" size="md" />
|
|
350
|
+
</CaloInline>
|
|
351
|
+
<CaloStack gap="size8">
|
|
352
|
+
<CaloText role="section">Cafe pickup</CaloText>
|
|
353
|
+
<CaloText role="bodyMuted">Keep campaign, product, or status cards in a stable horizontal rail.</CaloText>
|
|
354
|
+
</CaloStack>
|
|
355
|
+
</CaloStack>
|
|
356
|
+
</CaloSurface>
|
|
357
|
+
<CaloSurface padding="size20" elevation="raised" radius="radius6">
|
|
358
|
+
<CaloStack gap="size12">
|
|
359
|
+
<CaloInline align="center" justify="between">
|
|
360
|
+
<CaloBadge label="Rewards" variant="brand" />
|
|
361
|
+
<CaloIcon common="gift" size="md" tone="brand" />
|
|
362
|
+
</CaloInline>
|
|
363
|
+
<CaloStack gap="size8">
|
|
364
|
+
<CaloText role="section">Club balance</CaloText>
|
|
365
|
+
<CaloText role="bodyMuted">Use the carousel as the scroll owner; items keep their own content grammar.</CaloText>
|
|
366
|
+
</CaloStack>
|
|
367
|
+
</CaloStack>
|
|
368
|
+
</CaloSurface>
|
|
369
|
+
</CaloCarousel>
|
|
370
|
+
</VariantGroup>
|
|
371
|
+
<VariantGroup title="Promotion banner rail">
|
|
372
|
+
<CaloCarousel>
|
|
373
|
+
<CaloBanner
|
|
374
|
+
variant="promotion"
|
|
375
|
+
title="Summer smoothies are back"
|
|
376
|
+
description="Fresh blends with your next Cafe order."
|
|
377
|
+
image={smoothieImage}
|
|
378
|
+
tagLabel="Cafe offer"
|
|
379
|
+
onPress={previewOnly}
|
|
380
|
+
/>
|
|
381
|
+
<CaloBanner
|
|
382
|
+
variant="promotion"
|
|
383
|
+
title="Fuel your training week"
|
|
384
|
+
description="High-protein meals matched to your goals."
|
|
385
|
+
image={mealImage}
|
|
386
|
+
tagLabel="Featured"
|
|
387
|
+
onPress={previewOnly}
|
|
388
|
+
/>
|
|
389
|
+
</CaloCarousel>
|
|
390
|
+
</VariantGroup>
|
|
391
|
+
<VariantGroup title="Compact product rail">
|
|
392
|
+
<CaloCarousel itemSize="auto" pageControl={false}>
|
|
393
|
+
<CaloCafeProductCard
|
|
394
|
+
image={smoothieImage}
|
|
395
|
+
name="Iced Americano"
|
|
396
|
+
calories="0 cal"
|
|
397
|
+
price="2.8 BHD"
|
|
398
|
+
action="add"
|
|
399
|
+
scrolled
|
|
400
|
+
onPress={previewOnly}
|
|
401
|
+
onAddPress={previewOnly}
|
|
402
|
+
/>
|
|
403
|
+
<CaloCafeProductCard
|
|
404
|
+
image={smoothieImage}
|
|
405
|
+
name="Iced Latte"
|
|
406
|
+
calories="125 cal"
|
|
407
|
+
price="2.8 BHD"
|
|
408
|
+
action="add"
|
|
409
|
+
scrolled
|
|
410
|
+
onPress={previewOnly}
|
|
411
|
+
onAddPress={previewOnly}
|
|
412
|
+
/>
|
|
413
|
+
<CaloCafeProductCard
|
|
414
|
+
image={smoothieImage}
|
|
415
|
+
name="Iced Mocha Latte"
|
|
416
|
+
calories="210 cal"
|
|
417
|
+
price="2.2 BHD"
|
|
418
|
+
oldPrice="2.8 BHD"
|
|
419
|
+
action="add"
|
|
420
|
+
scrolled
|
|
421
|
+
onPress={previewOnly}
|
|
422
|
+
onAddPress={previewOnly}
|
|
423
|
+
/>
|
|
424
|
+
<CaloCafeProductCard
|
|
425
|
+
image={smoothieImage}
|
|
426
|
+
name="Rose Latte"
|
|
427
|
+
calories="229 cal"
|
|
428
|
+
price="2.8 BHD"
|
|
429
|
+
discountLabel="Spring Menu"
|
|
430
|
+
action="add"
|
|
431
|
+
scrolled
|
|
432
|
+
onPress={previewOnly}
|
|
433
|
+
onAddPress={previewOnly}
|
|
434
|
+
/>
|
|
435
|
+
</CaloCarousel>
|
|
436
|
+
</VariantGroup>
|
|
437
|
+
</GallerySection>
|
|
438
|
+
) : null}
|
|
439
|
+
|
|
440
|
+
{filterVisible("navigation") ? (
|
|
441
|
+
<GallerySection title="Navigation" label="Bars">
|
|
442
|
+
<VariantGroup title="Top action bars">
|
|
443
|
+
<CaloStack gap="size12">
|
|
444
|
+
<CaloTopActionBar
|
|
445
|
+
title="Meal details"
|
|
446
|
+
leadingActions={[{ label: "Back", icon: <CaloIcon common="back" />, onPress: previewOnly }]}
|
|
447
|
+
trailingActions={[{ label: "Help", icon: <CaloIcon common="help" />, onPress: previewOnly }]}
|
|
448
|
+
divider
|
|
449
|
+
/>
|
|
450
|
+
<CaloTopActionBar
|
|
451
|
+
title="Swap meal"
|
|
452
|
+
trailingActions={[{ label: "Done", presentation: "text", onPress: previewOnly }]}
|
|
453
|
+
divider
|
|
454
|
+
/>
|
|
455
|
+
<CaloTopActionBar
|
|
456
|
+
progress={{ value: 2, max: 5, accessibilityLabel: "Step 2 of 5" }}
|
|
457
|
+
leadingActions={[{ label: "Back", icon: <CaloIcon common="back" />, onPress: previewOnly }]}
|
|
458
|
+
trailingActions={[{ label: "Help", icon: <CaloIcon common="help" />, onPress: previewOnly }]}
|
|
459
|
+
/>
|
|
460
|
+
<CaloTopActionBar
|
|
461
|
+
leadingActions={[{ label: "Back", icon: <CaloIcon common="back" />, onPress: previewOnly }]}
|
|
462
|
+
trailingActions={[
|
|
463
|
+
{ label: "Help", icon: <CaloIcon common="help" />, onPress: previewOnly },
|
|
464
|
+
{ label: "Close", icon: <CaloIcon common="close" />, onPress: previewOnly }
|
|
465
|
+
]}
|
|
466
|
+
/>
|
|
467
|
+
<CaloTopActionBar
|
|
468
|
+
title="تفاصيل الوجبة"
|
|
469
|
+
language="ar"
|
|
470
|
+
leadingActions={[{ label: "Back", icon: <CaloIcon common="back" />, onPress: previewOnly }]}
|
|
471
|
+
trailingActions={[{ label: "Help", icon: <CaloIcon common="help" />, onPress: previewOnly }]}
|
|
472
|
+
divider
|
|
473
|
+
/>
|
|
474
|
+
</CaloStack>
|
|
475
|
+
</VariantGroup>
|
|
476
|
+
<VariantGroup title="Top appbar recipes">
|
|
477
|
+
<CaloStack gap="size12">
|
|
478
|
+
<CaloContextTopBar
|
|
479
|
+
leadingAction={{ label: "Cancel", onPress: previewOnly }}
|
|
480
|
+
context={{ label: "Regular", value: "SAR 2.7" }}
|
|
481
|
+
trailingAction={{ label: "Done", onPress: previewOnly }}
|
|
482
|
+
/>
|
|
483
|
+
<CaloCenteredTitleTopBar
|
|
484
|
+
title="Swap meal"
|
|
485
|
+
action={{ label: "Done", onPress: previewOnly }}
|
|
486
|
+
/>
|
|
487
|
+
<CaloInlineHeader
|
|
488
|
+
eyebrow="Pickup from"
|
|
489
|
+
title="Seef Mall"
|
|
490
|
+
titleAction={{ label: "Change pickup", onPress: previewOnly }}
|
|
491
|
+
pillAction={{ label: "Rewards", icon: <CaloIcon common="gift" size="sm" tone="brand" weight="fill" />, onPress: previewOnly }}
|
|
492
|
+
iconAction={{ label: "Search", icon: <CaloIcon common="search" />, onPress: previewOnly }}
|
|
493
|
+
/>
|
|
494
|
+
<CaloLargeTitleHeader
|
|
495
|
+
title="Market"
|
|
496
|
+
pillAction={{ label: "Rewards", icon: <CaloIcon common="gift" size="sm" tone="brand" weight="fill" />, onPress: previewOnly }}
|
|
497
|
+
iconAction={{ label: "AI search", icon: <CaloIcon common="sparkleSearch" />, onPress: previewOnly }}
|
|
498
|
+
/>
|
|
499
|
+
<CaloSummaryHeader
|
|
500
|
+
eyebrow="Cafe rewards"
|
|
501
|
+
value="1234 points"
|
|
502
|
+
valueIcon={<CaloIcon common="gift" size="sm" tone="default" weight="fill" />}
|
|
503
|
+
leadingAction={{ label: "Back", icon: <CaloIcon common="back" />, onPress: previewOnly }}
|
|
504
|
+
/>
|
|
505
|
+
<CaloLargeTitleHeader
|
|
506
|
+
title="Shopping bag"
|
|
507
|
+
iconAction={{ label: "Close", icon: <CaloIcon common="close" />, onPress: previewOnly }}
|
|
508
|
+
/>
|
|
509
|
+
<CaloContextTopBar
|
|
510
|
+
language="ar"
|
|
511
|
+
leadingAction={{ label: "إلغاء", onPress: previewOnly }}
|
|
512
|
+
context={{ label: "عادي", value: "SAR 2.7" }}
|
|
513
|
+
trailingAction={{ label: "تم", onPress: previewOnly }}
|
|
514
|
+
/>
|
|
515
|
+
<CaloLargeTitleHeader
|
|
516
|
+
language="ar"
|
|
517
|
+
title="الماركت"
|
|
518
|
+
pillAction={{ label: "مكآفات", icon: <CaloIcon common="gift" size="sm" tone="brand" weight="fill" />, iconPosition: "trailing", onPress: previewOnly }}
|
|
519
|
+
iconAction={{ label: "بحث ذكي", icon: <CaloIcon common="sparkleSearch" />, onPress: previewOnly }}
|
|
520
|
+
/>
|
|
521
|
+
</CaloStack>
|
|
522
|
+
</VariantGroup>
|
|
523
|
+
<VariantGroup title="Main hub bottom navigation">
|
|
524
|
+
<CaloBottomNavigation selectedId={bottomNavigationTab} onSelect={setBottomNavigationTab} />
|
|
525
|
+
</VariantGroup>
|
|
526
|
+
<VariantGroup title="Context screen">
|
|
527
|
+
<CaloContextScreen
|
|
528
|
+
title="Meal plan paused"
|
|
529
|
+
description="Your meal deliveries are paused from Tuesday, 29 June. Resume now to continue receiving your favorite meals!"
|
|
530
|
+
visual={<CaloVisual name="pause" size="lg" />}
|
|
531
|
+
footer={<CaloButton label="Resume deliveries" size="large" fullWidth onPress={previewOnly} />}
|
|
532
|
+
topBar={
|
|
533
|
+
<CaloLargeTitleHeader
|
|
534
|
+
title="Paused"
|
|
535
|
+
pillAction={{ label: "Refer & earn", icon: <CaloIcon common="gift" size="sm" tone="brand" weight="fill" />, iconPosition: "leading", onPress: previewOnly }}
|
|
536
|
+
/>
|
|
537
|
+
}
|
|
538
|
+
bottomBar={<CaloBottomNavigation selectedId="meals" onSelect={() => previewOnly()} />}
|
|
539
|
+
/>
|
|
540
|
+
</VariantGroup>
|
|
541
|
+
<VariantGroup title="Date bars">
|
|
542
|
+
<CaloStack gap="size12">
|
|
543
|
+
<CaloDateBar
|
|
544
|
+
selectedId={selectedDate}
|
|
545
|
+
onSelect={setSelectedDate}
|
|
546
|
+
items={[
|
|
547
|
+
{ id: "may-25", label: "25", dayLabel: "F" },
|
|
548
|
+
{ id: "may-26", label: "26", dayLabel: "S" },
|
|
549
|
+
{ id: "may-27", label: "27", dayLabel: "S" },
|
|
550
|
+
{ id: "may-28", label: "28", dayLabel: "M", state: "today" },
|
|
551
|
+
{ id: "may-29", label: "29", dayLabel: "T" },
|
|
552
|
+
{ id: "may-30", label: "30", dayLabel: "W" },
|
|
553
|
+
{ id: "may-31", label: "31", dayLabel: "T" }
|
|
554
|
+
]}
|
|
555
|
+
/>
|
|
556
|
+
<CaloDateBar
|
|
557
|
+
sticky
|
|
558
|
+
selectedId="fri"
|
|
559
|
+
items={[
|
|
560
|
+
{ id: "fri", label: "25", dayLabel: "F" },
|
|
561
|
+
{ id: "sat", label: "26", dayLabel: "S" },
|
|
562
|
+
{ id: "sun", label: "27", dayLabel: "S" },
|
|
563
|
+
{ id: "mon", label: "28", dayLabel: "M", state: "today" },
|
|
564
|
+
{ id: "tue", label: "29", dayLabel: "T" },
|
|
565
|
+
{ id: "wed", label: "30", dayLabel: "W" },
|
|
566
|
+
{ id: "thu", label: "31", dayLabel: "T" }
|
|
567
|
+
]}
|
|
568
|
+
/>
|
|
569
|
+
<CaloInline align="center" gap="size12" wrap>
|
|
570
|
+
<CaloDateBarItem id="skip" label="24" dayLabel="S" status="skip" />
|
|
571
|
+
<CaloDateBarItem id="pause-one" label="24" dayLabel="S" status="pause" />
|
|
572
|
+
<CaloDateBarItem id="pause-range" label="24" endLabel="29" status="pause" state="selected" />
|
|
573
|
+
<CaloDateBarItem id="expired" label="24" dayLabel="S" status="expired" />
|
|
574
|
+
<CaloDateBarItem id="exclude" label="20" endLabel="24" status="exclude" state="selected" />
|
|
575
|
+
<CaloDateBarItem id="arabic" label="24" dayLabel="س" status="skip" language="ar" state="selected" />
|
|
576
|
+
</CaloInline>
|
|
577
|
+
</CaloStack>
|
|
578
|
+
</VariantGroup>
|
|
579
|
+
</GallerySection>
|
|
580
|
+
) : null}
|
|
581
|
+
|
|
582
|
+
{filterVisible("actions") ? (
|
|
583
|
+
<GallerySection title="Actions" label="Buttons">
|
|
584
|
+
<VariantGroup title="Button variants">
|
|
585
|
+
<CaloInline align="center" gap="size10" wrap>
|
|
586
|
+
<CaloButton label="Continue" onPress={previewOnly} />
|
|
587
|
+
<CaloButton label="Preview" variant="secondary" onPress={previewOnly} />
|
|
588
|
+
<CaloButton label="Tools" variant="utility" onPress={previewOnly} />
|
|
589
|
+
<CaloButton label="Remove" variant="danger" onPress={previewOnly} />
|
|
590
|
+
<CaloButton label="Learn more" variant="link" onPress={previewOnly} />
|
|
591
|
+
</CaloInline>
|
|
592
|
+
</VariantGroup>
|
|
593
|
+
<VariantGroup title="Button sizes">
|
|
594
|
+
<CaloStack gap="size10">
|
|
595
|
+
<ButtonSizeRow label="Large">
|
|
596
|
+
<CaloButton label="Continue" size="large" onPress={previewOnly} />
|
|
597
|
+
<CaloButton label="Preview" variant="secondary" size="large" onPress={previewOnly} />
|
|
598
|
+
</ButtonSizeRow>
|
|
599
|
+
<CaloDivider />
|
|
600
|
+
<ButtonSizeRow label="Medium">
|
|
601
|
+
<CaloButton label="Continue" size="medium" onPress={previewOnly} />
|
|
602
|
+
<CaloButton label="Preview" variant="secondary" size="medium" onPress={previewOnly} />
|
|
603
|
+
</ButtonSizeRow>
|
|
604
|
+
<CaloDivider />
|
|
605
|
+
<ButtonSizeRow label="Small">
|
|
606
|
+
<CaloButton label="Continue" size="small" onPress={previewOnly} />
|
|
607
|
+
<CaloButton label="Preview" variant="secondary" size="small" onPress={previewOnly} />
|
|
608
|
+
</ButtonSizeRow>
|
|
609
|
+
</CaloStack>
|
|
610
|
+
</VariantGroup>
|
|
611
|
+
<VariantGroup title="Icon button variants">
|
|
612
|
+
<CaloInline align="center" gap="size10" wrap>
|
|
613
|
+
<CaloIconButton label="Confirm" icon={<CaloIcon common="check" />} variant="primary" onPress={previewOnly} />
|
|
614
|
+
<CaloIconButton label="Search" icon={<CaloIcon common="search" />} variant="secondary" onPress={previewOnly} />
|
|
615
|
+
<CaloIconButton label="Settings" icon={<CaloIcon common="settings" />} variant="neutral" onPress={previewOnly} />
|
|
616
|
+
<CaloIconButton label="Filter" icon={<CaloIcon common="filter" />} variant="outline" onPress={previewOnly} />
|
|
617
|
+
<CaloIconButton label="Delete" icon={<CaloIcon common="remove" />} variant="danger" onPress={previewOnly} />
|
|
618
|
+
</CaloInline>
|
|
619
|
+
</VariantGroup>
|
|
620
|
+
<VariantGroup title="Icon button sizes">
|
|
621
|
+
<CaloStack gap="size10">
|
|
622
|
+
<ButtonSizeRow label="Large">
|
|
623
|
+
<CaloIconButton label="Large confirm" icon={<CaloIcon common="check" />} variant="primary" size="large" onPress={previewOnly} />
|
|
624
|
+
<CaloIconButton label="Large filter" icon={<CaloIcon common="filter" />} variant="outline" size="large" onPress={previewOnly} />
|
|
625
|
+
</ButtonSizeRow>
|
|
626
|
+
<CaloDivider />
|
|
627
|
+
<ButtonSizeRow label="Medium">
|
|
628
|
+
<CaloIconButton label="Medium confirm" icon={<CaloIcon common="check" />} variant="primary" size="medium" onPress={previewOnly} />
|
|
629
|
+
<CaloIconButton label="Medium filter" icon={<CaloIcon common="filter" />} variant="outline" size="medium" onPress={previewOnly} />
|
|
630
|
+
</ButtonSizeRow>
|
|
631
|
+
<CaloDivider />
|
|
632
|
+
<ButtonSizeRow label="Small">
|
|
633
|
+
<CaloIconButton label="Small confirm" icon={<CaloIcon common="check" />} variant="primary" size="small" onPress={previewOnly} />
|
|
634
|
+
<CaloIconButton label="Small filter" icon={<CaloIcon common="filter" />} variant="outline" size="small" onPress={previewOnly} />
|
|
635
|
+
</ButtonSizeRow>
|
|
636
|
+
</CaloStack>
|
|
637
|
+
</VariantGroup>
|
|
638
|
+
<VariantGroup title="Bottom action bars">
|
|
639
|
+
<CaloStack gap="size12">
|
|
640
|
+
<CaloBottomActionBar primaryAction={{ label: "Continue", onPress: previewOnly }} divider />
|
|
641
|
+
<CaloBottomActionBar
|
|
642
|
+
primaryAction={{ label: "Apply", onPress: previewOnly }}
|
|
643
|
+
secondaryAction={{ label: "Clear", onPress: previewOnly }}
|
|
644
|
+
layout="horizontal"
|
|
645
|
+
divider
|
|
646
|
+
/>
|
|
647
|
+
<CaloBottomActionBar
|
|
648
|
+
primaryAction={{ label: "Continue", disabled: true, onPress: previewOnly }}
|
|
649
|
+
secondaryAction={{ label: "I'm okay with everything", onPress: previewOnly }}
|
|
650
|
+
actionOrder="secondaryFirst"
|
|
651
|
+
layout="verticalTertiary"
|
|
652
|
+
divider
|
|
653
|
+
/>
|
|
654
|
+
<CaloBottomActionBar
|
|
655
|
+
primaryAction={{ label: "Add", priceLabel: "2.8 BHD", onPress: previewOnly }}
|
|
656
|
+
summary={{ amount: { value: "2,200", unit: "AED", suffix: "per month" }, description: "VAT inclusive" }}
|
|
657
|
+
divider
|
|
658
|
+
/>
|
|
659
|
+
<CaloBottomActionBar
|
|
660
|
+
primaryAction={{ label: "Pay with", paymentMark: "applePay", onPress: previewOnly }}
|
|
661
|
+
summary={{ amount: { value: "2,420", currency: "SAR" }, description: "VAT inclusive" }}
|
|
662
|
+
divider
|
|
663
|
+
/>
|
|
664
|
+
<CaloBottomActionBar
|
|
665
|
+
primaryAction={{ label: "Add", priceLabel: "2.8 BHD", onPress: previewOnly }}
|
|
666
|
+
stepper={{
|
|
667
|
+
value: actionBarQuantity,
|
|
668
|
+
onIncrement: () => setActionBarQuantity((value) => value + 1),
|
|
669
|
+
onDecrement: () => setActionBarQuantity((value) => Math.max(0, value - 1))
|
|
670
|
+
}}
|
|
671
|
+
divider
|
|
672
|
+
/>
|
|
673
|
+
</CaloStack>
|
|
674
|
+
</VariantGroup>
|
|
675
|
+
<VariantGroup title="Bottom sheets">
|
|
676
|
+
<CaloStack gap="size20">
|
|
677
|
+
<CaloButton label="Open real draggable sheet" variant="secondary" onPress={() => setModalSheetOpen(true)} />
|
|
678
|
+
<CaloBottomSheet
|
|
679
|
+
open={modalSheetOpen}
|
|
680
|
+
onOpenChange={setModalSheetOpen}
|
|
681
|
+
presentation="modal"
|
|
682
|
+
type="information"
|
|
683
|
+
title="Real bottom sheet"
|
|
684
|
+
description="This sheet slides in, drags down, and dismisses through Gorhom."
|
|
685
|
+
visual={<CaloVisual name="green-gift" size="xl" />}
|
|
686
|
+
primaryAction={{ label: "Close", onPress: () => setModalSheetOpen(false) }}
|
|
687
|
+
/>
|
|
688
|
+
<CaloBottomSheet
|
|
689
|
+
width="figma"
|
|
690
|
+
title="Maximum 1 line title"
|
|
691
|
+
description="Use this space to put a maximum of two lines of your description text."
|
|
692
|
+
primaryAction={{ label: "Label", onPress: previewOnly }}
|
|
693
|
+
>
|
|
694
|
+
<CaloSurface padding="size20" radius="radius3">
|
|
695
|
+
<CaloText role="paragraphMedium" tone="muted">Replace this content</CaloText>
|
|
696
|
+
</CaloSurface>
|
|
697
|
+
</CaloBottomSheet>
|
|
698
|
+
<CaloBottomSheet
|
|
699
|
+
width="figma"
|
|
700
|
+
type="information"
|
|
701
|
+
title="Title goes here"
|
|
702
|
+
description="Use this line for the description text."
|
|
703
|
+
visual={<CaloVisual name="green-gift" size="xl" />}
|
|
704
|
+
primaryAction={{ label: "Label", onPress: previewOnly }}
|
|
705
|
+
/>
|
|
706
|
+
<CaloBottomSheet
|
|
707
|
+
width="figma"
|
|
708
|
+
title="العنوان يذهب هنا"
|
|
709
|
+
description="استخدم هذه المساحة لوصف مختصر."
|
|
710
|
+
language="ar"
|
|
711
|
+
primaryAction={{ label: "استمرار", onPress: previewOnly }}
|
|
712
|
+
>
|
|
713
|
+
<CaloStack gap="size12">
|
|
714
|
+
<CaloText role="paragraphMedium" language="ar">استبدل هذا المحتوى.</CaloText>
|
|
715
|
+
<CaloText role="paragraphMedium" language="ar" tone="muted">يحافظ المكون على التمرير والكروم القياسي دون حالة محلية.</CaloText>
|
|
716
|
+
</CaloStack>
|
|
717
|
+
</CaloBottomSheet>
|
|
718
|
+
</CaloStack>
|
|
719
|
+
</VariantGroup>
|
|
720
|
+
</GallerySection>
|
|
721
|
+
) : null}
|
|
722
|
+
|
|
723
|
+
{filterVisible("actions") ? (
|
|
724
|
+
<GallerySection title="Compact States" label="Badges">
|
|
725
|
+
<VariantGroup title="Large badges">
|
|
726
|
+
<CaloInline align="center" gap="size8" wrap>
|
|
727
|
+
<CaloBadge label="Applied" variant="brand" size="big" leadingIcon trailingIcon />
|
|
728
|
+
<CaloBadge label="Draft" variant="neutral" size="big" leadingIcon />
|
|
729
|
+
<CaloBadge label="Remove" variant="danger" size="big" trailingIcon />
|
|
730
|
+
<CaloBadge label="Meal plans" variant="outline" size="big" />
|
|
731
|
+
<CaloBadge label="VIP" variant="custom" size="big" />
|
|
732
|
+
</CaloInline>
|
|
733
|
+
</VariantGroup>
|
|
734
|
+
<VariantGroup title="Small badges">
|
|
735
|
+
<CaloInline align="center" gap="size8" wrap>
|
|
736
|
+
<CaloBadge label="Fresh" variant="brand" />
|
|
737
|
+
<CaloBadge label="Info" variant="neutral" />
|
|
738
|
+
<CaloBadge label="Risk" variant="danger" />
|
|
739
|
+
<CaloBadge label="Filter" variant="outline" />
|
|
740
|
+
<CaloBadge label="Special" variant="custom" />
|
|
741
|
+
</CaloInline>
|
|
742
|
+
</VariantGroup>
|
|
743
|
+
</GallerySection>
|
|
744
|
+
) : null}
|
|
745
|
+
|
|
746
|
+
{filterVisible("selection") ? (
|
|
747
|
+
<GallerySection title="Selection" label="Controls">
|
|
748
|
+
<VariantGroup title="Tab menu groups">
|
|
749
|
+
<CaloStack gap="size12">
|
|
750
|
+
<CaloTabMenuGroup items={tabMenuOptions.slice(0, 2)} selectedId={outlineTab} onSelect={setOutlineTab} />
|
|
751
|
+
<CaloTabMenuGroup items={tabMenuOptions} selectedId={outlineTab} onSelect={setOutlineTab} width="hug" />
|
|
752
|
+
<CaloTabMenuGroup items={chipMenuOptions.slice(0, 2)} selectedId={chipTab} onSelect={setChipTab} variant="chips" />
|
|
753
|
+
<CaloTabMenuGroup items={chipMenuOptions} selectedId={chipTab} onSelect={setChipTab} variant="chips" width="hug" />
|
|
754
|
+
</CaloStack>
|
|
755
|
+
</VariantGroup>
|
|
756
|
+
<CaloInline align="center" gap="size16" wrap>
|
|
757
|
+
<SelectionPreview label="Regular meal">
|
|
758
|
+
<CaloRadio selected={radioSelected === "regular"} label="Regular meal" onSelectedChange={() => setRadioSelected("regular")} />
|
|
759
|
+
</SelectionPreview>
|
|
760
|
+
<SelectionPreview label="Low carb">
|
|
761
|
+
<CaloRadio selected={radioSelected === "low-carb"} label="Low carb" onSelectedChange={() => setRadioSelected("low-carb")} />
|
|
762
|
+
</SelectionPreview>
|
|
763
|
+
<SelectionPreview label="Add cutlery">
|
|
764
|
+
<CaloCheckbox selected={checkboxSelected} label="Add cutlery" onSelectedChange={setCheckboxSelected} />
|
|
765
|
+
</SelectionPreview>
|
|
766
|
+
<SelectionPreview label="Disabled">
|
|
767
|
+
<CaloCheckbox selected disabled />
|
|
768
|
+
</SelectionPreview>
|
|
769
|
+
</CaloInline>
|
|
770
|
+
<CaloInline align="center" gap="size16" wrap>
|
|
771
|
+
<CaloToggle label="Enable meals" selected={toggleSelected} onSelectedChange={setToggleSelected} />
|
|
772
|
+
<CaloToggle label="Loading selected" selected loading onSelectedChange={previewOnly} />
|
|
773
|
+
</CaloInline>
|
|
774
|
+
<VariantGroup title="Selection card radio group">
|
|
775
|
+
<CaloInline align="start" gap="size16" wrap>
|
|
776
|
+
<CaloSelectionCardRadioGroup
|
|
777
|
+
selectedId={selectionCardOption}
|
|
778
|
+
onSelect={setSelectionCardOption}
|
|
779
|
+
options={[
|
|
780
|
+
{ id: "daily", title: "Daily delivery", description: "Meals arrive every day at your chosen time." },
|
|
781
|
+
{ id: "weekly", title: "Weekly delivery", description: "Get a weekly box with meals ready to heat." },
|
|
782
|
+
{ id: "paused", title: "Pause deliveries", description: "Temporarily stop meals while you travel.", disabled: true }
|
|
783
|
+
]}
|
|
784
|
+
/>
|
|
785
|
+
<CaloSelectionCardRadioGroup
|
|
786
|
+
language="ar"
|
|
787
|
+
selectedId={selectionCardOption}
|
|
788
|
+
onSelect={setSelectionCardOption}
|
|
789
|
+
options={[
|
|
790
|
+
{ id: "daily", title: "توصيل يومي", description: "تصل وجباتك كل يوم في الوقت الذي تختاره." },
|
|
791
|
+
{ id: "weekly", title: "توصيل أسبوعي", description: "استلم صندوقا أسبوعيا بوجبات جاهزة للتسخين." },
|
|
792
|
+
{ id: "paused", title: "إيقاف التوصيل مؤقتا", description: "أوقف الوجبات مؤقتا أثناء السفر.", disabled: true }
|
|
793
|
+
]}
|
|
794
|
+
/>
|
|
795
|
+
<CaloSelectionCardRadioItem title="Standalone row" description="Use only when another component owns the group shell." selected onPress={previewOnly} />
|
|
796
|
+
</CaloInline>
|
|
797
|
+
</VariantGroup>
|
|
798
|
+
<VariantGroup title="Selection card recipes">
|
|
799
|
+
<CaloInline align="start" gap="size16" wrap>
|
|
800
|
+
<CaloSelectionCard
|
|
801
|
+
title="Option title"
|
|
802
|
+
description="Descriptive text goes here"
|
|
803
|
+
selected={selectionCardChoice === "visual"}
|
|
804
|
+
visual="face-savoring-food"
|
|
805
|
+
visualSize="small"
|
|
806
|
+
onPress={() => setSelectionCardChoice("visual")}
|
|
807
|
+
/>
|
|
808
|
+
<CaloSelectionCard
|
|
809
|
+
title="Option title"
|
|
810
|
+
selected={selectionCardChoice === "control"}
|
|
811
|
+
control="checkbox"
|
|
812
|
+
density="compact"
|
|
813
|
+
onPress={() => setSelectionCardChoice("control")}
|
|
814
|
+
/>
|
|
815
|
+
<CaloSelectionCard
|
|
816
|
+
title="Smoothie"
|
|
817
|
+
description="Description goes here"
|
|
818
|
+
selected={selectionCardChoice === "value"}
|
|
819
|
+
visual="smoothie-l"
|
|
820
|
+
visualPosition="leading"
|
|
821
|
+
visualSize="medium"
|
|
822
|
+
trailingMeta={<CaloSelectionCardPrice value="2.7" currency="BHD" oldValue="3.1 BHD" />}
|
|
823
|
+
onPress={() => setSelectionCardChoice("value")}
|
|
824
|
+
/>
|
|
825
|
+
<CaloSelectionCard
|
|
826
|
+
title="club"
|
|
827
|
+
titleTone="brand"
|
|
828
|
+
titleVariant="program"
|
|
829
|
+
selected={selectionCardChoice === "program"}
|
|
830
|
+
width="full"
|
|
831
|
+
titleTrailingMeta={
|
|
832
|
+
<CaloToggle
|
|
833
|
+
label="Join program"
|
|
834
|
+
selected={selectionCardChoice === "program"}
|
|
835
|
+
onSelectedChange={(selected) => setSelectionCardChoice(selected ? "program" : "value")}
|
|
836
|
+
/>
|
|
837
|
+
}
|
|
838
|
+
bodyMeta={
|
|
839
|
+
<CaloInline align="center" justify="between" gap="size16">
|
|
840
|
+
<CaloInline align="center" gap="size4" wrap>
|
|
841
|
+
<CaloText role="paragraphLargeStrong">Save</CaloText>
|
|
842
|
+
<CaloPriceText value="440" currency="SAR" tone="brand" size="medium" />
|
|
843
|
+
<CaloText role="paragraphLargeStrong">by joining Club</CaloText>
|
|
844
|
+
</CaloInline>
|
|
845
|
+
<CaloPriceText value="699" currency="SAR" size="medium" />
|
|
846
|
+
</CaloInline>
|
|
847
|
+
}
|
|
848
|
+
onPress={() => setSelectionCardChoice("program")}
|
|
849
|
+
/>
|
|
850
|
+
<CaloSelectionCard
|
|
851
|
+
title="For myself"
|
|
852
|
+
description="Descriptive explanatory text lines go here"
|
|
853
|
+
selected={selectionCardChoice === "media-control"}
|
|
854
|
+
control="checkbox"
|
|
855
|
+
density="media"
|
|
856
|
+
visual="meal-single-1"
|
|
857
|
+
visualSize="large"
|
|
858
|
+
onPress={() => setSelectionCardChoice("media-control")}
|
|
859
|
+
/>
|
|
860
|
+
<CaloSelectionCard
|
|
861
|
+
title="Bag 2"
|
|
862
|
+
selected={selectionCardChoice === "metric-summary"}
|
|
863
|
+
control="checkbox"
|
|
864
|
+
density="media"
|
|
865
|
+
bodyMeta={<CaloSelectionCardMacros calories="1234 kcal" macros={[{ label: "Protein", value: "120g", tone: "protein" }, { label: "Carbs", value: "123g", tone: "carbs" }, { label: "Fat", value: "123g", tone: "fat" }]} />}
|
|
866
|
+
onPress={() => setSelectionCardChoice("metric-summary")}
|
|
867
|
+
/>
|
|
868
|
+
<CaloSelectionCard
|
|
869
|
+
language="ar"
|
|
870
|
+
title="عنوان الخيار"
|
|
871
|
+
description="النص الوصفي يظهر هنا"
|
|
872
|
+
selected={selectionCardChoice === "arabic"}
|
|
873
|
+
control="checkbox"
|
|
874
|
+
visual="meal-single-1"
|
|
875
|
+
visualSize="large"
|
|
876
|
+
density="media"
|
|
877
|
+
onPress={() => setSelectionCardChoice("arabic")}
|
|
878
|
+
/>
|
|
879
|
+
</CaloInline>
|
|
880
|
+
</VariantGroup>
|
|
881
|
+
</GallerySection>
|
|
882
|
+
) : null}
|
|
883
|
+
|
|
884
|
+
{filterVisible("selection") ? (
|
|
885
|
+
<GallerySection title="Quantity" label="Steppers">
|
|
886
|
+
<VariantGroup title="Stepper variants">
|
|
887
|
+
<CaloInline align="center" gap="size12" wrap>
|
|
888
|
+
<CaloStepper value={stepperValue} size="large" onIncrement={() => setStepperValue((value) => value + 1)} onDecrement={() => setStepperValue((value) => Math.max(0, value - 1))} />
|
|
889
|
+
<CaloStepper value={compactValue} variant="compact" compactMode="auto" onIncrement={() => setCompactValue((value) => value + 1)} onDecrement={() => setCompactValue((value) => Math.max(0, value - 1))} />
|
|
890
|
+
<CaloStepper value={1} variant="compact" compactMode="delete" onIncrement={previewOnly} onDecrement={previewOnly} />
|
|
891
|
+
</CaloInline>
|
|
892
|
+
</VariantGroup>
|
|
893
|
+
</GallerySection>
|
|
894
|
+
) : null}
|
|
895
|
+
|
|
896
|
+
{filterVisible("forms") ? (
|
|
897
|
+
<GallerySection title="Inputs" label="Forms">
|
|
898
|
+
<CaloInput label="Full name" required helperText="Use the name shown on the account." value={fieldValue} onChangeText={setFieldValue} />
|
|
899
|
+
<CaloTextArea label="Delivery note" optional maxLength={120} value={noteValue} onChangeText={setNoteValue} />
|
|
900
|
+
<CaloVerificationCodeInput value={codeValue} helperText="Enter the 6 digit code sent to your phone." onChangeText={setCodeValue} />
|
|
901
|
+
<CaloPhoneInput value={phoneValue} helperText="We only use this for delivery updates." onChangeText={setPhoneValue} />
|
|
902
|
+
<CaloCardDetailsInput
|
|
903
|
+
cardNumber={cardNumber}
|
|
904
|
+
expiry={expiry}
|
|
905
|
+
cvv={cvv}
|
|
906
|
+
helperText="Card details stay grouped as one payment object."
|
|
907
|
+
onCardNumberChange={setCardNumber}
|
|
908
|
+
onExpiryChange={setExpiry}
|
|
909
|
+
onCvvChange={setCvv}
|
|
910
|
+
/>
|
|
911
|
+
<CaloPromoCodeInput value={promoValue} state="applied" helperText="Promo codes use the ticket-leading field with a right action." onChangeText={setPromoValue} onApply={previewOnly} />
|
|
912
|
+
</GallerySection>
|
|
913
|
+
) : null}
|
|
914
|
+
|
|
915
|
+
{filterVisible("feedback") ? (
|
|
916
|
+
<GallerySection title="Feedback" label="Toast">
|
|
917
|
+
<CaloToast message="Your plan was updated successfully." state="success" onDismiss={previewOnly} />
|
|
918
|
+
<CaloToast message="Some meals are unavailable for this branch." state="warning" onDismiss={previewOnly} />
|
|
919
|
+
<CaloToast message="Payment failed. Try another method." state="error" onDismiss={previewOnly} />
|
|
920
|
+
</GallerySection>
|
|
921
|
+
) : null}
|
|
922
|
+
|
|
923
|
+
{filterVisible("commerce") ? (
|
|
924
|
+
<GallerySection title="Offers" label="Voucher">
|
|
925
|
+
<VariantGroup title="Banners">
|
|
926
|
+
<CaloStack gap="size12">
|
|
927
|
+
<CaloBanner
|
|
928
|
+
variant="optIn"
|
|
929
|
+
title="Unlock 7 days offer"
|
|
930
|
+
description={[
|
|
931
|
+
"Get ",
|
|
932
|
+
{ text: "10% off +", strong: true },
|
|
933
|
+
" ",
|
|
934
|
+
{ text: "free daily juice", strong: true },
|
|
935
|
+
" worth 366 SAR with monthly & weekly plan."
|
|
936
|
+
]}
|
|
937
|
+
visualName="smoothie-1"
|
|
938
|
+
control={{ type: "toggle", label: "Unlock 7 days offer", selected: bannerSelected, onSelectedChange: setBannerSelected }}
|
|
939
|
+
/>
|
|
940
|
+
<CaloBanner
|
|
941
|
+
variant="optIn"
|
|
942
|
+
title="عرض فتح لمدة 7 أيام"
|
|
943
|
+
description="احصل على خصم 10% + عصير مجاني يومياً بقيمة 366 ريال سعودي مع الخطة الشهرية والأسبوعية."
|
|
944
|
+
language="ar"
|
|
945
|
+
selected
|
|
946
|
+
visualName="smoothie-1"
|
|
947
|
+
control={{ type: "toggle", label: "عرض فتح لمدة 7 أيام", selected: true, onSelectedChange: previewOnly }}
|
|
948
|
+
/>
|
|
949
|
+
<CaloBanner
|
|
950
|
+
variant="action"
|
|
951
|
+
state="warning"
|
|
952
|
+
title={["Your plan is ending in ", { text: "1 day", tone: "warning" }, "!"]}
|
|
953
|
+
description="Today is your last delivery. Renew now to continue your meal plan."
|
|
954
|
+
action={{ label: "Renew my plan", onPress: previewOnly }}
|
|
955
|
+
onDismiss={previewOnly}
|
|
956
|
+
visualName="meal-tray-fork"
|
|
957
|
+
/>
|
|
958
|
+
<CaloBanner
|
|
959
|
+
variant="action"
|
|
960
|
+
title="You skipped this bag"
|
|
961
|
+
description="You skipped this bag's delivery so there are no meals for us to show you."
|
|
962
|
+
action={{ label: "Unskip bag", onPress: previewOnly }}
|
|
963
|
+
onDismiss={previewOnly}
|
|
964
|
+
visualName="calo-bag-nice-today"
|
|
965
|
+
/>
|
|
966
|
+
</CaloStack>
|
|
967
|
+
</VariantGroup>
|
|
968
|
+
<VariantGroup title="Promotion banners">
|
|
969
|
+
<CaloStack gap="size16">
|
|
970
|
+
<CaloCarousel>
|
|
971
|
+
<CaloBanner
|
|
972
|
+
variant="promotion"
|
|
973
|
+
title="Summer smoothies are back"
|
|
974
|
+
description="Fresh blends with your next Cafe order."
|
|
975
|
+
image={smoothieImage}
|
|
976
|
+
tagLabel="Cafe offer"
|
|
977
|
+
onPress={previewOnly}
|
|
978
|
+
/>
|
|
979
|
+
<CaloBanner
|
|
980
|
+
variant="promotion"
|
|
981
|
+
title="Fuel your training week"
|
|
982
|
+
description="High-protein meals matched to your goals."
|
|
983
|
+
image={mealImage}
|
|
984
|
+
tagLabel="Featured"
|
|
985
|
+
onPress={previewOnly}
|
|
986
|
+
/>
|
|
987
|
+
</CaloCarousel>
|
|
988
|
+
<CaloBanner
|
|
989
|
+
variant="promotion"
|
|
990
|
+
title="Summer smoothies are back"
|
|
991
|
+
description="Fresh blends with your next Cafe order."
|
|
992
|
+
image={smoothieImage}
|
|
993
|
+
tagLabel="Cafe offer"
|
|
994
|
+
onPress={previewOnly}
|
|
995
|
+
onDismiss={previewOnly}
|
|
996
|
+
/>
|
|
997
|
+
<CaloInline gap="size16" wrap>
|
|
998
|
+
<CaloBanner
|
|
999
|
+
variant="promotion"
|
|
1000
|
+
title="Fuel your training week"
|
|
1001
|
+
description="High-protein meals matched to your goals."
|
|
1002
|
+
image={mealImage}
|
|
1003
|
+
tagLabel="Featured"
|
|
1004
|
+
onPress={previewOnly}
|
|
1005
|
+
/>
|
|
1006
|
+
<CaloBanner
|
|
1007
|
+
variant="promotion"
|
|
1008
|
+
title="عودة عروض العصائر"
|
|
1009
|
+
description="خلطات منعشة مع طلبك القادم من كافيه."
|
|
1010
|
+
image={smoothieImage}
|
|
1011
|
+
language="ar"
|
|
1012
|
+
tagLabel="عرض كافيه"
|
|
1013
|
+
onPress={previewOnly}
|
|
1014
|
+
onDismiss={previewOnly}
|
|
1015
|
+
/>
|
|
1016
|
+
</CaloInline>
|
|
1017
|
+
</CaloStack>
|
|
1018
|
+
</VariantGroup>
|
|
1019
|
+
<VariantGroup title="Voucher states">
|
|
1020
|
+
<CaloVoucherCard title="Get 20% off" condition="Applies to orders above 12 BHD" state="available" onPress={previewOnly} onInfoPress={previewOnly} />
|
|
1021
|
+
<CaloVoucherCard title="Free smoothie" condition="Applied to your next delivery" state="applied" visualName="smoothie-1" onPress={previewOnly} />
|
|
1022
|
+
<CaloVoucherCard title="Club reward" condition="This voucher is no longer valid" state="disabled" visualName="green-gift" />
|
|
1023
|
+
</VariantGroup>
|
|
1024
|
+
</GallerySection>
|
|
1025
|
+
) : null}
|
|
1026
|
+
|
|
1027
|
+
{filterVisible("commerce") ? (
|
|
1028
|
+
<GallerySection title="Delivery Status" label="Home">
|
|
1029
|
+
<VariantGroup title="Expanded active states">
|
|
1030
|
+
<CaloDeliveryStatusCard status="active" onSkipPress={previewOnly} onToggleExpanded={previewOnly} />
|
|
1031
|
+
<CaloDeliveryStatusCard status="active-address-time" onSkipPress={previewOnly} onToggleExpanded={previewOnly} />
|
|
1032
|
+
<CaloDeliveryStatusCard status="active-address" onSkipPress={previewOnly} onToggleExpanded={previewOnly} />
|
|
1033
|
+
</VariantGroup>
|
|
1034
|
+
<VariantGroup title="Expanded progress states">
|
|
1035
|
+
<CaloDeliveryStatusCard status="ready" onToggleExpanded={previewOnly} />
|
|
1036
|
+
<CaloDeliveryStatusCard status="on-the-way" statusText="Your driver is on the way" onToggleExpanded={previewOnly} />
|
|
1037
|
+
<CaloDeliveryStatusCard status="nearby" statusText="Nearby, delivering to Home" etaText="Arriving by 8:45 - 9:00 AM" onToggleExpanded={previewOnly} />
|
|
1038
|
+
<CaloDeliveryStatusCard status="delay" statusText="Running a bit late, but it's coming!" onToggleExpanded={previewOnly} />
|
|
1039
|
+
</VariantGroup>
|
|
1040
|
+
<VariantGroup title="Collapsed states">
|
|
1041
|
+
<CaloDeliveryStatusCard status="active" defaultExpanded={false} onToggleExpanded={previewOnly} />
|
|
1042
|
+
<CaloDeliveryStatusCard status="active-address-time" defaultExpanded={false} onToggleExpanded={previewOnly} />
|
|
1043
|
+
<CaloDeliveryStatusCard status="active-address" defaultExpanded={false} onToggleExpanded={previewOnly} />
|
|
1044
|
+
<CaloDeliveryStatusCard status="ready" defaultExpanded={false} onToggleExpanded={previewOnly} />
|
|
1045
|
+
<CaloDeliveryStatusCard status="on-the-way" defaultExpanded={false} onToggleExpanded={previewOnly} />
|
|
1046
|
+
<CaloDeliveryStatusCard status="nearby" defaultExpanded={false} onToggleExpanded={previewOnly} />
|
|
1047
|
+
<CaloDeliveryStatusCard status="delay" defaultExpanded={false} onToggleExpanded={previewOnly} />
|
|
1048
|
+
</VariantGroup>
|
|
1049
|
+
<VariantGroup title="Terminal states">
|
|
1050
|
+
<CaloDeliveryStatusCard status="delivered-rating" onNegativeRatingPress={previewOnly} onPositiveRatingPress={previewOnly} />
|
|
1051
|
+
<CaloDeliveryStatusCard status="delivered-done" deliveredAtText="Delivered at 9:00 PM to Home" />
|
|
1052
|
+
<CaloDeliveryStatusCard status="skipped" statusText="You skipped this delivery" deliveredAtText="No meals scheduled for this bag" />
|
|
1053
|
+
<CaloDeliveryStatusCard status="cancelled" statusText="This delivery was cancelled" deliveredAtText="Contact support if this looks wrong" />
|
|
1054
|
+
<CaloDeliveryStatusCard status="failed" statusText="We couldn't deliver this bag" deliveredAtText="Contact support to reschedule" />
|
|
1055
|
+
</VariantGroup>
|
|
1056
|
+
<VariantGroup title="Small screen">
|
|
1057
|
+
<CaloDeliveryStatusCard status="active" size="compact" onSkipPress={previewOnly} onToggleExpanded={previewOnly} />
|
|
1058
|
+
<CaloDeliveryStatusCard status="active" defaultExpanded={false} size="compact" onToggleExpanded={previewOnly} />
|
|
1059
|
+
<CaloDeliveryStatusCard status="nearby" size="compact" statusText="Nearby, delivering to Home" etaText="Arriving by 8:45 - 9:00 AM" onToggleExpanded={previewOnly} />
|
|
1060
|
+
<CaloDeliveryStatusCard status="delivered-done" size="compact" deliveredAtText="Delivered at 9:00 PM to Home" />
|
|
1061
|
+
</VariantGroup>
|
|
1062
|
+
<VariantGroup title="Arabic states">
|
|
1063
|
+
<CaloDeliveryStatusCard language="ar" status="active-address-time" onSkipPress={previewOnly} onToggleExpanded={previewOnly} />
|
|
1064
|
+
<CaloDeliveryStatusCard language="ar" status="active-address-time" defaultExpanded={false} onToggleExpanded={previewOnly} />
|
|
1065
|
+
<CaloDeliveryStatusCard language="ar" status="nearby" statusText="قريب منك، يتم التوصيل إلى المنزل" etaText="يصل بين 8:45 - 9:00 ص" onToggleExpanded={previewOnly} />
|
|
1066
|
+
<CaloDeliveryStatusCard language="ar" status="delivered-rating" onNegativeRatingPress={previewOnly} onPositiveRatingPress={previewOnly} />
|
|
1067
|
+
<CaloDeliveryStatusCard language="ar" status="delivered-done" deliveredAtText="تم التوصيل الساعة 9:00 م إلى المنزل" />
|
|
1068
|
+
<CaloDeliveryStatusCard language="ar" status="failed" statusText="تعذر توصيل هذه الحقيبة" deliveredAtText="تواصل مع الدعم لإعادة الجدولة" />
|
|
1069
|
+
</VariantGroup>
|
|
1070
|
+
</GallerySection>
|
|
1071
|
+
) : null}
|
|
1072
|
+
|
|
1073
|
+
{filterVisible("commerce") ? (
|
|
1074
|
+
<GallerySection title="Payment" label="Methods">
|
|
1075
|
+
<CaloPaymentMethodList
|
|
1076
|
+
selectedId={paymentMethodId}
|
|
1077
|
+
onSelect={setPaymentMethodId}
|
|
1078
|
+
footerAction={{
|
|
1079
|
+
label: "See all payment methods",
|
|
1080
|
+
onPress: previewOnly,
|
|
1081
|
+
trailingIcon: <CaloIcon name="CaretDown" tone="brand" size="sm" />
|
|
1082
|
+
}}
|
|
1083
|
+
options={[
|
|
1084
|
+
{ id: "wallet", type: "wallet", balance: "10 BHD" },
|
|
1085
|
+
{ id: "apple-pay", type: "applePay" },
|
|
1086
|
+
{ id: "saved-card", type: "savedCard", brand: "visa", last4: "4242" },
|
|
1087
|
+
{ id: "new-card", type: "newCard" },
|
|
1088
|
+
{ id: "tabby", type: "tabby", descriptionEmphasis: "Learn more", descriptionEmphasisTone: "brand" },
|
|
1089
|
+
{ id: "tamara", type: "tamara" }
|
|
1090
|
+
]}
|
|
1091
|
+
newCardDetails={{
|
|
1092
|
+
cardNumber,
|
|
1093
|
+
expiry,
|
|
1094
|
+
cvv,
|
|
1095
|
+
onCardNumberChange: setCardNumber,
|
|
1096
|
+
onExpiryChange: setExpiry,
|
|
1097
|
+
onCvvChange: setCvv,
|
|
1098
|
+
helperText: "We will verify your card with a small temporary charge."
|
|
1099
|
+
}}
|
|
1100
|
+
/>
|
|
1101
|
+
</GallerySection>
|
|
1102
|
+
) : null}
|
|
1103
|
+
|
|
1104
|
+
{filterVisible("food") ? (
|
|
1105
|
+
<GallerySection title="Dietary Cards" label="Food">
|
|
1106
|
+
<CaloInline gap="size16" wrap>
|
|
1107
|
+
<CaloDietaryCard type="balanced" selected={dietaryPlan === "balanced"} onPress={() => setDietaryPlan("balanced")} />
|
|
1108
|
+
<CaloDietaryCard type="highProtein" selected={dietaryPlan === "highProtein"} onPress={() => setDietaryPlan("highProtein")} />
|
|
1109
|
+
<CaloDietaryCard type="personalized" selected={dietaryPlan === "personalized"} onPress={() => setDietaryPlan("personalized")} onEditMacrosPress={previewOnly} />
|
|
1110
|
+
<CaloDietaryCard type="lowCarb" language="ar" selected={dietaryPlan === "lowCarb"} onPress={() => setDietaryPlan("lowCarb")} />
|
|
1111
|
+
</CaloInline>
|
|
1112
|
+
</GallerySection>
|
|
1113
|
+
) : null}
|
|
1114
|
+
|
|
1115
|
+
{filterVisible("food") ? (
|
|
1116
|
+
<GallerySection title="Category Tile Grid" label="Food">
|
|
1117
|
+
<CaloCategoryTileGrid items={cafeCategoryItems} onSelect={previewOnly} />
|
|
1118
|
+
</GallerySection>
|
|
1119
|
+
) : null}
|
|
1120
|
+
|
|
1121
|
+
{filterVisible("food") ? (
|
|
1122
|
+
<GallerySection title="Meal Cards" label="Food">
|
|
1123
|
+
<CaloMacroSummary macros={macros} />
|
|
1124
|
+
<CaloFoodPreferenceWarning label="Contains dairy" tone="warning" />
|
|
1125
|
+
<CaloInline align="center" gap="size8" wrap>
|
|
1126
|
+
<CaloRatingChip label="Loved it" selected={rating === "Loved it"} onPress={() => setRating("Loved it")} />
|
|
1127
|
+
<CaloRatingChip label="Okay" selected={rating === "Okay"} onPress={() => setRating("Okay")} />
|
|
1128
|
+
</CaloInline>
|
|
1129
|
+
<CaloInline align="start" gap="size12" wrap>
|
|
1130
|
+
<CaloCompactVariantTile
|
|
1131
|
+
image={sauceImage}
|
|
1132
|
+
title="Rose Sauce"
|
|
1133
|
+
meta="120 kcal"
|
|
1134
|
+
selected={selectedVariant === "rose"}
|
|
1135
|
+
onPress={() => setSelectedVariant("rose")}
|
|
1136
|
+
/>
|
|
1137
|
+
<CaloCompactVariantTile
|
|
1138
|
+
image={sauceImage}
|
|
1139
|
+
title="Spicy Sauce"
|
|
1140
|
+
meta="80 kcal"
|
|
1141
|
+
selected={selectedVariant === "spicy"}
|
|
1142
|
+
onPress={() => setSelectedVariant("spicy")}
|
|
1143
|
+
/>
|
|
1144
|
+
<CaloCompactVariantTile
|
|
1145
|
+
image={sauceImage}
|
|
1146
|
+
title="صلصة الورد"
|
|
1147
|
+
meta="120 ك.كال"
|
|
1148
|
+
language="ar"
|
|
1149
|
+
selected={selectedVariant === "arabic"}
|
|
1150
|
+
onPress={() => setSelectedVariant("arabic")}
|
|
1151
|
+
/>
|
|
1152
|
+
</CaloInline>
|
|
1153
|
+
<CaloProductTile
|
|
1154
|
+
image={smoothieImage}
|
|
1155
|
+
name="Mango protein smoothie"
|
|
1156
|
+
calories="210 Cal"
|
|
1157
|
+
price="2.4 BHD"
|
|
1158
|
+
badges={[{ label: "New", variant: "brand" }]}
|
|
1159
|
+
favorite
|
|
1160
|
+
quantity={compactValue}
|
|
1161
|
+
onFavoritePress={previewOnly}
|
|
1162
|
+
onIncrement={() => setCompactValue((value) => value + 1)}
|
|
1163
|
+
onDecrement={() => setCompactValue((value) => Math.max(0, value - 1))}
|
|
1164
|
+
/>
|
|
1165
|
+
<CaloProductTile
|
|
1166
|
+
image={mealImage}
|
|
1167
|
+
name="Meal title goes here"
|
|
1168
|
+
calories="123 kcal"
|
|
1169
|
+
price="2.5 BHD"
|
|
1170
|
+
badges={[{ label: "Label", variant: "neutral" }]}
|
|
1171
|
+
metadataLayout="inline"
|
|
1172
|
+
/>
|
|
1173
|
+
<CaloStack gap="size12">
|
|
1174
|
+
<CaloCompactProductRowCard
|
|
1175
|
+
image={smoothieImage}
|
|
1176
|
+
title="The Brain Wizard Bar"
|
|
1177
|
+
meta="150 kcal"
|
|
1178
|
+
price="2.5"
|
|
1179
|
+
priceUnit="BHD"
|
|
1180
|
+
action="add"
|
|
1181
|
+
onAddPress={() => setMarketQuantity(1)}
|
|
1182
|
+
/>
|
|
1183
|
+
<CaloCompactProductRowCard
|
|
1184
|
+
image={smoothieImage}
|
|
1185
|
+
title="بار معالج الدماغ"
|
|
1186
|
+
meta="495 ك.كال"
|
|
1187
|
+
price="2.5"
|
|
1188
|
+
priceUnit="د.ب"
|
|
1189
|
+
action="stepper"
|
|
1190
|
+
quantity={Math.max(1, marketQuantity)}
|
|
1191
|
+
language="ar"
|
|
1192
|
+
onIncrement={() => setMarketQuantity((value) => value + 1)}
|
|
1193
|
+
onDecrement={() => setMarketQuantity((value) => Math.max(0, value - 1))}
|
|
1194
|
+
/>
|
|
1195
|
+
</CaloStack>
|
|
1196
|
+
<CaloInline gap="size16" wrap>
|
|
1197
|
+
<CaloMarketProductCard
|
|
1198
|
+
image={mealImage}
|
|
1199
|
+
name="Maximum 2 lines of market product title"
|
|
1200
|
+
price="3.5 BHD"
|
|
1201
|
+
oldPrice="3.96 BHD"
|
|
1202
|
+
discountLabel="15% off"
|
|
1203
|
+
action={marketQuantity > 0 ? "stepper" : "add"}
|
|
1204
|
+
quantity={marketQuantity}
|
|
1205
|
+
onAddPress={() => setMarketQuantity(1)}
|
|
1206
|
+
onIncrement={() => setMarketQuantity((value) => value + 1)}
|
|
1207
|
+
onDecrement={() => setMarketQuantity((value) => Math.max(0, value - 1))}
|
|
1208
|
+
/>
|
|
1209
|
+
<CaloCafeProductCard
|
|
1210
|
+
image={smoothieImage}
|
|
1211
|
+
name="Mango protein smoothie"
|
|
1212
|
+
calories="210 kcal"
|
|
1213
|
+
price="2.4 BHD"
|
|
1214
|
+
action="stepper"
|
|
1215
|
+
quantity={cafeQuantity}
|
|
1216
|
+
scrolled
|
|
1217
|
+
onIncrement={() => setCafeQuantity((value) => value + 1)}
|
|
1218
|
+
onDecrement={() => setCafeQuantity((value) => Math.max(0, value - 1))}
|
|
1219
|
+
/>
|
|
1220
|
+
</CaloInline>
|
|
1221
|
+
<CaloSelectableMealTile
|
|
1222
|
+
image={mealImage}
|
|
1223
|
+
name="Chicken teriyaki rice bowl"
|
|
1224
|
+
subtitle="Lunch"
|
|
1225
|
+
calories="520 Cal"
|
|
1226
|
+
macros={macros}
|
|
1227
|
+
badges={[{ label: "High protein", variant: "brand" }]}
|
|
1228
|
+
selected={mealSelected}
|
|
1229
|
+
onPress={() => setMealSelected((selected) => !selected)}
|
|
1230
|
+
/>
|
|
1231
|
+
<CaloMenuMealRow
|
|
1232
|
+
image={mealImage}
|
|
1233
|
+
name="Chicken teriyaki rice bowl"
|
|
1234
|
+
mealType="Lunch"
|
|
1235
|
+
calories="520 Cal"
|
|
1236
|
+
macros={macros}
|
|
1237
|
+
badges={[{ label: "New", variant: "outline" }]}
|
|
1238
|
+
imageBottomBadge={{ label: "Contains dairy", tone: "danger" }}
|
|
1239
|
+
favorite
|
|
1240
|
+
onFavoritePress={previewOnly}
|
|
1241
|
+
onSwapPress={previewOnly}
|
|
1242
|
+
onCustomizePress={previewOnly}
|
|
1243
|
+
/>
|
|
1244
|
+
<CaloMenuMealRow
|
|
1245
|
+
image={smoothieImage}
|
|
1246
|
+
name="Mango protein smoothie"
|
|
1247
|
+
mealType="Snack"
|
|
1248
|
+
calories="210 Cal"
|
|
1249
|
+
macros={macros}
|
|
1250
|
+
mode="rate"
|
|
1251
|
+
selectedRatingId={rowRating}
|
|
1252
|
+
onRatingSelect={setRowRating}
|
|
1253
|
+
/>
|
|
1254
|
+
<CaloSwapMealCard image={mealImage} name="Grilled chicken with saffron rice" mealType="Dinner" calories="610 Cal" macros={macros} state="default" onPress={previewOnly} onFavoritePress={previewOnly} />
|
|
1255
|
+
<CaloSwapMealCard image={mealImage} name="Grilled chicken with saffron rice" mealType="Dinner" calories="610 Cal" macros={macros} state="loading" />
|
|
1256
|
+
<CaloSwapMealCard image={mealImage} name="Grilled chicken with saffron rice" mealType="Dinner" calories="610 Cal" macros={macros} state="fetching" />
|
|
1257
|
+
<CaloSwapMealCard image={mealImage} name="Grilled chicken with saffron rice" mealType="Dinner" calories="610 Cal" macros={macros} state="selected" />
|
|
1258
|
+
<CaloMealRow
|
|
1259
|
+
image={mealImage}
|
|
1260
|
+
name="Grilled chicken with saffron rice"
|
|
1261
|
+
mealType="Dinner"
|
|
1262
|
+
calories="610 Cal"
|
|
1263
|
+
macros={macros}
|
|
1264
|
+
badges={[{ label: "Chef pick", variant: "outline" }]}
|
|
1265
|
+
warnings={[{ label: "Contains nuts", tone: "danger" }]}
|
|
1266
|
+
selected
|
|
1267
|
+
favorite
|
|
1268
|
+
actionLabel="Swap"
|
|
1269
|
+
onActionPress={previewOnly}
|
|
1270
|
+
onFavoritePress={previewOnly}
|
|
1271
|
+
/>
|
|
1272
|
+
<CaloMealRatingCard
|
|
1273
|
+
image={mealImage}
|
|
1274
|
+
title="Chicken teriyaki rice bowl"
|
|
1275
|
+
description="Tell us how this meal landed so future menus learn your taste."
|
|
1276
|
+
badgeLabel="Lunch"
|
|
1277
|
+
selectedOptionId={cardRating}
|
|
1278
|
+
onOptionSelect={setCardRating}
|
|
1279
|
+
/>
|
|
1280
|
+
<CaloCustomizationOptionCard
|
|
1281
|
+
image={ingredientPenneImage}
|
|
1282
|
+
quantityTag="2x"
|
|
1283
|
+
title="Penne"
|
|
1284
|
+
metadata={["235 kcal", "120g"]}
|
|
1285
|
+
price="(+ SAR 3)"
|
|
1286
|
+
warning="Contains gluten"
|
|
1287
|
+
detailsTitle="Ingredients"
|
|
1288
|
+
details={["Durum wheat semolina, water, ", { text: "gluten", tone: "danger" }, ", sea salt."]}
|
|
1289
|
+
macros={macros}
|
|
1290
|
+
defaultExpanded
|
|
1291
|
+
secondaryActionLabel="Swap sauce"
|
|
1292
|
+
selected={checkboxSelected}
|
|
1293
|
+
onPress={() => setCheckboxSelected((selected) => !selected)}
|
|
1294
|
+
onSecondaryActionPress={previewOnly}
|
|
1295
|
+
/>
|
|
1296
|
+
<CaloCustomizationOptionCard
|
|
1297
|
+
image={ingredientPenneImage}
|
|
1298
|
+
title="Penne"
|
|
1299
|
+
metadata={["235 kcal", "120g"]}
|
|
1300
|
+
detailsTitle="Ingredients"
|
|
1301
|
+
details={["Durum wheat semolina, water, sea salt."]}
|
|
1302
|
+
macros={macros}
|
|
1303
|
+
selected={!checkboxSelected}
|
|
1304
|
+
onPress={() => setCheckboxSelected((selected) => !selected)}
|
|
1305
|
+
/>
|
|
1306
|
+
<CaloCustomizationOptionCard
|
|
1307
|
+
image={ingredientPenneImage}
|
|
1308
|
+
title="بيني"
|
|
1309
|
+
metadata={["235 سعرة", "120غ"]}
|
|
1310
|
+
price="(+ 3 ر.س)"
|
|
1311
|
+
warning={{ label: "يحتوي على الغلوتين", tone: "danger" }}
|
|
1312
|
+
detailsTitle="المكونات"
|
|
1313
|
+
details={["سميد القمح القاسي، ماء، ", { text: "غلوتين", tone: "danger" }, "، ملح بحري."]}
|
|
1314
|
+
macros={[
|
|
1315
|
+
{ label: "ب", value: "37غ", tone: "protein" },
|
|
1316
|
+
{ label: "ك", value: "42غ", tone: "carbs" },
|
|
1317
|
+
{ label: "د", value: "14غ", tone: "fat" }
|
|
1318
|
+
]}
|
|
1319
|
+
defaultExpanded
|
|
1320
|
+
secondaryActionLabel="بدل الصوص"
|
|
1321
|
+
selected
|
|
1322
|
+
language="ar"
|
|
1323
|
+
onPress={previewOnly}
|
|
1324
|
+
onSecondaryActionPress={previewOnly}
|
|
1325
|
+
/>
|
|
1326
|
+
<CaloCustomizationOptionCard title="Choose spicy sauce" subtitle="One sauce per meal" price="Free" selectionType="radio" selected={radioSelected === "spicy"} onPress={() => setRadioSelected("spicy")} />
|
|
1327
|
+
<CaloCustomizationOptionCard image={ingredientPenneImage} title="Unavailable pasta" metadata={["235 kcal", "120g"]} selected={false} disabled onPress={previewOnly} />
|
|
1328
|
+
</GallerySection>
|
|
1329
|
+
) : null}
|
|
1330
|
+
|
|
1331
|
+
{filterVisible("commerce") ? (
|
|
1332
|
+
<GallerySection title="Cart Items" label="Cafe + Market">
|
|
1333
|
+
<VariantGroup title="Cart item variants">
|
|
1334
|
+
<CaloCartItemCard
|
|
1335
|
+
family="cafe"
|
|
1336
|
+
image={mealImage}
|
|
1337
|
+
name="Chicken katsu curry"
|
|
1338
|
+
details="Extra sauce, no sesame"
|
|
1339
|
+
price="3.5 BHD"
|
|
1340
|
+
oldPrice="3.96 BHD"
|
|
1341
|
+
quantity={stepperValue}
|
|
1342
|
+
onEditPress={previewOnly}
|
|
1343
|
+
onIncrement={() => setStepperValue((value) => value + 1)}
|
|
1344
|
+
onDecrement={() => setStepperValue((value) => Math.max(0, value - 1))}
|
|
1345
|
+
/>
|
|
1346
|
+
<CaloDivider />
|
|
1347
|
+
<CaloCartItemCard family="market" image={marketImage} name="Organic salad bowl" price="2.9 BHD" quantity={1} onIncrement={previewOnly} onDecrement={previewOnly} />
|
|
1348
|
+
<CaloDivider />
|
|
1349
|
+
<CaloCartItemCard family="cafe" variant="loyalty" image={smoothieImage} name="Reward smoothie" details="Club reward item" price="Free" quantity={1} onEditPress={previewOnly} onIncrement={previewOnly} onDecrement={previewOnly} />
|
|
1350
|
+
<CaloDivider />
|
|
1351
|
+
<CaloCartItemCard family="cafe" variant="unavailable" image={mealImage} name="Breakfast egg wrap" price="2.1 BHD" unavailableLabel="Not available at this branch" onRemovePress={previewOnly} />
|
|
1352
|
+
</VariantGroup>
|
|
1353
|
+
</GallerySection>
|
|
1354
|
+
) : null}
|
|
1355
|
+
|
|
1356
|
+
{/* ===== Web / marketing components (Healthy UI "Web Components") ===== */}
|
|
1357
|
+
{filterVisible("marketing") ? (
|
|
1358
|
+
<GallerySection title="FAQ" label="Marketing">
|
|
1359
|
+
<CaloFaqAccordion
|
|
1360
|
+
expandMode="single"
|
|
1361
|
+
defaultOpenIds={["pause"]}
|
|
1362
|
+
items={[
|
|
1363
|
+
{ id: "pause", question: "How do I pause my subscription?", answer: "To put your subscription on hold, sign in to your account, go to 'Account Settings', and choose 'Pause Subscription'. You can set the duration of the pause according to your needs." },
|
|
1364
|
+
{ id: "delivery", question: "When will my meals arrive?", answer: "Your meals are delivered fresh before 7 AM on the days you choose." },
|
|
1365
|
+
{ id: "swap", question: "Can I swap a meal I don't like?", answer: "Yes — open the menu for that day and pick any alternative before the cut-off time." }
|
|
1366
|
+
]}
|
|
1367
|
+
/>
|
|
1368
|
+
<CaloFaqAccordion
|
|
1369
|
+
expandMode="multiple"
|
|
1370
|
+
size="M"
|
|
1371
|
+
language="ar"
|
|
1372
|
+
defaultOpenIds={["pause"]}
|
|
1373
|
+
items={[
|
|
1374
|
+
{ id: "pause", question: "كيف يمكنني إيقاف اشتراكي مؤقتًا؟", answer: 'لإيقاف اشتراكك مؤقتًا، سجّل الدخول إلى حسابك، ثم انتقل إلى "إعدادات الحساب"، واختر "إيقاف الاشتراك مؤقتًا".' },
|
|
1375
|
+
{ id: "delivery", question: "متى ستصل وجباتي؟", answer: "تصلك وجباتك طازجة قبل الساعة 7 صباحًا في الأيام التي تختارها." }
|
|
1376
|
+
]}
|
|
1377
|
+
/>
|
|
1378
|
+
</GallerySection>
|
|
1379
|
+
) : null}
|
|
1380
|
+
|
|
1381
|
+
{filterVisible("marketing") ? (
|
|
1382
|
+
<GallerySection title="Marketing Cards" label="Marketing">
|
|
1383
|
+
<CaloInline gap="size16" wrap>
|
|
1384
|
+
<CaloTestimonialCard
|
|
1385
|
+
avatar={{ uri: "https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=200" }}
|
|
1386
|
+
name="Safa Ebrahim"
|
|
1387
|
+
subtitle="Calo subscription"
|
|
1388
|
+
rating={5}
|
|
1389
|
+
quote="I've been subscribed to Calo for over a year and a half now, and I can't imagine being without it! I reached my goal weight, but most of all, I love the flexibility and the outstanding customer service."
|
|
1390
|
+
/>
|
|
1391
|
+
<CaloSupportPromptCard
|
|
1392
|
+
title="Got more questions?"
|
|
1393
|
+
description="Start a live chat with us or fill out the form. We're here to help!"
|
|
1394
|
+
avatars={[
|
|
1395
|
+
{ uri: "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=120" },
|
|
1396
|
+
{ uri: "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=120" },
|
|
1397
|
+
{ uri: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=120" }
|
|
1398
|
+
]}
|
|
1399
|
+
action={{ label: "Chat with us", onPress: previewOnly }}
|
|
1400
|
+
/>
|
|
1401
|
+
<CaloUgcCard
|
|
1402
|
+
image={{ uri: "https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=400" }}
|
|
1403
|
+
avatar={{ uri: "https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=120" }}
|
|
1404
|
+
handle="@amaal"
|
|
1405
|
+
/>
|
|
1406
|
+
<CaloFeatureCard
|
|
1407
|
+
image={{ uri: "https://images.unsplash.com/photo-1512621776951-a57141f2eefd?w=600" }}
|
|
1408
|
+
title="Calories and macros that match your goals"
|
|
1409
|
+
description="Eat with confidence knowing every meal is cooked and portioned to your goals."
|
|
1410
|
+
/>
|
|
1411
|
+
</CaloInline>
|
|
1412
|
+
</GallerySection>
|
|
1413
|
+
) : null}
|
|
1414
|
+
|
|
1415
|
+
{filterVisible("marketing") ? (
|
|
1416
|
+
<GallerySection title="Partner Cards" label="Marketing">
|
|
1417
|
+
<CaloStack gap="size16">
|
|
1418
|
+
<CaloPartnerCard
|
|
1419
|
+
logo={<CaloText role="heading4">Fitness First</CaloText>}
|
|
1420
|
+
locations={["Deira City Center", "Bur Juman Centre", "DIFC"]}
|
|
1421
|
+
perks={["3 branches across Dubai", "Mixed gym & ladies gym", "Personal trainers & free classes"]}
|
|
1422
|
+
/>
|
|
1423
|
+
<CaloPartnerCard
|
|
1424
|
+
language="ar"
|
|
1425
|
+
logo={<CaloText role="heading4">Fitness First</CaloText>}
|
|
1426
|
+
locations={["سيتي سنتر ديرة", "مول بر جمان", "مركز المالي"]}
|
|
1427
|
+
perks={["ثلاثة فروع في دبي", "صالة رياضية مختلطة وصالة رياضية للسيدات", "مدربون شخصيون ودروس مجانية"]}
|
|
1428
|
+
/>
|
|
1429
|
+
</CaloStack>
|
|
1430
|
+
</GallerySection>
|
|
1431
|
+
) : null}
|
|
1432
|
+
|
|
1433
|
+
{filterVisible("food") ? (
|
|
1434
|
+
<GallerySection title="Plan Cards" label="Food">
|
|
1435
|
+
<CaloPlanCardGrid>
|
|
1436
|
+
<CaloPlanCard type="balanced" selected onSelect={previewOnly} onLearnMore={previewOnly} />
|
|
1437
|
+
<CaloPlanCard type="highProtein" onSelect={previewOnly} onLearnMore={previewOnly} />
|
|
1438
|
+
<CaloPlanCard type="chefPicks" onSelect={previewOnly} onLearnMore={previewOnly} />
|
|
1439
|
+
<CaloPlanCard type="customMacros" onSelect={previewOnly} onLearnMore={previewOnly} />
|
|
1440
|
+
</CaloPlanCardGrid>
|
|
1441
|
+
</GallerySection>
|
|
1442
|
+
) : null}
|
|
1443
|
+
|
|
1444
|
+
{filterVisible("selection") ? (
|
|
1445
|
+
<GallerySection title="Quantity Row" label="Steppers">
|
|
1446
|
+
<CaloStack gap="size8">
|
|
1447
|
+
<CaloQuantityRow label="Meals per day" value={webMeals} min={1} max={6} onIncrement={() => setWebMeals((v) => Math.min(6, v + 1))} onDecrement={() => setWebMeals((v) => Math.max(1, v - 1))} />
|
|
1448
|
+
<CaloQuantityRow label="Protein boost" subLabel="+12g protein per meal" value={1} max={3} error="Limited to 3 per plan" onIncrement={previewOnly} onDecrement={previewOnly} />
|
|
1449
|
+
<CaloQuantityRow label="وجبات في اليوم" subLabel="عدد الوجبات لكل يوم" value={webMeals} min={1} max={6} language="ar" onIncrement={() => setWebMeals((v) => Math.min(6, v + 1))} onDecrement={() => setWebMeals((v) => Math.max(1, v - 1))} />
|
|
1450
|
+
</CaloStack>
|
|
1451
|
+
</GallerySection>
|
|
1452
|
+
) : null}
|
|
1453
|
+
|
|
1454
|
+
{filterVisible("navigation") ? (
|
|
1455
|
+
<GallerySection title="Pagination" label="Navigation">
|
|
1456
|
+
<CaloStack gap="size24">
|
|
1457
|
+
<CaloPagination page={paginationPage} pageCount={7} onChange={setPaginationPage} />
|
|
1458
|
+
<CaloPagination page={paginationPage} pageCount={7} size="small" onChange={setPaginationPage} />
|
|
1459
|
+
<CaloPagination page={paginationPage} pageCount={20} siblingCount={1} showPrevNext onChange={setPaginationPage} />
|
|
1460
|
+
<CaloPagination page={paginationPage} pageCount={7} language="ar" onChange={setPaginationPage} />
|
|
1461
|
+
</CaloStack>
|
|
1462
|
+
</GallerySection>
|
|
1463
|
+
) : null}
|
|
1464
|
+
|
|
1465
|
+
{filterVisible("food") ? (
|
|
1466
|
+
<GallerySection title="Meal Feature Cards" label="Food">
|
|
1467
|
+
<CaloInline gap="size16" wrap>
|
|
1468
|
+
<CaloMealMacroCard
|
|
1469
|
+
image={{ uri: "https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=600" }}
|
|
1470
|
+
visual="avocado"
|
|
1471
|
+
title="Low carb"
|
|
1472
|
+
macros={[
|
|
1473
|
+
{ label: "Protein", value: "40%", tone: "protein" },
|
|
1474
|
+
{ label: "Carbs", value: "20%", tone: "carbs" },
|
|
1475
|
+
{ label: "Fat", value: "40%", tone: "fat" }
|
|
1476
|
+
]}
|
|
1477
|
+
/>
|
|
1478
|
+
<CaloMealMacroCard
|
|
1479
|
+
image={{ uri: "https://images.unsplash.com/photo-1567620905732-2d1ec7ab7445?w=600" }}
|
|
1480
|
+
visual="meat"
|
|
1481
|
+
title="High Protein Plan"
|
|
1482
|
+
macros={[
|
|
1483
|
+
{ label: "Protein", value: "50%", tone: "protein" },
|
|
1484
|
+
{ label: "Carbs", value: "25%", tone: "carbs" },
|
|
1485
|
+
{ label: "Fat", value: "25%", tone: "fat" }
|
|
1486
|
+
]}
|
|
1487
|
+
/>
|
|
1488
|
+
</CaloInline>
|
|
1489
|
+
<CaloInline gap="size16" wrap>
|
|
1490
|
+
<CaloDishCard
|
|
1491
|
+
image={{ uri: "https://images.unsplash.com/photo-1467003909585-2f8a72700288?w=400" }}
|
|
1492
|
+
calories="646 kcal"
|
|
1493
|
+
isNew
|
|
1494
|
+
title="Spiced Cajun Salmon Salad"
|
|
1495
|
+
description="Description up to 2 lines talking about the food"
|
|
1496
|
+
macros={[
|
|
1497
|
+
{ label: "Pro", value: "20g", tone: "protein" },
|
|
1498
|
+
{ label: "Carbs", value: "19g", tone: "carbs" },
|
|
1499
|
+
{ label: "Fat", value: "19g", tone: "fat" }
|
|
1500
|
+
]}
|
|
1501
|
+
/>
|
|
1502
|
+
<CaloDishCard
|
|
1503
|
+
image={{ uri: "https://images.unsplash.com/photo-1455619452474-d2be8b1e70cd?w=400" }}
|
|
1504
|
+
calories="646 kcal"
|
|
1505
|
+
title="Asian Beef Rice Bowl"
|
|
1506
|
+
detailed={false}
|
|
1507
|
+
/>
|
|
1508
|
+
</CaloInline>
|
|
1509
|
+
</GallerySection>
|
|
1510
|
+
) : null}
|
|
1511
|
+
|
|
1512
|
+
{filterVisible("commerce") ? (
|
|
1513
|
+
<GallerySection title="Checkout Summary" label="Commerce">
|
|
1514
|
+
<CaloInline gap="size16" wrap>
|
|
1515
|
+
<CaloCheckoutSummaryCard
|
|
1516
|
+
subtitle="Balanced, 5 Meals, 5 Days per week"
|
|
1517
|
+
visual={{ uri: "https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=400" }}
|
|
1518
|
+
promoValue=""
|
|
1519
|
+
onPromoChangeText={previewOnly}
|
|
1520
|
+
promoState="default"
|
|
1521
|
+
onPromoApply={previewOnly}
|
|
1522
|
+
lineItems={[
|
|
1523
|
+
{ label: "Plan price", value: "2,200", currency: "SAR" },
|
|
1524
|
+
{ label: "Delivery fee", value: "FREE", tone: "positive" },
|
|
1525
|
+
{ label: "VAT(5%)", value: "115", currency: "SAR", info: true }
|
|
1526
|
+
]}
|
|
1527
|
+
total={{ label: "Total", value: "2,085", currency: "SAR" }}
|
|
1528
|
+
checkoutVariant="default"
|
|
1529
|
+
onCheckout={previewOnly}
|
|
1530
|
+
/>
|
|
1531
|
+
<CaloCheckoutSummaryCard
|
|
1532
|
+
subtitle="Balanced, 5 Meals, 5 Days per week"
|
|
1533
|
+
visual={{ uri: "https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=400" }}
|
|
1534
|
+
promoValue="CALO2025"
|
|
1535
|
+
promoState="applied"
|
|
1536
|
+
onPromoRemove={previewOnly}
|
|
1537
|
+
lineItems={[
|
|
1538
|
+
{ label: "Plan price", value: "2,200", currency: "SAR" },
|
|
1539
|
+
{ label: "Delivery fee", value: "FREE", tone: "positive" },
|
|
1540
|
+
{ label: "Discount", value: "115", currency: "SAR", tone: "positive", negative: true },
|
|
1541
|
+
{ label: "VAT(5%)", value: "115", currency: "SAR", info: true }
|
|
1542
|
+
]}
|
|
1543
|
+
total={{ label: "Total", value: "2,085", currency: "SAR" }}
|
|
1544
|
+
checkoutVariant="applePay"
|
|
1545
|
+
onApplePay={previewOnly}
|
|
1546
|
+
/>
|
|
1547
|
+
<CaloCheckoutSummaryCard
|
|
1548
|
+
subtitle="Balanced, 5 Meals, 5 Days per week"
|
|
1549
|
+
visual={{ uri: "https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=400" }}
|
|
1550
|
+
promoValue="CALO2025"
|
|
1551
|
+
promoState="error"
|
|
1552
|
+
onPromoChangeText={previewOnly}
|
|
1553
|
+
onPromoApply={previewOnly}
|
|
1554
|
+
promoErrorLabel="This promo code is not valid for this plan."
|
|
1555
|
+
lineItems={[
|
|
1556
|
+
{ label: "Plan price", value: "2,200", currency: "SAR" },
|
|
1557
|
+
{ label: "Delivery fee", value: "FREE", tone: "positive" },
|
|
1558
|
+
{ label: "VAT(5%)", value: "115", currency: "SAR", info: true }
|
|
1559
|
+
]}
|
|
1560
|
+
total={{ label: "Total", value: "2,085", currency: "SAR" }}
|
|
1561
|
+
onCheckout={previewOnly}
|
|
1562
|
+
/>
|
|
1563
|
+
</CaloInline>
|
|
1564
|
+
</GallerySection>
|
|
1565
|
+
) : null}
|
|
1566
|
+
</CaloStack>
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
function GallerySection({ title, label, children }: { title: string; label: string; children: React.ReactNode }) {
|
|
1571
|
+
return (
|
|
1572
|
+
<CaloStack gap="size16">
|
|
1573
|
+
<CaloInline align="center" justify="between">
|
|
1574
|
+
<CaloText role="section">{title}</CaloText>
|
|
1575
|
+
<CaloBadge label={label} variant="neutral" />
|
|
1576
|
+
</CaloInline>
|
|
1577
|
+
<CaloStack gap="size28">{children}</CaloStack>
|
|
1578
|
+
</CaloStack>
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
export function ComponentGalleryFilters({
|
|
1583
|
+
selectedFilter,
|
|
1584
|
+
onFilterChange
|
|
1585
|
+
}: {
|
|
1586
|
+
selectedFilter: GalleryCategory;
|
|
1587
|
+
visibleSectionCount?: number;
|
|
1588
|
+
onFilterChange: (filter: GalleryCategory) => void;
|
|
1589
|
+
}) {
|
|
1590
|
+
return (
|
|
1591
|
+
<CaloStack gap="size8">
|
|
1592
|
+
<CaloInline align="center" gap="size8" wrap>
|
|
1593
|
+
{galleryFilters.map((filter) => (
|
|
1594
|
+
<CaloButton
|
|
1595
|
+
key={filter.id}
|
|
1596
|
+
label={filter.label}
|
|
1597
|
+
size="small"
|
|
1598
|
+
variant={selectedFilter === filter.id ? "secondary" : "utility"}
|
|
1599
|
+
onPress={() => onFilterChange(filter.id)}
|
|
1600
|
+
/>
|
|
1601
|
+
))}
|
|
1602
|
+
</CaloInline>
|
|
1603
|
+
</CaloStack>
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
function VariantGroup({ title, children }: { title: string; children: React.ReactNode }) {
|
|
1608
|
+
return (
|
|
1609
|
+
<CaloStack gap="size16">
|
|
1610
|
+
<CaloText role="paragraphMediumStrong">{title}</CaloText>
|
|
1611
|
+
{children}
|
|
1612
|
+
</CaloStack>
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
function ButtonSizeRow({ label, children }: { label: string; children: React.ReactNode }) {
|
|
1617
|
+
return (
|
|
1618
|
+
<CaloInline align="center" gap="size20" wrap>
|
|
1619
|
+
<CaloText role="paragraphMediumStrong">{label}</CaloText>
|
|
1620
|
+
<CaloInline align="center" gap="size10" wrap>
|
|
1621
|
+
{children}
|
|
1622
|
+
</CaloInline>
|
|
1623
|
+
</CaloInline>
|
|
1624
|
+
);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1627
|
+
function TypographySpecimen({
|
|
1628
|
+
roleName,
|
|
1629
|
+
role,
|
|
1630
|
+
sample,
|
|
1631
|
+
usage,
|
|
1632
|
+
language = "en"
|
|
1633
|
+
}: {
|
|
1634
|
+
roleName: string;
|
|
1635
|
+
role: React.ComponentProps<typeof CaloText>["role"];
|
|
1636
|
+
sample: string;
|
|
1637
|
+
usage: string;
|
|
1638
|
+
language?: React.ComponentProps<typeof CaloText>["language"];
|
|
1639
|
+
}) {
|
|
1640
|
+
return (
|
|
1641
|
+
<CaloInline align="center" justify="between" gap="size16" wrap>
|
|
1642
|
+
<CaloStack gap="size4">
|
|
1643
|
+
<CaloText role="paragraphMediumStrong">{roleName}</CaloText>
|
|
1644
|
+
<CaloText role="captionSmall" tone="muted">{usage}</CaloText>
|
|
1645
|
+
</CaloStack>
|
|
1646
|
+
<CaloText role={role} language={language}>{sample}</CaloText>
|
|
1647
|
+
</CaloInline>
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
function isHealthySansAvailable() {
|
|
1652
|
+
if (typeof document === "undefined") return false;
|
|
1653
|
+
const canvas = document.createElement("canvas");
|
|
1654
|
+
const context = canvas.getContext("2d");
|
|
1655
|
+
if (!context) return false;
|
|
1656
|
+
|
|
1657
|
+
const text = "وجبة الغداء";
|
|
1658
|
+
const quotedFamily = JSON.stringify("HealthySans-Bold");
|
|
1659
|
+
const measure = (font: string) => {
|
|
1660
|
+
context.font = font;
|
|
1661
|
+
return context.measureText(text).width;
|
|
1662
|
+
};
|
|
1663
|
+
|
|
1664
|
+
const healthySansWidth = measure(`700 24px ${quotedFamily}`);
|
|
1665
|
+
const serifWidth = measure("700 24px serif");
|
|
1666
|
+
const sansWidth = measure("700 24px sans-serif");
|
|
1667
|
+
|
|
1668
|
+
return Math.abs(healthySansWidth - serifWidth) > 1 && Math.abs(healthySansWidth - sansWidth) > 1;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
function SelectionPreview({ label, children }: { label: string; children: React.ReactNode }) {
|
|
1672
|
+
return (
|
|
1673
|
+
<CaloInline align="center" gap="size8">
|
|
1674
|
+
{children}
|
|
1675
|
+
<CaloText role="body">{label}</CaloText>
|
|
1676
|
+
</CaloInline>
|
|
1677
|
+
);
|
|
1678
|
+
}
|