@ttoss/react-billing 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -1,7 +1,80 @@
1
1
  # @ttoss/react-billing
2
2
 
3
- React billing components for ttoss.
3
+ Billing UI components for React apps.
4
+
5
+ Built on top of `@ttoss/ui`.
6
+
7
+ ## Installation
8
+
9
+ ```shell
10
+ pnpm add @ttoss/react-billing
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ This package expects your app to be wrapped with `@ttoss/ui` `ThemeProvider`.
16
+
17
+ ### PlanCard
18
+
19
+ ```tsx
20
+ import { PlanCard } from '@ttoss/react-billing';
21
+
22
+ export const Example = () => (
23
+ <PlanCard
24
+ title="Pro"
25
+ subtitle="Best for teams"
26
+ price={{
27
+ value: 'R$ 99,00',
28
+ interval: 'month',
29
+ description: 'Billed monthly',
30
+ }}
31
+ metadata={[
32
+ { title: 'Seats', value: '5', icon: 'fluent:people-24-regular' },
33
+ {
34
+ title: 'Support',
35
+ value: 'Priority',
36
+ icon: 'fluent:chat-help-24-regular',
37
+ },
38
+ ]}
39
+ features={[{ label: 'Unlimited projects' }, { label: 'SSO' }]}
40
+ buttonProps={{ label: 'Choose plan', variant: 'accent' }}
41
+ />
42
+ );
43
+ ```
44
+
45
+ ### SubscriptionCard
46
+
47
+ ```tsx
48
+ import { SubscriptionCard } from '@ttoss/react-billing';
49
+
50
+ export const Example = () => (
51
+ <SubscriptionCard
52
+ planName="Pro"
53
+ price={{ value: 'R$ 99,00', interval: 'month' }}
54
+ status={{ status: 'active', interval: 'Monthly' }}
55
+ features={[{ label: 'Unlimited projects' }, { label: 'SSO' }]}
56
+ actions={[{ label: 'Manage', onClick: () => {}, variant: 'accent' }]}
57
+ metrics={[
58
+ {
59
+ type: 'date',
60
+ label: 'Renews on',
61
+ date: '2025-01-15',
62
+ icon: 'fluent:calendar-24-regular',
63
+ },
64
+ {
65
+ type: 'percentage',
66
+ label: 'Usage',
67
+ current: 75,
68
+ max: 100,
69
+ icon: 'fluent:data-usage-24-regular',
70
+ },
71
+ ]}
72
+ />
73
+ );
74
+ ```
4
75
 
5
76
  ## Exports
6
77
 
7
78
  - `PlanCard`
79
+ - `SubscriptionCard`
80
+ - Types: `PlanCardProps`, `PlanCardVariant`, `PlanCardPrice`, `PlanCardMetadata`, `PlanCardButtonProps`, `PlanCardMetadataSlotService` (and related slot types)
package/dist/esm/index.js CHANGED
@@ -6,10 +6,10 @@ var __name = (target, value) => __defProp(target, "name", {
6
6
  configurable: true
7
7
  });
8
8
 
9
- // src/components/PlanCard.tsx
9
+ // src/components/planCard/PlanCard.tsx
10
10
  import { Box as Box4, Card } from "@ttoss/ui";
11
11
 
12
- // src/components/PlanCardCtaSlot.tsx
12
+ // src/components/planCard/PlanCardCtaSlot.tsx
13
13
  import { Button, Flex } from "@ttoss/ui";
14
14
  var PlanCardCtaSlot = /* @__PURE__ */__name(({
15
15
  buttonProps
@@ -18,11 +18,12 @@ var PlanCardCtaSlot = /* @__PURE__ */__name(({
18
18
  label: ctaLabel = "Assine agora",
19
19
  sx: buttonSx,
20
20
  leftIcon,
21
- variant,
21
+ variant: buttonVariant,
22
22
  ...restButtonProps
23
23
  } = buttonProps ?? {};
24
24
  return /* @__PURE__ */React.createElement(Flex, {
25
25
  sx: {
26
+ marginTop: "auto",
26
27
  paddingY: "2",
27
28
  paddingX: "6",
28
29
  width: "full",
@@ -30,7 +31,7 @@ var PlanCardCtaSlot = /* @__PURE__ */__name(({
30
31
  }
31
32
  }, /* @__PURE__ */React.createElement(Button, {
32
33
  ...restButtonProps,
33
- variant: variant ?? "accent",
34
+ variant: buttonVariant ?? "accent",
34
35
  leftIcon,
35
36
  sx: {
36
37
  width: "full",
@@ -41,17 +42,48 @@ var PlanCardCtaSlot = /* @__PURE__ */__name(({
41
42
  }, ctaLabel));
42
43
  }, "PlanCardCtaSlot");
43
44
 
44
- // src/components/PlanCardFeaturesSlot.tsx
45
+ // src/components/planCard/PlanCardFeaturesSlot.tsx
45
46
  import { Icon } from "@ttoss/react-icons";
46
47
  import { Box, Flex as Flex2, Stack, Text } from "@ttoss/ui";
47
48
  import * as React2 from "react";
49
+
50
+ // src/components/planCard/PlanCardVariants.ts
51
+ var normalizeVariant = /* @__PURE__ */__name(variantType => {
52
+ if (variantType === "default") return "primary";
53
+ if (variantType === "enterprise") return "secondary";
54
+ return variantType ?? "primary";
55
+ }, "normalizeVariant");
56
+ var getPlanCardVariantStyles = /* @__PURE__ */__name(variantType => {
57
+ const variant = normalizeVariant(variantType);
58
+ const variants = {
59
+ primary: {
60
+ // Primary is the default PlanCard styling (previously `muted`).
61
+ backgroundColor: "display.background.primary.default",
62
+ color: "display.text.primary.default",
63
+ secondaryColor: "display.text.secondary.default",
64
+ borderColor: "display.border.muted.default",
65
+ positiveColor: "feedback.text.positive.default"
66
+ },
67
+ secondary: {
68
+ // Secondary matches the old `enterprise` look.
69
+ backgroundColor: "display.background.primary.active",
70
+ color: "action.text.primary.default",
71
+ secondaryColor: "action.text.primary.default",
72
+ borderColor: "display.border.muted.default",
73
+ positiveColor: "action.text.primary.default"
74
+ }
75
+ };
76
+ return variants[variant] ?? variants.primary;
77
+ }, "getPlanCardVariantStyles");
78
+
79
+ // src/components/planCard/PlanCardFeaturesSlot.tsx
48
80
  var featuresTitle = "RECURSOS";
49
81
  var PlanCardFeaturesSlot = /* @__PURE__ */__name(({
50
82
  features,
51
- variant = "default"
83
+ variant = "primary"
52
84
  }) => {
53
- const isEnterprise = variant === "enterprise";
54
- const featureColor = isEnterprise ? "white" : "feedback.text.positive.default";
85
+ const variantStyles = getPlanCardVariantStyles(variant);
86
+ const featurePositiveColor = variantStyles.positiveColor;
55
87
  return /* @__PURE__ */React2.createElement(Box, {
56
88
  sx: {
57
89
  paddingY: "4",
@@ -69,7 +101,7 @@ var PlanCardFeaturesSlot = /* @__PURE__ */__name(({
69
101
  }, /* @__PURE__ */React2.createElement(Flex2, {
70
102
  sx: {
71
103
  letterSpacing: "widest",
72
- color: isEnterprise ? "white" : "text"
104
+ color: variantStyles.color
73
105
  }
74
106
  }, featuresTitle), /* @__PURE__ */React2.createElement(Flex2, {
75
107
  sx: {
@@ -86,7 +118,7 @@ var PlanCardFeaturesSlot = /* @__PURE__ */__name(({
86
118
  key: index,
87
119
  sx: {
88
120
  fontSize: "sm",
89
- color: featureColor,
121
+ color: featurePositiveColor,
90
122
  alignItems: "center",
91
123
  gap: "3"
92
124
  }
@@ -95,22 +127,22 @@ var PlanCardFeaturesSlot = /* @__PURE__ */__name(({
95
127
  }), /* @__PURE__ */React2.createElement(Text, {
96
128
  sx: {
97
129
  fontSize: "sm",
98
- color: featureColor,
130
+ color: featurePositiveColor,
99
131
  alignItems: "center"
100
132
  }
101
133
  }, String(feature)));
102
134
  }))));
103
135
  }, "PlanCardFeaturesSlot");
104
136
 
105
- // src/components/PlanCardHeaderSlot.tsx
137
+ // src/components/planCard/PlanCardHeaderSlot.tsx
106
138
  import { Box as Box2, Flex as Flex3, Heading, Text as Text2 } from "@ttoss/ui";
107
139
  var PlanCardHeaderSlot = /* @__PURE__ */__name(({
108
140
  title,
109
141
  subtitle,
110
142
  hasTopTag,
111
- variant = "default"
143
+ variant = "primary"
112
144
  }) => {
113
- const isEnterprise = variant === "enterprise";
145
+ const variantStyles = getPlanCardVariantStyles(variant);
114
146
  return /* @__PURE__ */React.createElement(Box2, {
115
147
  sx: {
116
148
  paddingY: hasTopTag ? "4" : "8",
@@ -127,24 +159,24 @@ var PlanCardHeaderSlot = /* @__PURE__ */__name(({
127
159
  }, /* @__PURE__ */React.createElement(Heading, {
128
160
  sx: {
129
161
  fontSize: "3xl",
130
- color: isEnterprise ? "white" : "text"
162
+ color: variantStyles.color
131
163
  }
132
164
  }, title), subtitle && /* @__PURE__ */React.createElement(Text2, {
133
165
  sx: {
134
166
  fontSize: "sm",
135
- color: isEnterprise ? "white" : "display.text.secondary.default"
167
+ color: variantStyles.secondaryColor
136
168
  }
137
169
  }, subtitle)));
138
170
  }, "PlanCardHeaderSlot");
139
171
 
140
- // src/components/PlanCardMetadataSlot.tsx
172
+ // src/components/planCard/PlanCardMetadataSlot.tsx
141
173
  import { Icon as Icon2 } from "@ttoss/react-icons";
142
174
  import { Flex as Flex4, Stack as Stack2, Text as Text3 } from "@ttoss/ui";
143
175
  var PlanCardMetadataSlot = /* @__PURE__ */__name(({
144
176
  metadata,
145
- variant = "default"
177
+ variant = "primary"
146
178
  }) => {
147
- const isEnterprise = variant === "enterprise";
179
+ const variantStyles = getPlanCardVariantStyles(variant);
148
180
  return /* @__PURE__ */React.createElement(Stack2, {
149
181
  sx: {
150
182
  width: "full",
@@ -163,14 +195,14 @@ var PlanCardMetadataSlot = /* @__PURE__ */__name(({
163
195
  sx: {
164
196
  gap: "4",
165
197
  alignItems: "center",
166
- color: isEnterprise ? "white" : "display.text.secondary.default",
198
+ color: variantStyles.secondaryColor,
167
199
  width: "full"
168
200
  }
169
201
  }, service.icon && /* @__PURE__ */React.createElement(Icon2, {
170
202
  icon: service.icon
171
203
  }), /* @__PURE__ */React.createElement(Text3, {
172
204
  sx: {
173
- color: isEnterprise ? "white" : "display.text.secondary.default"
205
+ color: variantStyles.secondaryColor
174
206
  }
175
207
  }, service.label + String.fromCharCode(58))), service.parameters.map(parameter => {
176
208
  return /* @__PURE__ */React.createElement(Flex4, {
@@ -205,13 +237,13 @@ var PlanCardMetadataSlot = /* @__PURE__ */__name(({
205
237
  }));
206
238
  }, "PlanCardMetadataSlot");
207
239
 
208
- // src/components/PlanCardPriceSlot.tsx
240
+ // src/components/planCard/PlanCardPriceSlot.tsx
209
241
  import { Flex as Flex5, Text as Text4 } from "@ttoss/ui";
210
242
  var PlanCardPriceSlot = /* @__PURE__ */__name(({
211
243
  price,
212
- variant = "default"
244
+ variant = "primary"
213
245
  }) => {
214
- const isEnterprise = variant === "enterprise";
246
+ const variantStyles = getPlanCardVariantStyles(variant);
215
247
  return /* @__PURE__ */React.createElement(Flex5, {
216
248
  sx: {
217
249
  paddingY: "6",
@@ -231,22 +263,22 @@ var PlanCardPriceSlot = /* @__PURE__ */__name(({
231
263
  sx: {
232
264
  fontSize: "4xl",
233
265
  fontWeight: "bold",
234
- color: isEnterprise ? "white" : "display.text.primary.default"
266
+ color: variantStyles.color
235
267
  }
236
268
  }, price.value), /* @__PURE__ */React.createElement(Text4, {
237
269
  sx: {
238
270
  fontSize: "sm",
239
- color: isEnterprise ? "white" : "display.text.secondary.default"
271
+ color: variantStyles.secondaryColor
240
272
  }
241
273
  }, price.interval)), price.description && /* @__PURE__ */React.createElement(Text4, {
242
274
  sx: {
243
275
  fontSize: "md",
244
- color: isEnterprise ? "white" : "display.text.secondary.default"
276
+ color: variantStyles.secondaryColor
245
277
  }
246
278
  }, price.description));
247
279
  }, "PlanCardPriceSlot");
248
280
 
249
- // src/components/PlanCardTopTagSlot.tsx
281
+ // src/components/planCard/PlanCardTopTagSlot.tsx
250
282
  import { Box as Box3 } from "@ttoss/ui";
251
283
  var PlanCardTopTagSlot = /* @__PURE__ */__name(({
252
284
  children
@@ -258,10 +290,11 @@ var PlanCardTopTagSlot = /* @__PURE__ */__name(({
258
290
  }, children);
259
291
  }, "PlanCardTopTagSlot");
260
292
 
261
- // src/components/PlanCard.tsx
293
+ // src/components/planCard/PlanCard.tsx
262
294
  var PlanCard = /* @__PURE__ */__name(props => {
263
295
  const {
264
- variant = "default",
296
+ variant = "primary",
297
+ topTag,
265
298
  title,
266
299
  subtitle,
267
300
  metadata = [],
@@ -272,20 +305,24 @@ var PlanCard = /* @__PURE__ */__name(props => {
272
305
  ...cardProps
273
306
  } = props;
274
307
  const effectiveMetadataVariant = metadataVariant ?? variant;
308
+ const variantStyles = getPlanCardVariantStyles(variant);
275
309
  return /* @__PURE__ */React.createElement(Card, {
276
310
  ...cardProps,
277
311
  sx: {
312
+ display: "flex",
313
+ flexDirection: "column",
278
314
  width: "full",
279
315
  maxWidth: "410px",
280
- backgroundColor: variant === "enterprise" ? "display.background.primary.active" : "display.background.primary.default",
316
+ backgroundColor: variantStyles.backgroundColor,
317
+ borderColor: variantStyles.borderColor,
281
318
  ...cardProps.sx
282
319
  }
283
- }, props.topTag && /* @__PURE__ */React.createElement(PlanCardTopTagSlot, {
320
+ }, topTag && /* @__PURE__ */React.createElement(PlanCardTopTagSlot, {
284
321
  variant
285
- }, props.topTag), /* @__PURE__ */React.createElement(PlanCardHeaderSlot, {
322
+ }, topTag), /* @__PURE__ */React.createElement(PlanCardHeaderSlot, {
286
323
  title,
287
324
  subtitle,
288
- hasTopTag: Boolean(props.topTag),
325
+ hasTopTag: Boolean(topTag),
289
326
  variant
290
327
  }), metadata.length > 0 && /* @__PURE__ */React.createElement(Box4, {
291
328
  sx: {
@@ -307,4 +344,260 @@ var PlanCard = /* @__PURE__ */__name(props => {
307
344
  variant
308
345
  }));
309
346
  }, "PlanCard");
310
- export { PlanCard };
347
+
348
+ // src/components/subscriptionPanel/SubscriptionPanel.tsx
349
+ import { EnhancedTitle } from "@ttoss/components/EnhancedTitle";
350
+ import { MetricCard } from "@ttoss/components/MetricCard";
351
+ import { Card as Card2, Flex as Flex7, Spinner } from "@ttoss/ui";
352
+
353
+ // src/components/subscriptionPanel/SubscriptionPanel.styles.ts
354
+ import { keyframes } from "@ttoss/ui";
355
+ var gradientFlow = keyframes({
356
+ "0%": {
357
+ backgroundPosition: "0% 50%"
358
+ },
359
+ "50%": {
360
+ backgroundPosition: "100% 50%"
361
+ },
362
+ "100%": {
363
+ backgroundPosition: "0% 50%"
364
+ }
365
+ });
366
+ var getAccentGradientBackground = /* @__PURE__ */__name(t => {
367
+ const theme = t;
368
+ const start = theme.colors?.action?.background?.accent?.default || theme.colors?.input?.background?.accent?.default;
369
+ if (!start) return void 0;
370
+ const middle = theme.colors?.action?.background?.accent?.active || theme.colors?.input?.background?.accent?.active || start;
371
+ return `linear-gradient(270deg, ${start}, ${middle}, ${start})`;
372
+ }, "getAccentGradientBackground");
373
+ var getPrimaryGradientBackground = /* @__PURE__ */__name(t => {
374
+ const theme = t;
375
+ const start = theme.colors?.action?.background?.primary?.default;
376
+ if (!start) return void 0;
377
+ const middle = theme.colors?.action?.background?.secondary?.default || start;
378
+ return `linear-gradient(270deg, ${start}, ${middle}, ${start})`;
379
+ }, "getPrimaryGradientBackground");
380
+ var getVariantStyles = /* @__PURE__ */__name(variantType => {
381
+ const variants = {
382
+ "spotlight-accent": {
383
+ backgroundColor: "input.background.accent.default",
384
+ color: "action.text.accent.default",
385
+ borderColor: "display.border.muted.default",
386
+ gradientBackground: getAccentGradientBackground
387
+ },
388
+ "spotlight-primary": {
389
+ backgroundColor: "action.background.primary.default",
390
+ color: "display.text.accent.default",
391
+ borderColor: "display.border.muted.default",
392
+ gradientBackground: getPrimaryGradientBackground
393
+ },
394
+ primary: {
395
+ backgroundColor: "action.background.primary.default",
396
+ color: "action.text.primary.default",
397
+ borderColor: "display.border.muted.default",
398
+ gradientBackground: void 0
399
+ },
400
+ secondary: {
401
+ backgroundColor: "action.background.secondary.default",
402
+ color: "action.text.primary.default",
403
+ borderColor: "display.border.muted.default",
404
+ gradientBackground: void 0
405
+ },
406
+ accent: {
407
+ backgroundColor: "action.background.accent.default",
408
+ color: "action.text.accent.default",
409
+ borderColor: "display.border.muted.default",
410
+ gradientBackground: void 0
411
+ }
412
+ };
413
+ return variants[variantType] || variants["spotlight-accent"];
414
+ }, "getVariantStyles");
415
+ var getSubscriptionPanelAccentBarSx = /* @__PURE__ */__name((variant = "spotlight-accent") => {
416
+ const variantStyles = getVariantStyles(variant);
417
+ const isSpotlight = variant.startsWith("spotlight-");
418
+ const gradientBg = variant === "spotlight-accent" ? getAccentGradientBackground : getPrimaryGradientBackground;
419
+ return {
420
+ height: "12px",
421
+ width: "full",
422
+ borderTopLeftRadius: "lg",
423
+ borderTopRightRadius: "lg",
424
+ backgroundColor: variantStyles.backgroundColor,
425
+ color: variantStyles.color,
426
+ borderColor: variantStyles.borderColor,
427
+ ...(isSpotlight ? {
428
+ background: gradientBg,
429
+ backgroundSize: "400% 400%",
430
+ animation: `${gradientFlow} 6s ease infinite`
431
+ } : {})
432
+ };
433
+ }, "getSubscriptionPanelAccentBarSx");
434
+ var SubscriptionPanelAccentBarSx = getSubscriptionPanelAccentBarSx("spotlight-accent");
435
+
436
+ // src/components/subscriptionPanel/SubscriptionPanelActionsSlot.tsx
437
+ import { Icon as Icon3 } from "@ttoss/react-icons";
438
+ import { Button as Button2, Flex as Flex6 } from "@ttoss/ui";
439
+ var SubscriptionPanelActionsSlot = /* @__PURE__ */__name(({
440
+ actions
441
+ }) => {
442
+ if (actions.length === 0) {
443
+ return null;
444
+ }
445
+ return /* @__PURE__ */React.createElement(Flex6, {
446
+ sx: {
447
+ gap: "3",
448
+ flexDirection: ["column", "row"],
449
+ flexShrink: 0,
450
+ paddingX: "6",
451
+ paddingBottom: "6"
452
+ }
453
+ }, actions.map((action, index) => {
454
+ const {
455
+ label,
456
+ onClick,
457
+ leftIcon,
458
+ isLoading,
459
+ variant = "secondary",
460
+ disabled,
461
+ ...buttonProps
462
+ } = action;
463
+ return /* @__PURE__ */React.createElement(Button2, {
464
+ key: index,
465
+ onClick,
466
+ variant,
467
+ disabled: disabled || isLoading,
468
+ leftIcon: isLoading ? void 0 : leftIcon,
469
+ sx: {
470
+ gap: "2",
471
+ justifyContent: "center",
472
+ minWidth: "fit-content"
473
+ },
474
+ ...buttonProps
475
+ }, isLoading ? /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(Icon3, {
476
+ icon: "fluent:spinner-ios-20-regular"
477
+ }), "Processando...") : label);
478
+ }));
479
+ }, "SubscriptionPanelActionsSlot");
480
+
481
+ // src/components/subscriptionPanel/SubscriptionPanel.tsx
482
+ var renderMetricCard = /* @__PURE__ */__name(params => {
483
+ const {
484
+ metric,
485
+ index,
486
+ isLoading
487
+ } = params;
488
+ return /* @__PURE__ */React.createElement(MetricCard, {
489
+ key: index,
490
+ metric,
491
+ isLoading
492
+ });
493
+ }, "renderMetricCard");
494
+ var SubscriptionPanel = /* @__PURE__ */__name(({
495
+ variant = "accent",
496
+ icon,
497
+ planName,
498
+ price,
499
+ status,
500
+ features = [],
501
+ actions = [],
502
+ metrics = [],
503
+ isLoading = false
504
+ }) => {
505
+ if (isLoading) {
506
+ return /* @__PURE__ */React.createElement(Card2, {
507
+ sx: {
508
+ width: "full",
509
+ height: "400px",
510
+ alignItems: "center",
511
+ justifyContent: "center"
512
+ }
513
+ }, /* @__PURE__ */React.createElement(Spinner, null));
514
+ }
515
+ return /* @__PURE__ */React.createElement(Card2, {
516
+ sx: {
517
+ width: "full"
518
+ }
519
+ }, /* @__PURE__ */React.createElement(Flex7, {
520
+ sx: getSubscriptionPanelAccentBarSx(variant)
521
+ }), /* @__PURE__ */React.createElement(Flex7, {
522
+ sx: {
523
+ width: "full",
524
+ paddingX: "6",
525
+ paddingY: "6",
526
+ flexDirection: "column"
527
+ }
528
+ }, /* @__PURE__ */React.createElement(Flex7, {
529
+ sx: {
530
+ paddingY: "10",
531
+ paddingX: "3",
532
+ width: "full",
533
+ flexDirection: ["column", "column", "row"],
534
+ alignItems: ["stretch", "stretch", "center"],
535
+ justifyContent: "space-between",
536
+ gap: "6",
537
+ borderBottom: "sm",
538
+ borderBottomColor: "display.border.muted.default"
539
+ }
540
+ }, /* @__PURE__ */React.createElement(EnhancedTitle, {
541
+ variant,
542
+ icon,
543
+ title: planName,
544
+ frontTitle: price.interval ? `${price.value}/${price.interval}` : price.value,
545
+ topBadges: [
546
+ // Status badge
547
+ ...(status.status === "active" ? [{
548
+ label: "Ativo",
549
+ variant: "positive",
550
+ icon: "fluent:checkmark-circle-24-filled"
551
+ }] : status.status === "inactive" ? [{
552
+ label: "Inativo",
553
+ variant: "muted",
554
+ icon: "fluent:dismiss-circle-24-filled"
555
+ }] : [{
556
+ label: "Cancelado",
557
+ variant: "negative",
558
+ icon: "fluent:error-circle-24-filled"
559
+ }]),
560
+ // Interval badge
561
+ ...(status.interval ? [{
562
+ label: status.interval,
563
+ variant: "informative"
564
+ }] : []),
565
+ // Scheduled update badge
566
+ ...(status.hasScheduledUpdate ? [{
567
+ label: "Altera\xE7\xE3o Agendada",
568
+ variant: "informative",
569
+ icon: "fluent:clock-24-regular"
570
+ }] : []),
571
+ // Cancellation badge
572
+ ...(status.hasCancellation ? [{
573
+ label: "Renova\xE7\xE3o Cancelada",
574
+ variant: "negative",
575
+ icon: "fluent:dismiss-circle-24-regular"
576
+ }] : [])],
577
+ bottomBadges: features.map(feature => {
578
+ return {
579
+ label: feature.label,
580
+ icon: typeof feature.icon === "string" ? feature.icon : "fluent:checkmark-24-filled",
581
+ variant: "muted"
582
+ };
583
+ })
584
+ }), actions.length > 0 && /* @__PURE__ */React.createElement(SubscriptionPanelActionsSlot, {
585
+ actions
586
+ })), metrics.length > 0 && /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(Flex7, {
587
+ sx: {
588
+ paddingY: "10",
589
+ paddingX: "3",
590
+ width: "full",
591
+ flexWrap: "wrap",
592
+ gap: "10",
593
+ justifyContent: "center"
594
+ }
595
+ }, metrics.map((metric, index) => {
596
+ return renderMetricCard({
597
+ metric,
598
+ index,
599
+ isLoading
600
+ });
601
+ })))));
602
+ }, "SubscriptionPanel");
603
+ export { PlanCard, SubscriptionPanel };
package/dist/index.d.ts CHANGED
@@ -3,6 +3,17 @@ import { IconType } from '@ttoss/react-icons';
3
3
  import { CardProps, ButtonProps } from '@ttoss/ui';
4
4
  import * as React from 'react';
5
5
 
6
+ type PlanCardVariantType = 'primary' | 'secondary'
7
+ /**
8
+ * @deprecated Use `primary`.
9
+ */
10
+ | 'default'
11
+ /**
12
+ * @deprecated Use `secondary`.
13
+ */
14
+ | 'enterprise';
15
+ type PlanCardVariant = PlanCardVariantType;
16
+
6
17
  type PlanCardMetadataSlotParameter = {
7
18
  name: string;
8
19
  value: React.ReactNode;
@@ -14,14 +25,12 @@ type PlanCardMetadataSlotService = {
14
25
  id?: string;
15
26
  key?: string;
16
27
  };
17
- type PlanCardMetadataSlotVariant = 'default' | 'enterprise';
28
+ type PlanCardMetadataSlotVariant = PlanCardVariantType;
18
29
  interface PlanCardMetadataSlotProps {
19
30
  metadata: PlanCardMetadataSlotService[];
20
31
  variant?: PlanCardMetadataSlotVariant;
21
32
  }
22
33
 
23
- type PlanCardVariant = 'default' | 'enterprise';
24
-
25
34
  type PlanCardMetadata = PlanCardMetadataSlotService[];
26
35
  type PlanCardPrice = {
27
36
  value: string | number;
@@ -45,4 +54,254 @@ interface PlanCardProps extends Omit<CardProps, 'children'> {
45
54
  }
46
55
  declare const PlanCard: (props: PlanCardProps) => react_jsx_runtime.JSX.Element;
47
56
 
48
- export { PlanCard, type PlanCardButtonProps, type PlanCardMetadata, type PlanCardMetadataSlotParameter, type PlanCardMetadataSlotProps, type PlanCardMetadataSlotService, type PlanCardMetadataSlotVariant, type PlanCardPrice, type PlanCardProps, type PlanCardVariant };
57
+ type SubscriptionPanelVariant = 'spotlight-accent' | 'spotlight-primary' | 'primary' | 'secondary' | 'accent';
58
+
59
+ /**
60
+ * Subscription status indicating the current state of the subscription.
61
+ */
62
+ type SubscriptionStatus = 'active' | 'inactive' | 'cancelled';
63
+ /**
64
+ * Base metric properties shared by all metric types.
65
+ */
66
+ interface BaseMetric {
67
+ /**
68
+ * Label displayed above the metric value.
69
+ */
70
+ label: string;
71
+ /**
72
+ * Optional tooltip text or action handler for additional context.
73
+ * When a string is provided, it displays a simple tooltip.
74
+ * When a function is provided, it's called when the tooltip icon is clicked (e.g., to open help articles).
75
+ */
76
+ tooltip?: string | (() => void);
77
+ /**
78
+ * Icon to display alongside the metric.
79
+ */
80
+ icon?: IconType;
81
+ /**
82
+ * Optional click handler to make the metric card interactive.
83
+ */
84
+ onClick?: () => void;
85
+ /**
86
+ * Optional help article action handler.
87
+ */
88
+ helpArticleAction?: () => void;
89
+ }
90
+ /**
91
+ * Date-based metric for displaying dates like expiration or renewal.
92
+ */
93
+ interface DateMetric extends BaseMetric {
94
+ type: 'date';
95
+ /**
96
+ * The date value to display.
97
+ */
98
+ date: string;
99
+ /**
100
+ * Optional message showing remaining days.
101
+ */
102
+ remainingDaysMessage?: string;
103
+ /**
104
+ * Whether to show a warning indicator.
105
+ */
106
+ isWarning?: boolean;
107
+ }
108
+ /**
109
+ * Percentage-based metric with progress bar.
110
+ */
111
+ interface PercentageMetric extends BaseMetric {
112
+ type: 'percentage';
113
+ /**
114
+ * Current value.
115
+ */
116
+ current: number;
117
+ /**
118
+ * Maximum value. Use null for unlimited.
119
+ */
120
+ max: number | null;
121
+ /**
122
+ * Custom formatter for displaying values.
123
+ */
124
+ formatValue?: (value: number) => string;
125
+ /**
126
+ * Percentage threshold at which to show an alert.
127
+ */
128
+ showAlertThreshold?: number;
129
+ }
130
+ /**
131
+ * Number-based metric for displaying counts.
132
+ */
133
+ interface NumberMetric extends BaseMetric {
134
+ type: 'number';
135
+ /**
136
+ * Current value.
137
+ */
138
+ current: number;
139
+ /**
140
+ * Maximum value. Use null for unlimited.
141
+ */
142
+ max: number | null;
143
+ /**
144
+ * Custom formatter for displaying values.
145
+ */
146
+ formatValue?: (value: number) => string;
147
+ /**
148
+ * Optional footer text below the metric.
149
+ */
150
+ footerText?: string;
151
+ }
152
+ /**
153
+ * Union type for all metric types.
154
+ */
155
+ type MetricType = DateMetric | PercentageMetric | NumberMetric;
156
+ /**
157
+ * Status badge configuration.
158
+ */
159
+ interface SubscriptionPanelStatusBadgeProps {
160
+ /**
161
+ * The subscription status.
162
+ */
163
+ status: SubscriptionStatus;
164
+ /**
165
+ * Billing interval (e.g., "Mensal", "Anual").
166
+ */
167
+ interval?: string;
168
+ /**
169
+ * Whether the subscription has a pending cancellation.
170
+ */
171
+ hasCancellation?: boolean;
172
+ /**
173
+ * Whether the subscription has a scheduled update.
174
+ */
175
+ hasScheduledUpdate?: boolean;
176
+ }
177
+ /**
178
+ * Feature tag displayed in the card header.
179
+ */
180
+ interface SubscriptionPanelFeatureTag {
181
+ /**
182
+ * Label text for the feature.
183
+ */
184
+ label: string;
185
+ /**
186
+ * Optional icon for the feature.
187
+ */
188
+ icon?: IconType;
189
+ }
190
+ /**
191
+ * Action button configuration.
192
+ */
193
+ interface SubscriptionPanelAction extends Omit<ButtonProps, 'children'> {
194
+ /**
195
+ * Button label text.
196
+ */
197
+ label: string;
198
+ /**
199
+ * Click handler for the action.
200
+ */
201
+ onClick: () => void;
202
+ /**
203
+ * Optional icon for the button.
204
+ */
205
+ leftIcon?: IconType;
206
+ /**
207
+ * Whether the action is currently loading.
208
+ */
209
+ isLoading?: boolean;
210
+ }
211
+ /**
212
+ * Price configuration.
213
+ */
214
+ interface SubscriptionPanelPrice {
215
+ /**
216
+ * Price value (e.g., "R$ 5,00").
217
+ */
218
+ value: string;
219
+ /**
220
+ * Price interval (e.g., "mês", "ano").
221
+ */
222
+ interval?: string;
223
+ }
224
+ /**
225
+ * Main SubscriptionPanel props.
226
+ */
227
+ interface SubscriptionPanelProps {
228
+ /**
229
+ * Visual variant for the accent bar and header icon.
230
+ * @default 'spotlight'
231
+ */
232
+ variant?: SubscriptionPanelVariant;
233
+ /**
234
+ * Plan icon to display. Can be a ReactNode or an IconType string.
235
+ */
236
+ icon?: string;
237
+ /**
238
+ * Name of the subscription plan.
239
+ */
240
+ planName: string;
241
+ /**
242
+ * Price configuration.
243
+ */
244
+ price: SubscriptionPanelPrice;
245
+ /**
246
+ * Status badge configuration.
247
+ */
248
+ status: SubscriptionPanelStatusBadgeProps;
249
+ /**
250
+ * Feature tags to display.
251
+ */
252
+ features?: SubscriptionPanelFeatureTag[];
253
+ /**
254
+ * Action buttons.
255
+ */
256
+ actions?: SubscriptionPanelAction[];
257
+ /**
258
+ * Metrics to display in the card.
259
+ */
260
+ metrics?: MetricType[];
261
+ /**
262
+ * Whether the card is in a loading state.
263
+ */
264
+ isLoading?: boolean;
265
+ }
266
+
267
+ /**
268
+ * SubscriptionPanel displays comprehensive subscription information including
269
+ * plan details, status, actions, and various metrics.
270
+ *
271
+ * It supports three types of metrics:
272
+ * - **Date**: For displaying dates like expiration or renewal
273
+ * - **Percentage**: For usage-based metrics with progress bars
274
+ * - **Number**: For count-based metrics
275
+ *
276
+ * @example
277
+ * ```tsx
278
+ * <SubscriptionPanel
279
+ * planName="Premium Plan"
280
+ * price={{ value: "R$ 99,00", interval: "mês" }}
281
+ * status={{ status: "active", interval: "Mensal" }}
282
+ * features={[{ label: "Feature 1" }, { label: "Feature 2" }]}
283
+ * actions={[
284
+ * { label: "Upgrade", onClick: () => {}, variant: "accent" },
285
+ * { label: "Cancel", onClick: () => {}, variant: "danger" },
286
+ * ]}
287
+ * metrics={[
288
+ * { type: "date", label: "Expira em", date: "15/01/2025", icon: "fluent:calendar-24-regular" },
289
+ * { type: "percentage", label: "Uso", current: 75, max: 100, icon: "fluent:data-usage-24-regular" },
290
+ * ]}
291
+ * />
292
+ * ```
293
+ *
294
+ * @example
295
+ * ```tsx
296
+ * // Compact mode for smaller spaces
297
+ * <SubscriptionPanel
298
+ * planName="Basic Plan"
299
+ * price={{ value: "R$ 29,00", interval: "mês" }}
300
+ * status={{ status: "active" }}
301
+ * compact
302
+ * />
303
+ * ```
304
+ */
305
+ declare const SubscriptionPanel: ({ variant, icon, planName, price, status, features, actions, metrics, isLoading, }: SubscriptionPanelProps) => react_jsx_runtime.JSX.Element;
306
+
307
+ export { PlanCard, type PlanCardButtonProps, type PlanCardMetadata, type PlanCardMetadataSlotParameter, type PlanCardMetadataSlotProps, type PlanCardMetadataSlotService, type PlanCardMetadataSlotVariant, type PlanCardPrice, type PlanCardProps, type PlanCardVariant, SubscriptionPanel };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/react-billing",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Billing UI components for React apps.",
5
5
  "author": "Giovane Guimarães",
6
6
  "repository": {
@@ -20,6 +20,7 @@
20
20
  ],
21
21
  "sideEffects": false,
22
22
  "dependencies": {
23
+ "@ttoss/components": "^2.11.5",
23
24
  "@ttoss/ui": "^6.3.2",
24
25
  "@ttoss/react-icons": "^0.5.6"
25
26
  },
package/dist/index.d.cts DELETED
@@ -1,48 +0,0 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { IconType } from '@ttoss/react-icons';
3
- import { CardProps, ButtonProps } from '@ttoss/ui';
4
- import * as React from 'react';
5
-
6
- type PlanCardMetadataSlotParameter = {
7
- name: string;
8
- value: React.ReactNode;
9
- };
10
- type PlanCardMetadataSlotService = {
11
- label: string;
12
- icon?: IconType;
13
- parameters: PlanCardMetadataSlotParameter[];
14
- id?: string;
15
- key?: string;
16
- };
17
- type PlanCardMetadataSlotVariant = 'default' | 'enterprise';
18
- interface PlanCardMetadataSlotProps {
19
- metadata: PlanCardMetadataSlotService[];
20
- variant?: PlanCardMetadataSlotVariant;
21
- }
22
-
23
- type PlanCardVariant = 'default' | 'enterprise';
24
-
25
- type PlanCardMetadata = PlanCardMetadataSlotService[];
26
- type PlanCardPrice = {
27
- value: string | number;
28
- interval: string;
29
- description?: string;
30
- };
31
- type PlanCardButtonProps = Omit<ButtonProps, 'children'> & {
32
- label?: string;
33
- leftIcon?: IconType;
34
- };
35
- interface PlanCardProps extends Omit<CardProps, 'children'> {
36
- variant?: PlanCardVariant;
37
- topTag?: React.ReactNode;
38
- title: string;
39
- subtitle?: string;
40
- metadata?: PlanCardMetadata;
41
- metadataVariant?: PlanCardMetadataSlotVariant;
42
- price: PlanCardPrice;
43
- features?: unknown[];
44
- buttonProps?: PlanCardButtonProps;
45
- }
46
- declare const PlanCard: (props: PlanCardProps) => react_jsx_runtime.JSX.Element;
47
-
48
- export { PlanCard, type PlanCardButtonProps, type PlanCardMetadata, type PlanCardMetadataSlotParameter, type PlanCardMetadataSlotProps, type PlanCardMetadataSlotService, type PlanCardMetadataSlotVariant, type PlanCardPrice, type PlanCardProps, type PlanCardVariant };
package/dist/index.js DELETED
@@ -1,354 +0,0 @@
1
- /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import * as React from 'react';
3
- "use strict";
4
-
5
- var __create = Object.create;
6
- var __defProp = Object.defineProperty;
7
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
8
- var __getOwnPropNames = Object.getOwnPropertyNames;
9
- var __getProtoOf = Object.getPrototypeOf;
10
- var __hasOwnProp = Object.prototype.hasOwnProperty;
11
- var __name = (target, value) => __defProp(target, "name", {
12
- value,
13
- configurable: true
14
- });
15
- var __export = (target, all) => {
16
- for (var name in all) __defProp(target, name, {
17
- get: all[name],
18
- enumerable: true
19
- });
20
- };
21
- var __copyProps = (to, from, except, desc) => {
22
- if (from && typeof from === "object" || typeof from === "function") {
23
- for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
24
- get: () => from[key],
25
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
26
- });
27
- }
28
- return to;
29
- };
30
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
31
- // If the importer is in node compatibility mode or this is not an ESM
32
- // file that has been converted to a CommonJS file using a Babel-
33
- // compatible transform (i.e. "__esModule" has not been set), then set
34
- // "default" to the CommonJS "module.exports" for node compatibility.
35
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
36
- value: mod,
37
- enumerable: true
38
- }) : target, mod));
39
- var __toCommonJS = mod => __copyProps(__defProp({}, "__esModule", {
40
- value: true
41
- }), mod);
42
-
43
- // src/index.ts
44
- var index_exports = {};
45
- __export(index_exports, {
46
- PlanCard: () => PlanCard
47
- });
48
- module.exports = __toCommonJS(index_exports);
49
-
50
- // src/components/PlanCard.tsx
51
- var import_ui7 = require("@ttoss/ui");
52
-
53
- // src/components/PlanCardCtaSlot.tsx
54
- var import_ui = require("@ttoss/ui");
55
- var PlanCardCtaSlot = /* @__PURE__ */__name(({
56
- buttonProps
57
- }) => {
58
- const {
59
- label: ctaLabel = "Assine agora",
60
- sx: buttonSx,
61
- leftIcon,
62
- variant,
63
- ...restButtonProps
64
- } = buttonProps ?? {};
65
- return /* @__PURE__ */React.createElement(import_ui.Flex, {
66
- sx: {
67
- paddingY: "2",
68
- paddingX: "6",
69
- width: "full",
70
- justifyContent: "center"
71
- }
72
- }, /* @__PURE__ */React.createElement(import_ui.Button, {
73
- ...restButtonProps,
74
- variant: variant ?? "accent",
75
- leftIcon,
76
- sx: {
77
- width: "full",
78
- justifyContent: "center",
79
- fontWeight: "semibold",
80
- ...buttonSx
81
- }
82
- }, ctaLabel));
83
- }, "PlanCardCtaSlot");
84
-
85
- // src/components/PlanCardFeaturesSlot.tsx
86
- var import_react_icons = require("@ttoss/react-icons");
87
- var import_ui2 = require("@ttoss/ui");
88
- var React2 = __toESM(require("react"), 1);
89
- var featuresTitle = "RECURSOS";
90
- var PlanCardFeaturesSlot = /* @__PURE__ */__name(({
91
- features,
92
- variant = "default"
93
- }) => {
94
- const isEnterprise = variant === "enterprise";
95
- const featureColor = isEnterprise ? "white" : "feedback.text.positive.default";
96
- return /* @__PURE__ */React2.createElement(import_ui2.Box, {
97
- sx: {
98
- paddingY: "4",
99
- paddingX: "6",
100
- width: "full",
101
- borderTop: "md",
102
- borderBottom: "md",
103
- borderColor: "display.border.muted.default"
104
- }
105
- }, /* @__PURE__ */React2.createElement(import_ui2.Stack, {
106
- sx: {
107
- gap: "5",
108
- paddingY: "3"
109
- }
110
- }, /* @__PURE__ */React2.createElement(import_ui2.Flex, {
111
- sx: {
112
- letterSpacing: "widest",
113
- color: isEnterprise ? "white" : "text"
114
- }
115
- }, featuresTitle), /* @__PURE__ */React2.createElement(import_ui2.Flex, {
116
- sx: {
117
- flexDirection: "column",
118
- gap: "3"
119
- }
120
- }, features.map((feature, index) => {
121
- if (/* @__PURE__ */React2.isValidElement(feature)) {
122
- return /* @__PURE__ */React2.createElement(React2.Fragment, {
123
- key: index
124
- }, feature);
125
- }
126
- return /* @__PURE__ */React2.createElement(import_ui2.Flex, {
127
- key: index,
128
- sx: {
129
- fontSize: "sm",
130
- color: featureColor,
131
- alignItems: "center",
132
- gap: "3"
133
- }
134
- }, /* @__PURE__ */React2.createElement(import_react_icons.Icon, {
135
- icon: "fluent:checkmark-24-filled"
136
- }), /* @__PURE__ */React2.createElement(import_ui2.Text, {
137
- sx: {
138
- fontSize: "sm",
139
- color: featureColor,
140
- alignItems: "center"
141
- }
142
- }, String(feature)));
143
- }))));
144
- }, "PlanCardFeaturesSlot");
145
-
146
- // src/components/PlanCardHeaderSlot.tsx
147
- var import_ui3 = require("@ttoss/ui");
148
- var PlanCardHeaderSlot = /* @__PURE__ */__name(({
149
- title,
150
- subtitle,
151
- hasTopTag,
152
- variant = "default"
153
- }) => {
154
- const isEnterprise = variant === "enterprise";
155
- return /* @__PURE__ */React.createElement(import_ui3.Box, {
156
- sx: {
157
- paddingY: hasTopTag ? "4" : "8",
158
- paddingX: "8",
159
- width: "full"
160
- }
161
- }, /* @__PURE__ */React.createElement(import_ui3.Flex, {
162
- sx: {
163
- flexDirection: "column",
164
- gap: "2",
165
- width: "full",
166
- alignItems: "center"
167
- }
168
- }, /* @__PURE__ */React.createElement(import_ui3.Heading, {
169
- sx: {
170
- fontSize: "3xl",
171
- color: isEnterprise ? "white" : "text"
172
- }
173
- }, title), subtitle && /* @__PURE__ */React.createElement(import_ui3.Text, {
174
- sx: {
175
- fontSize: "sm",
176
- color: isEnterprise ? "white" : "display.text.secondary.default"
177
- }
178
- }, subtitle)));
179
- }, "PlanCardHeaderSlot");
180
-
181
- // src/components/PlanCardMetadataSlot.tsx
182
- var import_react_icons2 = require("@ttoss/react-icons");
183
- var import_ui4 = require("@ttoss/ui");
184
- var PlanCardMetadataSlot = /* @__PURE__ */__name(({
185
- metadata,
186
- variant = "default"
187
- }) => {
188
- const isEnterprise = variant === "enterprise";
189
- return /* @__PURE__ */React.createElement(import_ui4.Stack, {
190
- sx: {
191
- width: "full",
192
- gap: "6"
193
- }
194
- }, metadata.map(service => {
195
- const serviceKey = service.id ?? service.key ?? service.label;
196
- return /* @__PURE__ */React.createElement(import_ui4.Flex, {
197
- key: serviceKey,
198
- sx: {
199
- gap: "3",
200
- flexDirection: "column",
201
- width: "full"
202
- }
203
- }, /* @__PURE__ */React.createElement(import_ui4.Flex, {
204
- sx: {
205
- gap: "4",
206
- alignItems: "center",
207
- color: isEnterprise ? "white" : "display.text.secondary.default",
208
- width: "full"
209
- }
210
- }, service.icon && /* @__PURE__ */React.createElement(import_react_icons2.Icon, {
211
- icon: service.icon
212
- }), /* @__PURE__ */React.createElement(import_ui4.Text, {
213
- sx: {
214
- color: isEnterprise ? "white" : "display.text.secondary.default"
215
- }
216
- }, service.label + String.fromCharCode(58))), service.parameters.map(parameter => {
217
- return /* @__PURE__ */React.createElement(import_ui4.Flex, {
218
- key: parameter.name,
219
- sx: {
220
- paddingX: "6",
221
- paddingY: "4",
222
- backgroundColor: "display.background.muted.default",
223
- borderRadius: "lg",
224
- width: "full"
225
- }
226
- }, /* @__PURE__ */React.createElement(import_ui4.Flex, {
227
- sx: {
228
- width: "full",
229
- display: "flex",
230
- alignItems: "center",
231
- justifyContent: "space-between",
232
- gap: "2"
233
- }
234
- }, /* @__PURE__ */React.createElement(import_ui4.Text, {
235
- sx: {
236
- fontSize: "sm",
237
- textAlign: "center"
238
- }
239
- }, parameter.name), /* @__PURE__ */React.createElement(import_ui4.Text, {
240
- sx: {
241
- fontWeight: "semibold",
242
- fontSize: "lg"
243
- }
244
- }, parameter.value)));
245
- }));
246
- }));
247
- }, "PlanCardMetadataSlot");
248
-
249
- // src/components/PlanCardPriceSlot.tsx
250
- var import_ui5 = require("@ttoss/ui");
251
- var PlanCardPriceSlot = /* @__PURE__ */__name(({
252
- price,
253
- variant = "default"
254
- }) => {
255
- const isEnterprise = variant === "enterprise";
256
- return /* @__PURE__ */React.createElement(import_ui5.Flex, {
257
- sx: {
258
- paddingY: "6",
259
- paddingX: "8",
260
- width: "full",
261
- alignItems: "center",
262
- flexDirection: "column",
263
- gap: "3"
264
- }
265
- }, /* @__PURE__ */React.createElement(import_ui5.Flex, {
266
- sx: {
267
- alignItems: "baseline",
268
- gap: "4",
269
- justifyContent: "center"
270
- }
271
- }, /* @__PURE__ */React.createElement(import_ui5.Text, {
272
- sx: {
273
- fontSize: "4xl",
274
- fontWeight: "bold",
275
- color: isEnterprise ? "white" : "display.text.primary.default"
276
- }
277
- }, price.value), /* @__PURE__ */React.createElement(import_ui5.Text, {
278
- sx: {
279
- fontSize: "sm",
280
- color: isEnterprise ? "white" : "display.text.secondary.default"
281
- }
282
- }, price.interval)), price.description && /* @__PURE__ */React.createElement(import_ui5.Text, {
283
- sx: {
284
- fontSize: "md",
285
- color: isEnterprise ? "white" : "display.text.secondary.default"
286
- }
287
- }, price.description));
288
- }, "PlanCardPriceSlot");
289
-
290
- // src/components/PlanCardTopTagSlot.tsx
291
- var import_ui6 = require("@ttoss/ui");
292
- var PlanCardTopTagSlot = /* @__PURE__ */__name(({
293
- children
294
- }) => {
295
- return /* @__PURE__ */React.createElement(import_ui6.Box, {
296
- sx: {
297
- padding: "4"
298
- }
299
- }, children);
300
- }, "PlanCardTopTagSlot");
301
-
302
- // src/components/PlanCard.tsx
303
- var PlanCard = /* @__PURE__ */__name(props => {
304
- const {
305
- variant = "default",
306
- title,
307
- subtitle,
308
- metadata = [],
309
- metadataVariant,
310
- price,
311
- features = [],
312
- buttonProps,
313
- ...cardProps
314
- } = props;
315
- const effectiveMetadataVariant = metadataVariant ?? variant;
316
- return /* @__PURE__ */React.createElement(import_ui7.Card, {
317
- ...cardProps,
318
- sx: {
319
- width: "full",
320
- maxWidth: "410px",
321
- backgroundColor: variant === "enterprise" ? "display.background.primary.active" : "display.background.primary.default",
322
- ...cardProps.sx
323
- }
324
- }, props.topTag && /* @__PURE__ */React.createElement(PlanCardTopTagSlot, {
325
- variant
326
- }, props.topTag), /* @__PURE__ */React.createElement(PlanCardHeaderSlot, {
327
- title,
328
- subtitle,
329
- hasTopTag: Boolean(props.topTag),
330
- variant
331
- }), metadata.length > 0 && /* @__PURE__ */React.createElement(import_ui7.Box, {
332
- sx: {
333
- paddingY: "4",
334
- paddingX: "8",
335
- width: "full"
336
- }
337
- }, /* @__PURE__ */React.createElement(PlanCardMetadataSlot, {
338
- metadata,
339
- variant: effectiveMetadataVariant
340
- })), /* @__PURE__ */React.createElement(PlanCardPriceSlot, {
341
- price,
342
- variant
343
- }), features.length > 0 && /* @__PURE__ */React.createElement(PlanCardFeaturesSlot, {
344
- features,
345
- variant
346
- }), /* @__PURE__ */React.createElement(PlanCardCtaSlot, {
347
- buttonProps,
348
- variant
349
- }));
350
- }, "PlanCard");
351
- // Annotate the CommonJS export names for ESM import in node:
352
- 0 && (module.exports = {
353
- PlanCard
354
- });