@omnibase/shadcn 0.1.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/dist/index.js ADDED
@@ -0,0 +1,748 @@
1
+ // src/components/ui/card.tsx
2
+ import "react";
3
+
4
+ // src/lib/utils.ts
5
+ import { clsx } from "clsx";
6
+ import { twMerge } from "tailwind-merge";
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+
11
+ // src/components/ui/card.tsx
12
+ import { jsx } from "react/jsx-runtime";
13
+ function Card({ className, ...props }) {
14
+ return /* @__PURE__ */ jsx(
15
+ "div",
16
+ {
17
+ "data-slot": "card",
18
+ className: cn(
19
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
20
+ className
21
+ ),
22
+ ...props
23
+ }
24
+ );
25
+ }
26
+ function CardHeader({ className, ...props }) {
27
+ return /* @__PURE__ */ jsx(
28
+ "div",
29
+ {
30
+ "data-slot": "card-header",
31
+ className: cn(
32
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
33
+ className
34
+ ),
35
+ ...props
36
+ }
37
+ );
38
+ }
39
+ function CardTitle({ className, ...props }) {
40
+ return /* @__PURE__ */ jsx(
41
+ "div",
42
+ {
43
+ "data-slot": "card-title",
44
+ className: cn("leading-none font-semibold", className),
45
+ ...props
46
+ }
47
+ );
48
+ }
49
+ function CardDescription({ className, ...props }) {
50
+ return /* @__PURE__ */ jsx(
51
+ "div",
52
+ {
53
+ "data-slot": "card-description",
54
+ className: cn("text-muted-foreground text-sm", className),
55
+ ...props
56
+ }
57
+ );
58
+ }
59
+ function CardContent({ className, ...props }) {
60
+ return /* @__PURE__ */ jsx(
61
+ "div",
62
+ {
63
+ "data-slot": "card-content",
64
+ className: cn("px-6", className),
65
+ ...props
66
+ }
67
+ );
68
+ }
69
+ function CardFooter({ className, ...props }) {
70
+ return /* @__PURE__ */ jsx(
71
+ "div",
72
+ {
73
+ "data-slot": "card-footer",
74
+ className: cn("flex items-center px-6 [.border-t]:pt-6", className),
75
+ ...props
76
+ }
77
+ );
78
+ }
79
+
80
+ // src/components/ui/button.tsx
81
+ import "react";
82
+ import { Slot } from "@radix-ui/react-slot";
83
+ import { cva } from "class-variance-authority";
84
+ import { jsx as jsx2 } from "react/jsx-runtime";
85
+ var buttonVariants = cva(
86
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
87
+ {
88
+ variants: {
89
+ variant: {
90
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
91
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
92
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
93
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
94
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
95
+ link: "text-primary underline-offset-4 hover:underline"
96
+ },
97
+ size: {
98
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
99
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
100
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
101
+ icon: "size-9"
102
+ }
103
+ },
104
+ defaultVariants: {
105
+ variant: "default",
106
+ size: "default"
107
+ }
108
+ }
109
+ );
110
+ function Button({
111
+ className,
112
+ variant,
113
+ size,
114
+ asChild = false,
115
+ ...props
116
+ }) {
117
+ const Comp = asChild ? Slot : "button";
118
+ return /* @__PURE__ */ jsx2(
119
+ Comp,
120
+ {
121
+ "data-slot": "button",
122
+ className: cn(buttonVariants({ variant, size, className })),
123
+ ...props
124
+ }
125
+ );
126
+ }
127
+
128
+ // src/components/ui/input.tsx
129
+ import "react";
130
+ import { jsx as jsx3 } from "react/jsx-runtime";
131
+ function Input({ className, type, ...props }) {
132
+ return /* @__PURE__ */ jsx3(
133
+ "input",
134
+ {
135
+ type,
136
+ "data-slot": "input",
137
+ className: cn(
138
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
139
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
140
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
141
+ className
142
+ ),
143
+ ...props
144
+ }
145
+ );
146
+ }
147
+
148
+ // src/components/ui/label.tsx
149
+ import "react";
150
+ import * as LabelPrimitive from "@radix-ui/react-label";
151
+ import { jsx as jsx4 } from "react/jsx-runtime";
152
+ function Label({
153
+ className,
154
+ ...props
155
+ }) {
156
+ return /* @__PURE__ */ jsx4(
157
+ LabelPrimitive.Root,
158
+ {
159
+ "data-slot": "label",
160
+ className: cn(
161
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
162
+ className
163
+ ),
164
+ ...props
165
+ }
166
+ );
167
+ }
168
+
169
+ // src/form/index.tsx
170
+ import { jsx as jsx5, jsxs } from "react/jsx-runtime";
171
+ function isUiNodeInputAttributes(attributes) {
172
+ return attributes && typeof attributes === "object" && "name" in attributes && "type" in attributes;
173
+ }
174
+ function CustomFlowForm({ flow, Header }) {
175
+ const hasSubmitButton = flow.ui.nodes.some(
176
+ (node) => isUiNodeInputAttributes(node.attributes) && node.attributes.type === "submit"
177
+ );
178
+ return /* @__PURE__ */ jsx5("div", { children: /* @__PURE__ */ jsx5(Card, { className: "w-full max-w-md mx-auto", children: /* @__PURE__ */ jsxs("form", { action: flow.ui.action, method: flow.ui.method, children: [
179
+ Header && /* @__PURE__ */ jsx5(CardHeader, { children: /* @__PURE__ */ jsx5(CardTitle, { className: "text-center pb-4", children: Header }) }),
180
+ /* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
181
+ flow.ui.nodes.map((node) => {
182
+ if (isUiNodeInputAttributes(node.attributes)) {
183
+ const isSubmitButton = node.attributes.type === "submit";
184
+ const isHiddenField = node.attributes.type === "hidden";
185
+ const isVisibleField = !isHiddenField && !isSubmitButton;
186
+ if (isHiddenField) {
187
+ return /* @__PURE__ */ jsx5(
188
+ "input",
189
+ {
190
+ name: node.attributes.name,
191
+ type: "hidden",
192
+ value: node.attributes.value || "",
193
+ readOnly: true
194
+ },
195
+ node.attributes.name
196
+ );
197
+ }
198
+ if (isSubmitButton) {
199
+ return /* @__PURE__ */ jsx5(
200
+ Button,
201
+ {
202
+ type: "submit",
203
+ name: node.attributes.name,
204
+ value: node.attributes.value || "",
205
+ className: "w-full mt-2",
206
+ children: node.meta.label?.text || node.attributes.value || "Submit"
207
+ },
208
+ node.attributes.name
209
+ );
210
+ }
211
+ if (isVisibleField && [
212
+ "default",
213
+ "password",
214
+ "code",
215
+ "webauthn",
216
+ "passkey",
217
+ "totp",
218
+ "lookup_secret"
219
+ ].includes(node.group)) {
220
+ return /* @__PURE__ */ jsxs(
221
+ "div",
222
+ {
223
+ className: "space-y-2",
224
+ children: [
225
+ /* @__PURE__ */ jsxs(Label, { htmlFor: node.attributes.name, children: [
226
+ node.meta.label?.text,
227
+ node.attributes.required && /* @__PURE__ */ jsx5("span", { className: "text-destructive ml-1", children: "*" })
228
+ ] }),
229
+ /* @__PURE__ */ jsx5(
230
+ Input,
231
+ {
232
+ id: node.attributes.name,
233
+ name: node.attributes.name,
234
+ type: node.attributes.type,
235
+ defaultValue: node.attributes.value || "",
236
+ required: node.attributes.required,
237
+ placeholder: `Enter your ${node.meta.label?.text?.toLowerCase() || node.attributes.name}`
238
+ }
239
+ )
240
+ ]
241
+ },
242
+ node.meta.label?.id || node.attributes.name
243
+ );
244
+ }
245
+ }
246
+ return null;
247
+ }),
248
+ !hasSubmitButton && /* @__PURE__ */ jsx5(Button, { type: "submit", className: "w-full", children: "Submit" })
249
+ ] })
250
+ ] }) }) });
251
+ }
252
+
253
+ // src/tenant-switcher/index.tsx
254
+ import * as React6 from "react";
255
+
256
+ // src/components/ui/select.tsx
257
+ import * as React5 from "react";
258
+ import * as SelectPrimitive from "@radix-ui/react-select";
259
+ import { Check, ChevronDown, ChevronUp } from "lucide-react";
260
+ import { jsx as jsx6, jsxs as jsxs2 } from "react/jsx-runtime";
261
+ var Select = SelectPrimitive.Root;
262
+ var SelectValue = SelectPrimitive.Value;
263
+ var SelectTrigger = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs2(
264
+ SelectPrimitive.Trigger,
265
+ {
266
+ ref,
267
+ className: cn(
268
+ "flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
269
+ className
270
+ ),
271
+ ...props,
272
+ children: [
273
+ children,
274
+ /* @__PURE__ */ jsx6(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx6(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
275
+ ]
276
+ }
277
+ ));
278
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
279
+ var SelectScrollUpButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
280
+ SelectPrimitive.ScrollUpButton,
281
+ {
282
+ ref,
283
+ className: cn(
284
+ "flex cursor-default items-center justify-center py-1",
285
+ className
286
+ ),
287
+ ...props,
288
+ children: /* @__PURE__ */ jsx6(ChevronUp, { className: "h-4 w-4" })
289
+ }
290
+ ));
291
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
292
+ var SelectScrollDownButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
293
+ SelectPrimitive.ScrollDownButton,
294
+ {
295
+ ref,
296
+ className: cn(
297
+ "flex cursor-default items-center justify-center py-1",
298
+ className
299
+ ),
300
+ ...props,
301
+ children: /* @__PURE__ */ jsx6(ChevronDown, { className: "h-4 w-4" })
302
+ }
303
+ ));
304
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
305
+ var SelectContent = React5.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx6(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs2(
306
+ SelectPrimitive.Content,
307
+ {
308
+ ref,
309
+ className: cn(
310
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
311
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
312
+ className
313
+ ),
314
+ position,
315
+ ...props,
316
+ children: [
317
+ /* @__PURE__ */ jsx6(SelectScrollUpButton, {}),
318
+ /* @__PURE__ */ jsx6(
319
+ SelectPrimitive.Viewport,
320
+ {
321
+ className: cn(
322
+ "p-1",
323
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
324
+ ),
325
+ children
326
+ }
327
+ ),
328
+ /* @__PURE__ */ jsx6(SelectScrollDownButton, {})
329
+ ]
330
+ }
331
+ ) }));
332
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
333
+ var SelectLabel = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
334
+ SelectPrimitive.Label,
335
+ {
336
+ ref,
337
+ className: cn("px-2 py-1.5 text-sm font-semibold", className),
338
+ ...props
339
+ }
340
+ ));
341
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
342
+ var SelectItem = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs2(
343
+ SelectPrimitive.Item,
344
+ {
345
+ ref,
346
+ className: cn(
347
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
348
+ className
349
+ ),
350
+ ...props,
351
+ children: [
352
+ /* @__PURE__ */ jsx6("span", { className: "absolute right-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx6(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx6(Check, { className: "h-4 w-4" }) }) }),
353
+ /* @__PURE__ */ jsx6(SelectPrimitive.ItemText, { children })
354
+ ]
355
+ }
356
+ ));
357
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
358
+ var SelectSeparator = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx6(
359
+ SelectPrimitive.Separator,
360
+ {
361
+ ref,
362
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
363
+ ...props
364
+ }
365
+ ));
366
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
367
+
368
+ // src/tenant-switcher/index.tsx
369
+ import { jsx as jsx7, jsxs as jsxs3 } from "react/jsx-runtime";
370
+ function SwitchActiveTenant({
371
+ tenants,
372
+ currentTenantId,
373
+ formAction,
374
+ placeholder = "Select tenant...",
375
+ className,
376
+ onTenantChange
377
+ }) {
378
+ const [isLoading, setIsLoading] = React6.useState(false);
379
+ const handleTenantChange = async (tenantId) => {
380
+ if (tenantId === currentTenantId) return;
381
+ setIsLoading(true);
382
+ try {
383
+ onTenantChange?.(tenantId);
384
+ if (formAction) {
385
+ const formData = new FormData();
386
+ formData.append("tenant_id", tenantId);
387
+ await formAction(formData);
388
+ }
389
+ } catch (error) {
390
+ console.error("Failed to switch tenant:", error);
391
+ } finally {
392
+ setIsLoading(false);
393
+ }
394
+ };
395
+ const currentTenant = tenants.find((tenant) => tenant.id === currentTenantId);
396
+ return /* @__PURE__ */ jsxs3(
397
+ Select,
398
+ {
399
+ value: currentTenantId,
400
+ onValueChange: handleTenantChange,
401
+ disabled: isLoading,
402
+ children: [
403
+ /* @__PURE__ */ jsx7(SelectTrigger, { className: cn("max-w-64", className), children: /* @__PURE__ */ jsx7(SelectValue, { placeholder, children: currentTenant ? currentTenant.name : placeholder }) }),
404
+ /* @__PURE__ */ jsx7(SelectContent, { children: tenants.map((tenant) => /* @__PURE__ */ jsx7(SelectItem, { value: tenant.id, children: tenant.name }, tenant.id)) })
405
+ ]
406
+ }
407
+ );
408
+ }
409
+
410
+ // src/pricing-table/index.tsx
411
+ import * as React7 from "react";
412
+ import { Check as Check2, Star, ChevronLeft, ChevronRight } from "lucide-react";
413
+ import { Fragment, jsx as jsx8, jsxs as jsxs4 } from "react/jsx-runtime";
414
+ var getCurrencySymbol = (currency) => {
415
+ const symbols = {
416
+ USD: "$",
417
+ EUR: "\u20AC",
418
+ GBP: "\xA3",
419
+ JPY: "\xA5",
420
+ CAD: "C$",
421
+ AUD: "A$"
422
+ };
423
+ return symbols[currency] || currency;
424
+ };
425
+ var formatPrice = (price) => {
426
+ const priceUI = price.ui || {};
427
+ if (priceUI.price_display?.custom_text)
428
+ return priceUI.price_display.custom_text;
429
+ if (!price.amount || price.amount === 0) return "Free";
430
+ const amount = price.amount / 100;
431
+ const currency = price.currency.toUpperCase();
432
+ let formattedPrice = priceUI.price_display?.show_currency !== false ? `${getCurrencySymbol(currency)}${amount.toFixed(2)}` : amount.toFixed(2);
433
+ if (priceUI.price_display?.suffix)
434
+ formattedPrice += ` ${priceUI.price_display.suffix}`;
435
+ return formattedPrice;
436
+ };
437
+ var formatBillingPeriod = (price) => {
438
+ const priceUI = price.ui || {};
439
+ if (priceUI.billing_period) return priceUI.billing_period;
440
+ if (price.interval) {
441
+ const count = price.interval_count || 1;
442
+ return `per ${count === 1 ? price.interval : `${count} ${price.interval}s`}`;
443
+ }
444
+ return "one-time";
445
+ };
446
+ function PricingCard({
447
+ product,
448
+ isSelected,
449
+ onPriceSelect,
450
+ displayedPrice
451
+ }) {
452
+ const ui = product.ui || {};
453
+ const isHighlighted = ui.highlighted;
454
+ return /* @__PURE__ */ jsxs4(
455
+ "div",
456
+ {
457
+ className: cn(
458
+ "flex flex-col h-full pb-6",
459
+ isHighlighted ? "relative" : ""
460
+ ),
461
+ children: [
462
+ /* @__PURE__ */ jsx8("div", { className: "h-4 flex-shrink-0 relative", children: ui.badge && /* @__PURE__ */ jsx8("div", { className: "absolute top-0 left-1/2 transform -translate-x-1/2 z-10", children: /* @__PURE__ */ jsxs4("div", { className: "bg-primary text-primary-foreground px-3 py-1 rounded-full text-sm font-medium flex items-center gap-1 whitespace-nowrap shadow-md", children: [
463
+ ui.badge === "Most Popular" && /* @__PURE__ */ jsx8(Star, { className: "w-3 h-3" }),
464
+ ui.badge
465
+ ] }) }) }),
466
+ /* @__PURE__ */ jsxs4(
467
+ Card,
468
+ {
469
+ className: cn(
470
+ "flex flex-col flex-1 w-full transition-all duration-200 hover:shadow-lg",
471
+ isHighlighted && "border-primary shadow-lg",
472
+ isSelected && "ring-2 ring-primary"
473
+ ),
474
+ children: [
475
+ /* @__PURE__ */ jsxs4(CardHeader, { className: "text-center", children: [
476
+ /* @__PURE__ */ jsx8(CardTitle, { className: "text-xl font-bold", children: ui.display_name || product.name }),
477
+ ui.tagline && /* @__PURE__ */ jsx8(CardDescription, { className: "text-base", children: ui.tagline })
478
+ ] }),
479
+ /* @__PURE__ */ jsxs4(CardContent, { className: "flex-1 space-y-6", children: [
480
+ /* @__PURE__ */ jsxs4("div", { className: "text-center", children: [
481
+ /* @__PURE__ */ jsx8("div", { className: "text-3xl font-bold", children: formatPrice(displayedPrice) }),
482
+ /* @__PURE__ */ jsx8("div", { className: "text-sm text-muted-foreground", children: formatBillingPeriod(displayedPrice) })
483
+ ] }),
484
+ (ui.features && ui.features.length > 0 || displayedPrice.ui?.features?.length > 0 || displayedPrice.ui?.limits?.length > 0) && /* @__PURE__ */ jsxs4("div", { className: "space-y-4", children: [
485
+ ui.features && ui.features.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
486
+ /* @__PURE__ */ jsx8("h4", { className: "font-medium text-sm text-muted-foreground uppercase tracking-wide", children: "Features" }),
487
+ /* @__PURE__ */ jsx8("ul", { className: "space-y-2", children: ui.features.map((feature, index) => /* @__PURE__ */ jsxs4("li", { className: "flex items-start gap-2", children: [
488
+ /* @__PURE__ */ jsx8(Check2, { className: "w-4 h-4 text-green-500 mt-0.5 flex-shrink-0" }),
489
+ /* @__PURE__ */ jsx8("span", { className: "text-sm", children: feature })
490
+ ] }, index)) })
491
+ ] }),
492
+ displayedPrice.ui?.features && displayedPrice.ui.features.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
493
+ /* @__PURE__ */ jsx8("h4", { className: "font-medium text-sm text-muted-foreground uppercase tracking-wide", children: "This Plan" }),
494
+ /* @__PURE__ */ jsx8("ul", { className: "space-y-2", children: displayedPrice.ui.features.map(
495
+ (feature, index) => /* @__PURE__ */ jsxs4("li", { className: "flex items-start gap-2", children: [
496
+ /* @__PURE__ */ jsx8(Check2, { className: "w-4 h-4 text-blue-500 mt-0.5 flex-shrink-0" }),
497
+ /* @__PURE__ */ jsx8("span", { className: "text-sm", children: feature })
498
+ ] }, index)
499
+ ) })
500
+ ] }),
501
+ displayedPrice.ui?.limits && displayedPrice.ui.limits.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "space-y-2", children: [
502
+ /* @__PURE__ */ jsx8("h4", { className: "font-medium text-sm text-muted-foreground uppercase tracking-wide", children: "Usage Limits" }),
503
+ /* @__PURE__ */ jsx8("ul", { className: "space-y-1", children: displayedPrice.ui.limits.map(
504
+ (limit, index) => /* @__PURE__ */ jsx8(
505
+ "li",
506
+ {
507
+ className: "text-sm text-muted-foreground",
508
+ children: limit.text
509
+ },
510
+ index
511
+ )
512
+ ) })
513
+ ] })
514
+ ] })
515
+ ] }),
516
+ /* @__PURE__ */ jsx8(CardFooter, { children: /* @__PURE__ */ jsx8(
517
+ Button,
518
+ {
519
+ className: "w-full",
520
+ variant: isHighlighted ? "default" : "outline",
521
+ size: "lg",
522
+ onClick: () => onPriceSelect?.(displayedPrice.id, product.id),
523
+ children: ui.cta_text || "Choose Plan"
524
+ }
525
+ ) })
526
+ ]
527
+ }
528
+ )
529
+ ]
530
+ }
531
+ );
532
+ }
533
+ var CARD_WIDTH = 320;
534
+ var GAP = 24;
535
+ function PricingTable({
536
+ products,
537
+ selectedPriceId,
538
+ onPriceSelect,
539
+ className,
540
+ showPricingToggle = false,
541
+ defaultInterval = "month"
542
+ }) {
543
+ const [selectedInterval, setSelectedInterval] = React7.useState(defaultInterval);
544
+ const [carouselIndex, setCarouselIndex] = React7.useState(0);
545
+ const sortedProducts = React7.useMemo(
546
+ () => [...products].sort(
547
+ (a, b) => (a.ui?.sort_order ?? 999) - (b.ui?.sort_order ?? 999)
548
+ ),
549
+ [products]
550
+ );
551
+ const hasMultipleIntervals = React7.useMemo(
552
+ () => products.some(
553
+ (p) => new Set(p.prices.map((price) => price.interval)).size > 1
554
+ ),
555
+ [products]
556
+ );
557
+ const getDisplayedPrice = (product) => product.prices.find((p) => p.interval === selectedInterval) || product.prices[0];
558
+ const renderCard = (product) => {
559
+ const displayedPrice = getDisplayedPrice(product);
560
+ return /* @__PURE__ */ jsx8(
561
+ PricingCard,
562
+ {
563
+ product,
564
+ displayedPrice,
565
+ isSelected: selectedPriceId === displayedPrice.id,
566
+ onPriceSelect
567
+ }
568
+ );
569
+ };
570
+ const desktopCarousel = /* @__PURE__ */ jsxs4("div", { className: "relative max-w-7xl mx-auto", children: [
571
+ /* @__PURE__ */ jsx8(
572
+ Button,
573
+ {
574
+ variant: "ghost",
575
+ size: "icon",
576
+ className: "absolute left-0 top-1/2 transform -translate-y-1/2 -translate-x-4 z-10 bg-white shadow-lg border hover:bg-gray-50 disabled:opacity-50",
577
+ onClick: () => setCarouselIndex(Math.max(0, carouselIndex - 1)),
578
+ disabled: carouselIndex === 0,
579
+ children: /* @__PURE__ */ jsx8(ChevronLeft, { className: "w-4 h-4" })
580
+ }
581
+ ),
582
+ /* @__PURE__ */ jsx8(
583
+ "div",
584
+ {
585
+ className: "overflow-hidden mx-auto",
586
+ style: { width: `${3 * CARD_WIDTH + 2 * GAP}px` },
587
+ children: /* @__PURE__ */ jsx8(
588
+ "div",
589
+ {
590
+ className: "flex items-stretch transition-transform duration-300 ease-in-out",
591
+ style: {
592
+ transform: `translateX(-${carouselIndex * (CARD_WIDTH + GAP)}px)`,
593
+ gap: `${GAP}px`
594
+ },
595
+ children: sortedProducts.map((p) => /* @__PURE__ */ jsx8(
596
+ "div",
597
+ {
598
+ style: { width: `${CARD_WIDTH}px` },
599
+ className: "flex-shrink-0",
600
+ children: renderCard(p)
601
+ },
602
+ p.id
603
+ ))
604
+ }
605
+ )
606
+ }
607
+ ),
608
+ /* @__PURE__ */ jsx8(
609
+ Button,
610
+ {
611
+ variant: "ghost",
612
+ size: "icon",
613
+ className: "absolute right-0 top-1/2 transform -translate-y-1/2 translate-x-4 z-10 bg-white shadow-lg border hover:bg-gray-50 disabled:opacity-50",
614
+ onClick: () => setCarouselIndex(
615
+ Math.min(sortedProducts.length - 3, carouselIndex + 1)
616
+ ),
617
+ disabled: carouselIndex >= sortedProducts.length - 3,
618
+ children: /* @__PURE__ */ jsx8(ChevronRight, { className: "w-4 h-4" })
619
+ }
620
+ ),
621
+ /* @__PURE__ */ jsx8("div", { className: "flex justify-center mt-6 space-x-2", children: Array.from(
622
+ { length: Math.max(1, sortedProducts.length - 2) },
623
+ (_, i) => /* @__PURE__ */ jsx8(
624
+ "button",
625
+ {
626
+ onClick: () => setCarouselIndex(i),
627
+ className: cn(
628
+ "w-2 h-2 rounded-full transition-colors",
629
+ i === carouselIndex ? "bg-primary" : "bg-gray-300"
630
+ ),
631
+ "aria-label": `Go to slide ${i + 1}`
632
+ },
633
+ i
634
+ )
635
+ ) })
636
+ ] });
637
+ const staticLayout = /* @__PURE__ */ jsx8(
638
+ "div",
639
+ {
640
+ className: "flex flex-row items-stretch justify-center",
641
+ style: { gap: `${GAP}px` },
642
+ children: sortedProducts.map((p) => /* @__PURE__ */ jsx8(
643
+ "div",
644
+ {
645
+ style: { width: `${CARD_WIDTH}px` },
646
+ className: "flex-shrink-0",
647
+ children: renderCard(p)
648
+ },
649
+ p.id
650
+ ))
651
+ }
652
+ );
653
+ return /* @__PURE__ */ jsxs4("div", { className: cn("w-full", className), children: [
654
+ showPricingToggle && hasMultipleIntervals && /* @__PURE__ */ jsx8("div", { className: "flex justify-center mb-4", children: /* @__PURE__ */ jsxs4("div", { className: "flex bg-gray-100 p-1 rounded-lg", children: [
655
+ /* @__PURE__ */ jsx8(
656
+ Button,
657
+ {
658
+ variant: selectedInterval === "month" ? "default" : "ghost",
659
+ size: "sm",
660
+ onClick: () => setSelectedInterval("month"),
661
+ className: "rounded-md",
662
+ children: "Monthly"
663
+ }
664
+ ),
665
+ /* @__PURE__ */ jsx8(
666
+ Button,
667
+ {
668
+ variant: selectedInterval === "year" ? "default" : "ghost",
669
+ size: "sm",
670
+ onClick: () => setSelectedInterval("year"),
671
+ className: "rounded-md",
672
+ children: "Yearly"
673
+ }
674
+ )
675
+ ] }) }),
676
+ /* @__PURE__ */ jsxs4("div", { className: "lg:hidden relative", children: [
677
+ /* @__PURE__ */ jsx8(
678
+ "div",
679
+ {
680
+ className: "overflow-hidden mx-auto",
681
+ style: { width: `${CARD_WIDTH}px` },
682
+ children: /* @__PURE__ */ jsx8(
683
+ "div",
684
+ {
685
+ className: "flex transition-transform duration-300 ease-in-out items-stretch",
686
+ style: {
687
+ transform: `translateX(-${carouselIndex * CARD_WIDTH}px)`
688
+ },
689
+ children: sortedProducts.map((product) => /* @__PURE__ */ jsx8(
690
+ "div",
691
+ {
692
+ className: "flex-shrink-0",
693
+ style: { width: `${CARD_WIDTH}px` },
694
+ children: renderCard(product)
695
+ },
696
+ product.id
697
+ ))
698
+ }
699
+ )
700
+ }
701
+ ),
702
+ sortedProducts.length > 1 && /* @__PURE__ */ jsxs4(Fragment, { children: [
703
+ /* @__PURE__ */ jsx8(
704
+ Button,
705
+ {
706
+ variant: "ghost",
707
+ size: "icon",
708
+ className: "absolute left-0 top-1/2 -translate-y-1/2 -translate-x-2 z-10 bg-white shadow-lg border hover:bg-gray-50 disabled:opacity-50",
709
+ onClick: () => setCarouselIndex((prev) => Math.max(0, prev - 1)),
710
+ disabled: carouselIndex === 0,
711
+ children: /* @__PURE__ */ jsx8(ChevronLeft, { className: "w-5 h-5" })
712
+ }
713
+ ),
714
+ /* @__PURE__ */ jsx8(
715
+ Button,
716
+ {
717
+ variant: "ghost",
718
+ size: "icon",
719
+ className: "absolute right-0 top-1/2 -translate-y-1/2 translate-x-2 z-10 bg-white shadow-lg border hover:bg-gray-50 disabled:opacity-50",
720
+ onClick: () => setCarouselIndex(
721
+ (prev) => Math.min(sortedProducts.length - 1, prev + 1)
722
+ ),
723
+ disabled: carouselIndex >= sortedProducts.length - 1,
724
+ children: /* @__PURE__ */ jsx8(ChevronRight, { className: "w-5 h-5" })
725
+ }
726
+ ),
727
+ /* @__PURE__ */ jsx8("div", { className: "flex justify-center mt-6 space-x-2", children: Array.from({ length: sortedProducts.length }, (_, i) => /* @__PURE__ */ jsx8(
728
+ "button",
729
+ {
730
+ onClick: () => setCarouselIndex(i),
731
+ className: cn(
732
+ "w-2 h-2 rounded-full transition-colors",
733
+ i === carouselIndex ? "bg-primary" : "bg-gray-300"
734
+ ),
735
+ "aria-label": `Go to slide ${i + 1}`
736
+ },
737
+ i
738
+ )) })
739
+ ] })
740
+ ] }),
741
+ /* @__PURE__ */ jsx8("div", { className: "hidden lg:block", children: sortedProducts.length <= 3 ? staticLayout : desktopCarousel })
742
+ ] });
743
+ }
744
+ export {
745
+ CustomFlowForm,
746
+ PricingTable,
747
+ SwitchActiveTenant
748
+ };