@ttoss/react-billing 0.3.5 → 0.4.0
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 +74 -5
- package/dist/esm/index.js +115 -8
- package/dist/index.d.ts +128 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -42,13 +42,81 @@ export const Example = () => (
|
|
|
42
42
|
);
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
###
|
|
45
|
+
### PlansPanel
|
|
46
|
+
|
|
47
|
+
A higher-level component that renders a grid of `PlanCard` components with optional multi-dimensional `SegmentedControl` filters. Plans are shown only when they match all active filter values.
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
import { PlansPanel } from '@ttoss/react-billing';
|
|
51
|
+
|
|
52
|
+
export const PricingPage = () => (
|
|
53
|
+
<PlansPanel
|
|
54
|
+
filters={[
|
|
55
|
+
{
|
|
56
|
+
id: 'interval',
|
|
57
|
+
label: 'Billing frequency',
|
|
58
|
+
options: [
|
|
59
|
+
{ label: 'Monthly', value: 'monthly' },
|
|
60
|
+
{ label: 'Yearly', value: 'yearly' },
|
|
61
|
+
],
|
|
62
|
+
defaultValue: 'monthly',
|
|
63
|
+
},
|
|
64
|
+
]}
|
|
65
|
+
plans={[
|
|
66
|
+
{
|
|
67
|
+
id: 'plan_basic_monthly',
|
|
68
|
+
filterValues: { interval: 'monthly' },
|
|
69
|
+
title: 'Basic',
|
|
70
|
+
price: {
|
|
71
|
+
value: '$29',
|
|
72
|
+
interval: '/month',
|
|
73
|
+
description: 'Billed monthly',
|
|
74
|
+
},
|
|
75
|
+
features: ['10 proposals/month'],
|
|
76
|
+
buttonProps: { label: 'Subscribe' },
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: 'plan_basic_yearly',
|
|
80
|
+
filterValues: { interval: 'yearly' },
|
|
81
|
+
title: 'Basic',
|
|
82
|
+
price: {
|
|
83
|
+
value: '$24',
|
|
84
|
+
interval: '/month',
|
|
85
|
+
description: 'Billed annually',
|
|
86
|
+
},
|
|
87
|
+
features: ['10 proposals/month'],
|
|
88
|
+
buttonProps: { label: 'Subscribe' },
|
|
89
|
+
},
|
|
90
|
+
]}
|
|
91
|
+
activePlanId="plan_basic_monthly"
|
|
92
|
+
onSelectPlan={(planId) => console.log('Selected:', planId)}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Controlled mode (parent owns filter state):
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
const [filterValues, setFilterValues] = React.useState({ interval: 'monthly' });
|
|
101
|
+
|
|
102
|
+
<PlansPanel
|
|
103
|
+
filters={filters}
|
|
104
|
+
plans={plans}
|
|
105
|
+
filterValues={filterValues}
|
|
106
|
+
onFilterChange={(id, value) =>
|
|
107
|
+
setFilterValues((prev) => ({ ...prev, [id]: value }))
|
|
108
|
+
}
|
|
109
|
+
onSelectPlan={(planId) => checkout(planId)}
|
|
110
|
+
/>;
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### SubscriptionPanel
|
|
46
114
|
|
|
47
115
|
```tsx
|
|
48
|
-
import {
|
|
116
|
+
import { SubscriptionPanel } from '@ttoss/react-billing';
|
|
49
117
|
|
|
50
118
|
export const Example = () => (
|
|
51
|
-
<
|
|
119
|
+
<SubscriptionPanel
|
|
52
120
|
planName="Pro"
|
|
53
121
|
price={{ value: 'R$ 99,00', interval: 'month' }}
|
|
54
122
|
status={{ status: 'active', interval: 'Monthly' }}
|
|
@@ -76,5 +144,6 @@ export const Example = () => (
|
|
|
76
144
|
## Exports
|
|
77
145
|
|
|
78
146
|
- `PlanCard`
|
|
79
|
-
- `
|
|
80
|
-
-
|
|
147
|
+
- `PlansPanel`
|
|
148
|
+
- `SubscriptionPanel`
|
|
149
|
+
- Types: `PlanCardProps`, `PlanCardVariant`, `PlanCardPrice`, `PlanCardMetadata`, `PlanCardButtonProps`, `PlanCardMetadataSlotService` (and related slot types), `PlansPanelProps`, `PlansPanelPlan`, `PlansPanelFilter`, `PlansPanelFilterOption`
|
package/dist/esm/index.js
CHANGED
|
@@ -345,10 +345,117 @@ var PlanCard = /* @__PURE__ */__name(props => {
|
|
|
345
345
|
}));
|
|
346
346
|
}, "PlanCard");
|
|
347
347
|
|
|
348
|
+
// src/components/plansPanel/PlansPanel.tsx
|
|
349
|
+
import { Flex as Flex6, SegmentedControl, Stack as Stack3, Text as Text5 } from "@ttoss/ui";
|
|
350
|
+
import * as React3 from "react";
|
|
351
|
+
var PlansPanel = /* @__PURE__ */__name(({
|
|
352
|
+
filters = [],
|
|
353
|
+
plans,
|
|
354
|
+
activePlanId,
|
|
355
|
+
onSelectPlan,
|
|
356
|
+
filterValues: controlledFilterValues,
|
|
357
|
+
onFilterChange
|
|
358
|
+
}) => {
|
|
359
|
+
const [internalFilterValues, setInternalFilterValues] = React3.useState(() => {
|
|
360
|
+
const initial = {};
|
|
361
|
+
for (const filter of filters) {
|
|
362
|
+
if (filter.defaultValue !== void 0) {
|
|
363
|
+
initial[filter.id] = filter.defaultValue;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return initial;
|
|
367
|
+
});
|
|
368
|
+
const isControlled = controlledFilterValues !== void 0;
|
|
369
|
+
const activeFilterValues = isControlled ? controlledFilterValues : internalFilterValues;
|
|
370
|
+
const handleFilterChange = /* @__PURE__ */__name((id, value) => {
|
|
371
|
+
const strValue = String(value);
|
|
372
|
+
if (isControlled) {
|
|
373
|
+
onFilterChange?.(id, strValue);
|
|
374
|
+
} else {
|
|
375
|
+
setInternalFilterValues(prev => {
|
|
376
|
+
return {
|
|
377
|
+
...prev,
|
|
378
|
+
[id]: strValue
|
|
379
|
+
};
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}, "handleFilterChange");
|
|
383
|
+
const visiblePlans = plans.filter(plan => {
|
|
384
|
+
if (!plan.filterValues || Object.keys(plan.filterValues).length === 0) {
|
|
385
|
+
return true;
|
|
386
|
+
}
|
|
387
|
+
return Object.entries(activeFilterValues).every(([filterId, filterValue]) => {
|
|
388
|
+
if (!plan.filterValues || !(filterId in plan.filterValues)) {
|
|
389
|
+
return true;
|
|
390
|
+
}
|
|
391
|
+
return plan.filterValues[filterId] === filterValue;
|
|
392
|
+
});
|
|
393
|
+
});
|
|
394
|
+
return /* @__PURE__ */React3.createElement(Stack3, {
|
|
395
|
+
sx: {
|
|
396
|
+
gap: "6",
|
|
397
|
+
width: "full",
|
|
398
|
+
alignItems: "center"
|
|
399
|
+
}
|
|
400
|
+
}, filters.length > 0 && /* @__PURE__ */React3.createElement(Stack3, {
|
|
401
|
+
sx: {
|
|
402
|
+
gap: "4",
|
|
403
|
+
alignItems: "center"
|
|
404
|
+
}
|
|
405
|
+
}, filters.map(filter => {
|
|
406
|
+
return /* @__PURE__ */React3.createElement(Stack3, {
|
|
407
|
+
key: filter.id,
|
|
408
|
+
sx: {
|
|
409
|
+
gap: "2",
|
|
410
|
+
alignItems: "center"
|
|
411
|
+
}
|
|
412
|
+
}, filter.label && /* @__PURE__ */React3.createElement(Text5, {
|
|
413
|
+
sx: {
|
|
414
|
+
fontWeight: "semibold",
|
|
415
|
+
color: "display.text.primary.default"
|
|
416
|
+
}
|
|
417
|
+
}, filter.label), /* @__PURE__ */React3.createElement(SegmentedControl, {
|
|
418
|
+
options: filter.options,
|
|
419
|
+
value: isControlled ? controlledFilterValues[filter.id] ?? filter.defaultValue : activeFilterValues[filter.id],
|
|
420
|
+
defaultValue: isControlled ? void 0 : filter.defaultValue,
|
|
421
|
+
onChange: /* @__PURE__ */__name(value => {
|
|
422
|
+
return handleFilterChange(filter.id, value);
|
|
423
|
+
}, "onChange")
|
|
424
|
+
}));
|
|
425
|
+
})), /* @__PURE__ */React3.createElement(Flex6, {
|
|
426
|
+
sx: {
|
|
427
|
+
flexWrap: "wrap",
|
|
428
|
+
gap: "4",
|
|
429
|
+
width: "full",
|
|
430
|
+
justifyContent: "center"
|
|
431
|
+
}
|
|
432
|
+
}, visiblePlans.map(plan => {
|
|
433
|
+
const isActive = plan.id === activePlanId;
|
|
434
|
+
const effectiveVariant = isActive ? "secondary" : plan.variant ?? "primary";
|
|
435
|
+
return /* @__PURE__ */React3.createElement(PlanCard, {
|
|
436
|
+
key: plan.id,
|
|
437
|
+
title: plan.title,
|
|
438
|
+
subtitle: plan.subtitle,
|
|
439
|
+
price: plan.price,
|
|
440
|
+
features: plan.features,
|
|
441
|
+
metadata: plan.metadata,
|
|
442
|
+
variant: effectiveVariant,
|
|
443
|
+
topTag: plan.topTag,
|
|
444
|
+
buttonProps: {
|
|
445
|
+
...plan.buttonProps,
|
|
446
|
+
onClick: /* @__PURE__ */__name(e => {
|
|
447
|
+
onSelectPlan?.(plan.id);
|
|
448
|
+
plan.buttonProps?.onClick?.(e);
|
|
449
|
+
}, "onClick")
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
})));
|
|
453
|
+
}, "PlansPanel");
|
|
454
|
+
|
|
348
455
|
// src/components/subscriptionPanel/SubscriptionPanel.tsx
|
|
349
456
|
import { EnhancedTitle } from "@ttoss/components/EnhancedTitle";
|
|
350
457
|
import { MetricCard } from "@ttoss/components/MetricCard";
|
|
351
|
-
import { Card as Card2, Flex as
|
|
458
|
+
import { Card as Card2, Flex as Flex8, Spinner } from "@ttoss/ui";
|
|
352
459
|
|
|
353
460
|
// src/components/subscriptionPanel/SubscriptionPanel.styles.ts
|
|
354
461
|
import { keyframes } from "@ttoss/ui";
|
|
@@ -435,14 +542,14 @@ var SubscriptionPanelAccentBarSx = getSubscriptionPanelAccentBarSx("spotlight-ac
|
|
|
435
542
|
|
|
436
543
|
// src/components/subscriptionPanel/SubscriptionPanelActionsSlot.tsx
|
|
437
544
|
import { Icon as Icon3 } from "@ttoss/react-icons";
|
|
438
|
-
import { Button as Button2, Flex as
|
|
545
|
+
import { Button as Button2, Flex as Flex7 } from "@ttoss/ui";
|
|
439
546
|
var SubscriptionPanelActionsSlot = /* @__PURE__ */__name(({
|
|
440
547
|
actions
|
|
441
548
|
}) => {
|
|
442
549
|
if (actions.length === 0) {
|
|
443
550
|
return null;
|
|
444
551
|
}
|
|
445
|
-
return /* @__PURE__ */React.createElement(
|
|
552
|
+
return /* @__PURE__ */React.createElement(Flex7, {
|
|
446
553
|
sx: {
|
|
447
554
|
gap: "3",
|
|
448
555
|
flexDirection: ["column", "row"],
|
|
@@ -516,16 +623,16 @@ var SubscriptionPanel = /* @__PURE__ */__name(({
|
|
|
516
623
|
sx: {
|
|
517
624
|
width: "full"
|
|
518
625
|
}
|
|
519
|
-
}, /* @__PURE__ */React.createElement(
|
|
626
|
+
}, /* @__PURE__ */React.createElement(Flex8, {
|
|
520
627
|
sx: getSubscriptionPanelAccentBarSx(variant)
|
|
521
|
-
}), /* @__PURE__ */React.createElement(
|
|
628
|
+
}), /* @__PURE__ */React.createElement(Flex8, {
|
|
522
629
|
sx: {
|
|
523
630
|
width: "full",
|
|
524
631
|
paddingX: "6",
|
|
525
632
|
paddingY: "6",
|
|
526
633
|
flexDirection: "column"
|
|
527
634
|
}
|
|
528
|
-
}, /* @__PURE__ */React.createElement(
|
|
635
|
+
}, /* @__PURE__ */React.createElement(Flex8, {
|
|
529
636
|
sx: {
|
|
530
637
|
paddingY: "10",
|
|
531
638
|
paddingX: "3",
|
|
@@ -583,7 +690,7 @@ var SubscriptionPanel = /* @__PURE__ */__name(({
|
|
|
583
690
|
})
|
|
584
691
|
}), actions.length > 0 && /* @__PURE__ */React.createElement(SubscriptionPanelActionsSlot, {
|
|
585
692
|
actions
|
|
586
|
-
})), metrics.length > 0 && /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(
|
|
693
|
+
})), metrics.length > 0 && /* @__PURE__ */React.createElement(React.Fragment, null, /* @__PURE__ */React.createElement(Flex8, {
|
|
587
694
|
sx: {
|
|
588
695
|
paddingY: "10",
|
|
589
696
|
paddingX: "3",
|
|
@@ -600,4 +707,4 @@ var SubscriptionPanel = /* @__PURE__ */__name(({
|
|
|
600
707
|
});
|
|
601
708
|
})))));
|
|
602
709
|
}, "SubscriptionPanel");
|
|
603
|
-
export { PlanCard, SubscriptionPanel };
|
|
710
|
+
export { PlanCard, PlansPanel, SubscriptionPanel };
|
package/dist/index.d.ts
CHANGED
|
@@ -54,6 +54,133 @@ interface PlanCardProps extends Omit<CardProps, 'children'> {
|
|
|
54
54
|
}
|
|
55
55
|
declare const PlanCard: (props: PlanCardProps) => react_jsx_runtime.JSX.Element;
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* A single option inside a PlansPanel filter.
|
|
59
|
+
*/
|
|
60
|
+
interface PlansPanelFilterOption {
|
|
61
|
+
/** Display label shown in the SegmentedControl. */
|
|
62
|
+
label: string;
|
|
63
|
+
/** Value used for filtering plans. */
|
|
64
|
+
value: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Defines one filter dimension rendered as a SegmentedControl above the plans grid.
|
|
68
|
+
*/
|
|
69
|
+
interface PlansPanelFilter {
|
|
70
|
+
/** Unique identifier for this filter dimension. */
|
|
71
|
+
id: string;
|
|
72
|
+
/** Optional label rendered above the SegmentedControl. */
|
|
73
|
+
label?: string;
|
|
74
|
+
/** List of selectable options. */
|
|
75
|
+
options: PlansPanelFilterOption[];
|
|
76
|
+
/** Initial value used in uncontrolled mode. */
|
|
77
|
+
defaultValue?: string;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* A plan displayed inside the PlansPanel.
|
|
81
|
+
*/
|
|
82
|
+
interface PlansPanelPlan {
|
|
83
|
+
/** Unique plan identifier. */
|
|
84
|
+
id: string;
|
|
85
|
+
/**
|
|
86
|
+
* Maps each filter id to the value this plan belongs to.
|
|
87
|
+
* Plans are only shown when all active filter values match.
|
|
88
|
+
*/
|
|
89
|
+
filterValues?: Record<string, string>;
|
|
90
|
+
/** Plan title (e.g. "Basic", "Pro"). */
|
|
91
|
+
title: string;
|
|
92
|
+
/** Optional subtitle displayed below the title. */
|
|
93
|
+
subtitle?: string;
|
|
94
|
+
/** Pricing information. */
|
|
95
|
+
price: PlanCardPrice;
|
|
96
|
+
/** Feature list items. */
|
|
97
|
+
features?: unknown[];
|
|
98
|
+
/** Optional metadata sections. */
|
|
99
|
+
metadata?: PlanCardMetadata;
|
|
100
|
+
/** Props forwarded to the CTA button. onClick is merged with onSelectPlan. */
|
|
101
|
+
buttonProps?: PlanCardButtonProps;
|
|
102
|
+
/** Visual variant for the PlanCard. Active plans override this with "secondary". */
|
|
103
|
+
variant?: PlanCardVariant;
|
|
104
|
+
/** Optional content rendered at the top of the PlanCard. */
|
|
105
|
+
topTag?: React.ReactNode;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Props for the PlansPanel component.
|
|
109
|
+
*/
|
|
110
|
+
interface PlansPanelProps {
|
|
111
|
+
/**
|
|
112
|
+
* Filter dimensions rendered as SegmentedControl rows above the plan grid.
|
|
113
|
+
* Each dimension independently narrows the visible set of plans.
|
|
114
|
+
*/
|
|
115
|
+
filters?: PlansPanelFilter[];
|
|
116
|
+
/** All available plans. Only plans matching every active filter value are shown. */
|
|
117
|
+
plans: PlansPanelPlan[];
|
|
118
|
+
/** ID of the currently active/subscribed plan. That card is highlighted. */
|
|
119
|
+
activePlanId?: string;
|
|
120
|
+
/**
|
|
121
|
+
* Called when the user clicks the CTA button of a plan.
|
|
122
|
+
* @param planId - The id of the selected plan.
|
|
123
|
+
*/
|
|
124
|
+
onSelectPlan?: (planId: string) => void;
|
|
125
|
+
/**
|
|
126
|
+
* Controlled filter values (record of filterId → selected value).
|
|
127
|
+
* When provided the component operates in controlled mode and internal
|
|
128
|
+
* state is not used.
|
|
129
|
+
*/
|
|
130
|
+
filterValues?: Record<string, string>;
|
|
131
|
+
/**
|
|
132
|
+
* Called when a filter value changes in controlled mode.
|
|
133
|
+
* @param id - The filter id that changed.
|
|
134
|
+
* @param value - The new selected value.
|
|
135
|
+
*/
|
|
136
|
+
onFilterChange?: (id: string, value: string) => void;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* PlansPanel renders a grid of PlanCard components with optional multi-dimensional
|
|
141
|
+
* SegmentedControl filters above them.
|
|
142
|
+
*
|
|
143
|
+
* Each filter dimension independently narrows the visible set of plan cards.
|
|
144
|
+
* The component supports both uncontrolled mode (internal state from `defaultValue`)
|
|
145
|
+
* and controlled mode (via `filterValues` + `onFilterChange`).
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```tsx
|
|
149
|
+
* // Uncontrolled — component manages filter state internally
|
|
150
|
+
* <PlansPanel
|
|
151
|
+
* filters={[
|
|
152
|
+
* {
|
|
153
|
+
* id: 'interval',
|
|
154
|
+
* label: 'Billing Frequency',
|
|
155
|
+
* options: [
|
|
156
|
+
* { label: 'Monthly', value: 'monthly' },
|
|
157
|
+
* { label: 'Yearly', value: 'yearly' },
|
|
158
|
+
* ],
|
|
159
|
+
* defaultValue: 'monthly',
|
|
160
|
+
* },
|
|
161
|
+
* ]}
|
|
162
|
+
* plans={plans}
|
|
163
|
+
* activePlanId="plan_basic_monthly"
|
|
164
|
+
* onSelectPlan={(planId) => console.log(planId)}
|
|
165
|
+
* />
|
|
166
|
+
* ```
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```tsx
|
|
170
|
+
* // Controlled — consumer owns filter state
|
|
171
|
+
* <PlansPanel
|
|
172
|
+
* filters={filters}
|
|
173
|
+
* filterValues={{ interval: 'yearly' }}
|
|
174
|
+
* onFilterChange={(id, value) =>
|
|
175
|
+
* setFilterValues((prev) => ({ ...prev, [id]: value }))
|
|
176
|
+
* }
|
|
177
|
+
* plans={plans}
|
|
178
|
+
* onSelectPlan={(planId) => checkout(planId)}
|
|
179
|
+
* />
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
declare const PlansPanel: ({ filters, plans, activePlanId, onSelectPlan, filterValues: controlledFilterValues, onFilterChange, }: PlansPanelProps) => react_jsx_runtime.JSX.Element;
|
|
183
|
+
|
|
57
184
|
type SubscriptionPanelVariant = 'spotlight-accent' | 'spotlight-primary' | 'primary' | 'secondary' | 'accent';
|
|
58
185
|
|
|
59
186
|
/**
|
|
@@ -304,4 +431,4 @@ interface SubscriptionPanelProps {
|
|
|
304
431
|
*/
|
|
305
432
|
declare const SubscriptionPanel: ({ variant, icon, planName, price, status, features, actions, metrics, isLoading, }: SubscriptionPanelProps) => react_jsx_runtime.JSX.Element;
|
|
306
433
|
|
|
307
|
-
export { PlanCard, type PlanCardButtonProps, type PlanCardMetadata, type PlanCardMetadataSlotParameter, type PlanCardMetadataSlotProps, type PlanCardMetadataSlotService, type PlanCardMetadataSlotVariant, type PlanCardPrice, type PlanCardProps, type PlanCardVariant, SubscriptionPanel };
|
|
434
|
+
export { PlanCard, type PlanCardButtonProps, type PlanCardMetadata, type PlanCardMetadataSlotParameter, type PlanCardMetadataSlotProps, type PlanCardMetadataSlotService, type PlanCardMetadataSlotVariant, type PlanCardPrice, type PlanCardProps, type PlanCardVariant, PlansPanel, type PlansPanelFilter, type PlansPanelFilterOption, type PlansPanelPlan, type PlansPanelProps, SubscriptionPanel };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ttoss/react-billing",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Billing UI components for React apps.",
|
|
5
5
|
"author": "Giovane Guimarães",
|
|
6
6
|
"repository": {
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
],
|
|
21
21
|
"sideEffects": false,
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@ttoss/
|
|
24
|
-
"@ttoss/
|
|
25
|
-
"@ttoss/ui": "^6.9.
|
|
23
|
+
"@ttoss/components": "^2.14.7",
|
|
24
|
+
"@ttoss/react-icons": "^0.7.7",
|
|
25
|
+
"@ttoss/ui": "^6.9.7"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
28
|
"react": ">=16.8.0"
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"jest": "^30.3.0",
|
|
34
34
|
"react": "^19.2.4",
|
|
35
35
|
"tsup": "^8.5.1",
|
|
36
|
-
"@ttoss/config": "^1.37.
|
|
37
|
-
"@ttoss/test-utils": "^4.2.
|
|
36
|
+
"@ttoss/config": "^1.37.7",
|
|
37
|
+
"@ttoss/test-utils": "^4.2.7"
|
|
38
38
|
},
|
|
39
39
|
"keywords": [
|
|
40
40
|
"React",
|