myoperator-ui 0.0.92 → 0.0.94
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 +1041 -723
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -265,12 +265,12 @@ async function getRegistry(prefix = "") {
|
|
|
265
265
|
files: [
|
|
266
266
|
{
|
|
267
267
|
name: "button.tsx",
|
|
268
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
269
|
-
import { Slot } from "@radix-ui/react-slot"
|
|
270
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
271
|
-
import { Loader2 } from "lucide-react"
|
|
268
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
269
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
270
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
271
|
+
import { Loader2 } from "lucide-react";
|
|
272
272
|
|
|
273
|
-
import { cn } from "../../lib/utils"
|
|
273
|
+
import { cn } from "../../lib/utils";
|
|
274
274
|
|
|
275
275
|
const buttonVariants = cva(
|
|
276
276
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#343E55] focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
@@ -279,12 +279,10 @@ const buttonVariants = cva(
|
|
|
279
279
|
variant: {
|
|
280
280
|
default: "bg-[#343E55] text-white hover:bg-[#2F384D]",
|
|
281
281
|
primary: "bg-[#343E55] text-white hover:bg-[#2F384D]",
|
|
282
|
-
destructive:
|
|
283
|
-
"bg-[#F04438] text-white hover:bg-[#D92D20]",
|
|
282
|
+
destructive: "bg-[#F04438] text-white hover:bg-[#D92D20]",
|
|
284
283
|
outline:
|
|
285
284
|
"border border-[#343E55] bg-transparent text-[#343E55] hover:bg-[#EBECEE]",
|
|
286
|
-
secondary:
|
|
287
|
-
"bg-[#EBECEE] text-[#343E55] hover:bg-[#D5D7DA]",
|
|
285
|
+
secondary: "bg-[#EBECEE] text-[#343E55] hover:bg-[#D5D7DA]",
|
|
288
286
|
ghost: "text-[#717680] hover:bg-[#F5F5F5] hover:text-[#181D27]",
|
|
289
287
|
link: "text-[#343E55] underline-offset-4 hover:underline",
|
|
290
288
|
dashed:
|
|
@@ -304,7 +302,7 @@ const buttonVariants = cva(
|
|
|
304
302
|
size: "default",
|
|
305
303
|
},
|
|
306
304
|
}
|
|
307
|
-
)
|
|
305
|
+
);
|
|
308
306
|
|
|
309
307
|
/**
|
|
310
308
|
* Button component for user interactions.
|
|
@@ -317,35 +315,39 @@ const buttonVariants = cva(
|
|
|
317
315
|
* \`\`\`
|
|
318
316
|
*/
|
|
319
317
|
export interface ButtonProps
|
|
320
|
-
extends
|
|
318
|
+
extends
|
|
319
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
321
320
|
VariantProps<typeof buttonVariants> {
|
|
322
321
|
/** Render as child element using Radix Slot */
|
|
323
|
-
asChild?: boolean
|
|
322
|
+
asChild?: boolean;
|
|
324
323
|
/** Icon displayed on the left side of the button text */
|
|
325
|
-
leftIcon?: React.ReactNode
|
|
324
|
+
leftIcon?: React.ReactNode;
|
|
326
325
|
/** Icon displayed on the right side of the button text */
|
|
327
|
-
rightIcon?: React.ReactNode
|
|
326
|
+
rightIcon?: React.ReactNode;
|
|
328
327
|
/** Shows loading spinner and disables button */
|
|
329
|
-
loading?: boolean
|
|
328
|
+
loading?: boolean;
|
|
330
329
|
/** Text shown during loading state */
|
|
331
|
-
loadingText?: string
|
|
330
|
+
loadingText?: string;
|
|
332
331
|
}
|
|
333
332
|
|
|
334
333
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
335
|
-
(
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
334
|
+
(
|
|
335
|
+
{
|
|
336
|
+
className,
|
|
337
|
+
variant,
|
|
338
|
+
size,
|
|
339
|
+
asChild = false,
|
|
340
|
+
leftIcon,
|
|
341
|
+
rightIcon,
|
|
342
|
+
loading = false,
|
|
343
|
+
loadingText,
|
|
344
|
+
children,
|
|
345
|
+
disabled,
|
|
346
|
+
...props
|
|
347
|
+
},
|
|
348
|
+
ref
|
|
349
|
+
) => {
|
|
350
|
+
const Comp = asChild ? Slot : "button";
|
|
349
351
|
|
|
350
352
|
return (
|
|
351
353
|
<Comp
|
|
@@ -367,12 +369,12 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
|
367
369
|
</>
|
|
368
370
|
)}
|
|
369
371
|
</Comp>
|
|
370
|
-
)
|
|
372
|
+
);
|
|
371
373
|
}
|
|
372
|
-
)
|
|
373
|
-
Button.displayName = "Button"
|
|
374
|
+
);
|
|
375
|
+
Button.displayName = "Button";
|
|
374
376
|
|
|
375
|
-
export { Button, buttonVariants }
|
|
377
|
+
export { Button, buttonVariants };
|
|
376
378
|
`, prefix)
|
|
377
379
|
}
|
|
378
380
|
]
|
|
@@ -389,11 +391,11 @@ export { Button, buttonVariants }
|
|
|
389
391
|
files: [
|
|
390
392
|
{
|
|
391
393
|
name: "badge.tsx",
|
|
392
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
393
|
-
import { Slot } from "@radix-ui/react-slot"
|
|
394
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
394
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
395
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
396
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
395
397
|
|
|
396
|
-
import { cn } from "../../lib/utils"
|
|
398
|
+
import { cn } from "../../lib/utils";
|
|
397
399
|
|
|
398
400
|
/**
|
|
399
401
|
* Badge variants for status indicators.
|
|
@@ -426,7 +428,7 @@ const badgeVariants = cva(
|
|
|
426
428
|
size: "default",
|
|
427
429
|
},
|
|
428
430
|
}
|
|
429
|
-
)
|
|
431
|
+
);
|
|
430
432
|
|
|
431
433
|
/**
|
|
432
434
|
* Badge component for displaying status indicators.
|
|
@@ -446,19 +448,32 @@ const badgeVariants = cva(
|
|
|
446
448
|
* \`\`\`
|
|
447
449
|
*/
|
|
448
450
|
export interface BadgeProps
|
|
449
|
-
extends
|
|
451
|
+
extends
|
|
452
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
450
453
|
VariantProps<typeof badgeVariants> {
|
|
451
454
|
/** Icon displayed on the left side of the badge text */
|
|
452
|
-
leftIcon?: React.ReactNode
|
|
455
|
+
leftIcon?: React.ReactNode;
|
|
453
456
|
/** Icon displayed on the right side of the badge text */
|
|
454
|
-
rightIcon?: React.ReactNode
|
|
457
|
+
rightIcon?: React.ReactNode;
|
|
455
458
|
/** Render as child element using Radix Slot */
|
|
456
|
-
asChild?: boolean
|
|
459
|
+
asChild?: boolean;
|
|
457
460
|
}
|
|
458
461
|
|
|
459
462
|
const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
|
|
460
|
-
(
|
|
461
|
-
|
|
463
|
+
(
|
|
464
|
+
{
|
|
465
|
+
className,
|
|
466
|
+
variant,
|
|
467
|
+
size,
|
|
468
|
+
leftIcon,
|
|
469
|
+
rightIcon,
|
|
470
|
+
asChild = false,
|
|
471
|
+
children,
|
|
472
|
+
...props
|
|
473
|
+
},
|
|
474
|
+
ref
|
|
475
|
+
) => {
|
|
476
|
+
const Comp = asChild ? Slot : "div";
|
|
462
477
|
|
|
463
478
|
// When using asChild, we can't wrap the child with extra elements
|
|
464
479
|
// The child must receive the className and ref directly
|
|
@@ -471,7 +486,7 @@ const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
|
|
|
471
486
|
>
|
|
472
487
|
{children}
|
|
473
488
|
</Comp>
|
|
474
|
-
)
|
|
489
|
+
);
|
|
475
490
|
}
|
|
476
491
|
|
|
477
492
|
return (
|
|
@@ -484,12 +499,210 @@ const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
|
|
|
484
499
|
{children}
|
|
485
500
|
{rightIcon && <span className="[&_svg]:size-3">{rightIcon}</span>}
|
|
486
501
|
</Comp>
|
|
487
|
-
)
|
|
502
|
+
);
|
|
488
503
|
}
|
|
489
|
-
)
|
|
490
|
-
Badge.displayName = "Badge"
|
|
504
|
+
);
|
|
505
|
+
Badge.displayName = "Badge";
|
|
506
|
+
|
|
507
|
+
export { Badge, badgeVariants };
|
|
508
|
+
`, prefix)
|
|
509
|
+
}
|
|
510
|
+
]
|
|
511
|
+
},
|
|
512
|
+
"typography": {
|
|
513
|
+
name: "typography",
|
|
514
|
+
description: "A semantic typography component with kind, variant, color, alignment, and truncation support",
|
|
515
|
+
dependencies: [
|
|
516
|
+
"clsx",
|
|
517
|
+
"tailwind-merge"
|
|
518
|
+
],
|
|
519
|
+
files: [
|
|
520
|
+
{
|
|
521
|
+
name: "typography.tsx",
|
|
522
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
523
|
+
import { cn } from "../../lib/utils";
|
|
524
|
+
|
|
525
|
+
// =============================================================================
|
|
526
|
+
// TYPES
|
|
527
|
+
// =============================================================================
|
|
528
|
+
|
|
529
|
+
export type Kind = "display" | "headline" | "title" | "label" | "body";
|
|
530
|
+
export type Variant = "large" | "medium" | "small";
|
|
531
|
+
export type Color =
|
|
532
|
+
| "primary"
|
|
533
|
+
| "secondary"
|
|
534
|
+
| "muted"
|
|
535
|
+
| "placeholder"
|
|
536
|
+
| "link"
|
|
537
|
+
| "inverted"
|
|
538
|
+
| "error"
|
|
539
|
+
| "success";
|
|
540
|
+
export type Align = "left" | "center" | "right";
|
|
541
|
+
|
|
542
|
+
type Key = \`\${Kind}-\${Variant}\`;
|
|
543
|
+
|
|
544
|
+
// =============================================================================
|
|
545
|
+
// MAPPINGS
|
|
546
|
+
// =============================================================================
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Maps kind-variant combinations to semantic HTML tags
|
|
550
|
+
*/
|
|
551
|
+
const mapTagName: { [key in Key]: keyof JSX.IntrinsicElements } = {
|
|
552
|
+
"display-large": "h4",
|
|
553
|
+
"display-medium": "h4",
|
|
554
|
+
"display-small": "h4",
|
|
555
|
+
"headline-large": "h1",
|
|
556
|
+
"headline-medium": "h2",
|
|
557
|
+
"headline-small": "h3",
|
|
558
|
+
"title-large": "h5",
|
|
559
|
+
"title-medium": "h5",
|
|
560
|
+
"title-small": "h5",
|
|
561
|
+
"label-large": "label",
|
|
562
|
+
"label-medium": "label",
|
|
563
|
+
"label-small": "label",
|
|
564
|
+
"body-large": "span",
|
|
565
|
+
"body-medium": "span",
|
|
566
|
+
"body-small": "span",
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Maps kind-variant combinations to Tailwind typography classes
|
|
571
|
+
*/
|
|
572
|
+
const mapClassName: { [key in Key]: string } = {
|
|
573
|
+
"display-large": "text-[57px] leading-[64px] font-normal",
|
|
574
|
+
"display-medium": "text-[45px] leading-[52px] font-normal",
|
|
575
|
+
"display-small": "text-[36px] leading-[44px] font-normal",
|
|
576
|
+
"headline-large": "text-[32px] leading-[40px] font-semibold",
|
|
577
|
+
"headline-medium": "text-[28px] leading-[36px] font-semibold",
|
|
578
|
+
"headline-small": "text-[24px] leading-[32px] font-semibold",
|
|
579
|
+
"title-large": "text-lg leading-[22px] font-semibold",
|
|
580
|
+
"title-medium": "text-base leading-5 font-semibold",
|
|
581
|
+
"title-small": "text-sm leading-[18px] font-semibold",
|
|
582
|
+
"label-large": "text-sm leading-5 font-semibold",
|
|
583
|
+
"label-medium": "text-xs leading-4 font-semibold",
|
|
584
|
+
"label-small": "text-[10px] leading-[14px] font-semibold",
|
|
585
|
+
"body-large": "text-base leading-5 font-normal",
|
|
586
|
+
"body-medium": "text-sm leading-[18px] font-normal",
|
|
587
|
+
"body-small": "text-xs leading-4 font-normal",
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
/**
|
|
591
|
+
* Maps color variants to Tailwind text color classes
|
|
592
|
+
*/
|
|
593
|
+
const mapColorClassName: { [key in Color]: string } = {
|
|
594
|
+
primary: "text-[#181D27]",
|
|
595
|
+
secondary: "text-[#343E55]",
|
|
596
|
+
muted: "text-[#717680]",
|
|
597
|
+
placeholder: "text-[#A2A6B1]",
|
|
598
|
+
link: "text-[#4275D6]",
|
|
599
|
+
inverted: "text-white",
|
|
600
|
+
error: "text-[#F04438]",
|
|
601
|
+
success: "text-[#17B26A]",
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Maps alignment to Tailwind text alignment classes
|
|
606
|
+
*/
|
|
607
|
+
const mapAlignClassName: { [key in Align]: string } = {
|
|
608
|
+
left: "text-left",
|
|
609
|
+
center: "text-center",
|
|
610
|
+
right: "text-right",
|
|
611
|
+
};
|
|
491
612
|
|
|
492
|
-
|
|
613
|
+
// =============================================================================
|
|
614
|
+
// COMPONENT
|
|
615
|
+
// =============================================================================
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Typography component props
|
|
619
|
+
*/
|
|
620
|
+
export interface TypographyProps extends React.HTMLAttributes<HTMLElement> {
|
|
621
|
+
/** Text content */
|
|
622
|
+
children: React.ReactNode;
|
|
623
|
+
/** Typography kind - determines base styling and semantic tag */
|
|
624
|
+
kind?: Kind;
|
|
625
|
+
/** Size variant */
|
|
626
|
+
variant?: Variant;
|
|
627
|
+
/** Text color */
|
|
628
|
+
color?: Color;
|
|
629
|
+
/** Text alignment */
|
|
630
|
+
align?: Align;
|
|
631
|
+
/** Enable text truncation with ellipsis */
|
|
632
|
+
truncate?: boolean;
|
|
633
|
+
/** Override the default HTML tag */
|
|
634
|
+
tag?: keyof JSX.IntrinsicElements;
|
|
635
|
+
/** For label elements - associates with form input */
|
|
636
|
+
htmlFor?: string;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* A semantic typography component for consistent text styling.
|
|
641
|
+
*
|
|
642
|
+
* @example
|
|
643
|
+
* \`\`\`tsx
|
|
644
|
+
* // Basic usage
|
|
645
|
+
* <Typography kind="headline" variant="large">Page Title</Typography>
|
|
646
|
+
*
|
|
647
|
+
* // With color
|
|
648
|
+
* <Typography kind="body" color="muted">Helper text</Typography>
|
|
649
|
+
*
|
|
650
|
+
* // Form label
|
|
651
|
+
* <Typography kind="label" variant="medium" htmlFor="email">Email</Typography>
|
|
652
|
+
*
|
|
653
|
+
* // Truncated text
|
|
654
|
+
* <Typography truncate>Very long text that will be truncated...</Typography>
|
|
655
|
+
* \`\`\`
|
|
656
|
+
*/
|
|
657
|
+
const Typography = React.forwardRef<HTMLElement, TypographyProps>(
|
|
658
|
+
(
|
|
659
|
+
{
|
|
660
|
+
children,
|
|
661
|
+
kind = "body",
|
|
662
|
+
variant = "medium",
|
|
663
|
+
color,
|
|
664
|
+
align,
|
|
665
|
+
truncate = false,
|
|
666
|
+
className,
|
|
667
|
+
tag,
|
|
668
|
+
htmlFor,
|
|
669
|
+
...props
|
|
670
|
+
},
|
|
671
|
+
ref
|
|
672
|
+
) => {
|
|
673
|
+
const key: Key = \`\${kind}-\${variant}\`;
|
|
674
|
+
const Tag = tag || mapTagName[key];
|
|
675
|
+
|
|
676
|
+
const classes = cn(
|
|
677
|
+
"m-0", // Reset margin
|
|
678
|
+
mapClassName[key],
|
|
679
|
+
color && mapColorClassName[color],
|
|
680
|
+
align && mapAlignClassName[align],
|
|
681
|
+
truncate && "truncate",
|
|
682
|
+
className
|
|
683
|
+
);
|
|
684
|
+
|
|
685
|
+
return (
|
|
686
|
+
<Tag
|
|
687
|
+
ref={ref as React.Ref<HTMLElement>}
|
|
688
|
+
className={classes}
|
|
689
|
+
htmlFor={Tag === "label" ? htmlFor : undefined}
|
|
690
|
+
{...props}
|
|
691
|
+
>
|
|
692
|
+
{children}
|
|
693
|
+
</Tag>
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
);
|
|
697
|
+
Typography.displayName = "Typography";
|
|
698
|
+
|
|
699
|
+
export {
|
|
700
|
+
Typography,
|
|
701
|
+
mapTagName,
|
|
702
|
+
mapClassName,
|
|
703
|
+
mapColorClassName,
|
|
704
|
+
mapAlignClassName,
|
|
705
|
+
};
|
|
493
706
|
`, prefix)
|
|
494
707
|
}
|
|
495
708
|
]
|
|
@@ -505,10 +718,10 @@ export { Badge, badgeVariants }
|
|
|
505
718
|
files: [
|
|
506
719
|
{
|
|
507
720
|
name: "input.tsx",
|
|
508
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
509
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
721
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
722
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
510
723
|
|
|
511
|
-
import { cn } from "../../lib/utils"
|
|
724
|
+
import { cn } from "../../lib/utils";
|
|
512
725
|
|
|
513
726
|
/**
|
|
514
727
|
* Input variants for different visual states
|
|
@@ -518,15 +731,17 @@ const inputVariants = cva(
|
|
|
518
731
|
{
|
|
519
732
|
variants: {
|
|
520
733
|
state: {
|
|
521
|
-
default:
|
|
522
|
-
|
|
734
|
+
default:
|
|
735
|
+
"border border-[#E9EAEB] focus:outline-none focus:border-[#2BBCCA]/50 focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
|
|
736
|
+
error:
|
|
737
|
+
"border border-[#F04438]/40 focus:outline-none focus:border-[#F04438]/60 focus:shadow-[0_0_0_1px_rgba(240,68,56,0.1)]",
|
|
523
738
|
},
|
|
524
739
|
},
|
|
525
740
|
defaultVariants: {
|
|
526
741
|
state: "default",
|
|
527
742
|
},
|
|
528
743
|
}
|
|
529
|
-
)
|
|
744
|
+
);
|
|
530
745
|
|
|
531
746
|
/**
|
|
532
747
|
* A flexible input component for text entry with state variants.
|
|
@@ -539,7 +754,8 @@ const inputVariants = cva(
|
|
|
539
754
|
* \`\`\`
|
|
540
755
|
*/
|
|
541
756
|
export interface InputProps
|
|
542
|
-
extends
|
|
757
|
+
extends
|
|
758
|
+
Omit<React.ComponentProps<"input">, "size">,
|
|
543
759
|
VariantProps<typeof inputVariants> {}
|
|
544
760
|
|
|
545
761
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
@@ -551,12 +767,12 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
551
767
|
ref={ref}
|
|
552
768
|
{...props}
|
|
553
769
|
/>
|
|
554
|
-
)
|
|
770
|
+
);
|
|
555
771
|
}
|
|
556
|
-
)
|
|
557
|
-
Input.displayName = "Input"
|
|
772
|
+
);
|
|
773
|
+
Input.displayName = "Input";
|
|
558
774
|
|
|
559
|
-
export { Input, inputVariants }
|
|
775
|
+
export { Input, inputVariants };
|
|
560
776
|
`, prefix)
|
|
561
777
|
}
|
|
562
778
|
]
|
|
@@ -574,12 +790,12 @@ export { Input, inputVariants }
|
|
|
574
790
|
files: [
|
|
575
791
|
{
|
|
576
792
|
name: "select.tsx",
|
|
577
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
578
|
-
import * as SelectPrimitive from "@radix-ui/react-select"
|
|
579
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
580
|
-
import { Check, ChevronDown, ChevronUp } from "lucide-react"
|
|
793
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
794
|
+
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
795
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
796
|
+
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
|
581
797
|
|
|
582
|
-
import { cn } from "../../lib/utils"
|
|
798
|
+
import { cn } from "../../lib/utils";
|
|
583
799
|
|
|
584
800
|
/**
|
|
585
801
|
* SelectTrigger variants matching TextField styling
|
|
@@ -589,24 +805,27 @@ const selectTriggerVariants = cva(
|
|
|
589
805
|
{
|
|
590
806
|
variants: {
|
|
591
807
|
state: {
|
|
592
|
-
default:
|
|
593
|
-
|
|
808
|
+
default:
|
|
809
|
+
"border border-[#E9EAEB] focus:outline-none focus:border-[#2BBCCA]/50 focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
|
|
810
|
+
error:
|
|
811
|
+
"border border-[#F04438]/40 focus:outline-none focus:border-[#F04438]/60 focus:shadow-[0_0_0_1px_rgba(240,68,56,0.1)]",
|
|
594
812
|
},
|
|
595
813
|
},
|
|
596
814
|
defaultVariants: {
|
|
597
815
|
state: "default",
|
|
598
816
|
},
|
|
599
817
|
}
|
|
600
|
-
)
|
|
818
|
+
);
|
|
601
819
|
|
|
602
|
-
const Select = SelectPrimitive.Root
|
|
820
|
+
const Select = SelectPrimitive.Root;
|
|
603
821
|
|
|
604
|
-
const SelectGroup = SelectPrimitive.Group
|
|
822
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
605
823
|
|
|
606
|
-
const SelectValue = SelectPrimitive.Value
|
|
824
|
+
const SelectValue = SelectPrimitive.Value;
|
|
607
825
|
|
|
608
826
|
export interface SelectTriggerProps
|
|
609
|
-
extends
|
|
827
|
+
extends
|
|
828
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>,
|
|
610
829
|
VariantProps<typeof selectTriggerVariants> {}
|
|
611
830
|
|
|
612
831
|
const SelectTrigger = React.forwardRef<
|
|
@@ -623,8 +842,8 @@ const SelectTrigger = React.forwardRef<
|
|
|
623
842
|
<ChevronDown className="size-4 text-[#717680] opacity-70" />
|
|
624
843
|
</SelectPrimitive.Icon>
|
|
625
844
|
</SelectPrimitive.Trigger>
|
|
626
|
-
))
|
|
627
|
-
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
|
845
|
+
));
|
|
846
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
628
847
|
|
|
629
848
|
const SelectScrollUpButton = React.forwardRef<
|
|
630
849
|
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
|
@@ -640,8 +859,8 @@ const SelectScrollUpButton = React.forwardRef<
|
|
|
640
859
|
>
|
|
641
860
|
<ChevronUp className="size-4 text-[#717680]" />
|
|
642
861
|
</SelectPrimitive.ScrollUpButton>
|
|
643
|
-
))
|
|
644
|
-
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
|
862
|
+
));
|
|
863
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
645
864
|
|
|
646
865
|
const SelectScrollDownButton = React.forwardRef<
|
|
647
866
|
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
|
@@ -657,8 +876,9 @@ const SelectScrollDownButton = React.forwardRef<
|
|
|
657
876
|
>
|
|
658
877
|
<ChevronDown className="size-4 text-[#717680]" />
|
|
659
878
|
</SelectPrimitive.ScrollDownButton>
|
|
660
|
-
))
|
|
661
|
-
SelectScrollDownButton.displayName =
|
|
879
|
+
));
|
|
880
|
+
SelectScrollDownButton.displayName =
|
|
881
|
+
SelectPrimitive.ScrollDownButton.displayName;
|
|
662
882
|
|
|
663
883
|
const SelectContent = React.forwardRef<
|
|
664
884
|
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
@@ -694,8 +914,8 @@ const SelectContent = React.forwardRef<
|
|
|
694
914
|
<SelectScrollDownButton />
|
|
695
915
|
</SelectPrimitive.Content>
|
|
696
916
|
</SelectPrimitive.Portal>
|
|
697
|
-
))
|
|
698
|
-
SelectContent.displayName = SelectPrimitive.Content.displayName
|
|
917
|
+
));
|
|
918
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
699
919
|
|
|
700
920
|
const SelectLabel = React.forwardRef<
|
|
701
921
|
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
@@ -706,8 +926,8 @@ const SelectLabel = React.forwardRef<
|
|
|
706
926
|
className={cn("px-4 py-1.5 text-xs font-medium text-[#717680]", className)}
|
|
707
927
|
{...props}
|
|
708
928
|
/>
|
|
709
|
-
))
|
|
710
|
-
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
|
929
|
+
));
|
|
930
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
711
931
|
|
|
712
932
|
const SelectItem = React.forwardRef<
|
|
713
933
|
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
@@ -730,8 +950,8 @@ const SelectItem = React.forwardRef<
|
|
|
730
950
|
</span>
|
|
731
951
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
732
952
|
</SelectPrimitive.Item>
|
|
733
|
-
))
|
|
734
|
-
SelectItem.displayName = SelectPrimitive.Item.displayName
|
|
953
|
+
));
|
|
954
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
735
955
|
|
|
736
956
|
const SelectSeparator = React.forwardRef<
|
|
737
957
|
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
@@ -742,8 +962,8 @@ const SelectSeparator = React.forwardRef<
|
|
|
742
962
|
className={cn("-mx-1 my-1 h-px bg-[#E9EAEB]", className)}
|
|
743
963
|
{...props}
|
|
744
964
|
/>
|
|
745
|
-
))
|
|
746
|
-
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
|
965
|
+
));
|
|
966
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
747
967
|
|
|
748
968
|
export {
|
|
749
969
|
Select,
|
|
@@ -757,7 +977,7 @@ export {
|
|
|
757
977
|
SelectScrollUpButton,
|
|
758
978
|
SelectScrollDownButton,
|
|
759
979
|
selectTriggerVariants,
|
|
760
|
-
}
|
|
980
|
+
};
|
|
761
981
|
`, prefix)
|
|
762
982
|
}
|
|
763
983
|
]
|
|
@@ -775,12 +995,12 @@ export {
|
|
|
775
995
|
files: [
|
|
776
996
|
{
|
|
777
997
|
name: "checkbox.tsx",
|
|
778
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
779
|
-
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
|
780
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
781
|
-
import { Check, Minus } from "lucide-react"
|
|
998
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
999
|
+
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
1000
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
1001
|
+
import { Check, Minus } from "lucide-react";
|
|
782
1002
|
|
|
783
|
-
import { cn } from "../../lib/utils"
|
|
1003
|
+
import { cn } from "../../lib/utils";
|
|
784
1004
|
|
|
785
1005
|
/**
|
|
786
1006
|
* Checkbox box variants (the outer container)
|
|
@@ -799,7 +1019,7 @@ const checkboxVariants = cva(
|
|
|
799
1019
|
size: "default",
|
|
800
1020
|
},
|
|
801
1021
|
}
|
|
802
|
-
)
|
|
1022
|
+
);
|
|
803
1023
|
|
|
804
1024
|
/**
|
|
805
1025
|
* Icon size variants based on checkbox size
|
|
@@ -815,7 +1035,7 @@ const iconSizeVariants = cva("", {
|
|
|
815
1035
|
defaultVariants: {
|
|
816
1036
|
size: "default",
|
|
817
1037
|
},
|
|
818
|
-
})
|
|
1038
|
+
});
|
|
819
1039
|
|
|
820
1040
|
/**
|
|
821
1041
|
* Label text size variants
|
|
@@ -831,9 +1051,9 @@ const labelSizeVariants = cva("", {
|
|
|
831
1051
|
defaultVariants: {
|
|
832
1052
|
size: "default",
|
|
833
1053
|
},
|
|
834
|
-
})
|
|
1054
|
+
});
|
|
835
1055
|
|
|
836
|
-
export type CheckedState = boolean | "indeterminate"
|
|
1056
|
+
export type CheckedState = boolean | "indeterminate";
|
|
837
1057
|
|
|
838
1058
|
/**
|
|
839
1059
|
* A tri-state checkbox component with label support. Built on Radix UI Checkbox primitive.
|
|
@@ -848,18 +1068,22 @@ export type CheckedState = boolean | "indeterminate"
|
|
|
848
1068
|
* \`\`\`
|
|
849
1069
|
*/
|
|
850
1070
|
export interface CheckboxProps
|
|
851
|
-
extends
|
|
1071
|
+
extends
|
|
1072
|
+
Omit<
|
|
1073
|
+
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>,
|
|
1074
|
+
"onChange"
|
|
1075
|
+
>,
|
|
852
1076
|
VariantProps<typeof checkboxVariants> {
|
|
853
1077
|
/** Optional label text */
|
|
854
|
-
label?: string
|
|
1078
|
+
label?: string;
|
|
855
1079
|
/** Position of the label */
|
|
856
|
-
labelPosition?: "left" | "right"
|
|
1080
|
+
labelPosition?: "left" | "right";
|
|
857
1081
|
/** Class name applied to the checkbox element */
|
|
858
|
-
checkboxClassName?: string
|
|
1082
|
+
checkboxClassName?: string;
|
|
859
1083
|
/** Class name applied to the label element */
|
|
860
|
-
labelClassName?: string
|
|
1084
|
+
labelClassName?: string;
|
|
861
1085
|
/** If true, uses separate labels with htmlFor/id association instead of wrapping the input. Requires id prop. */
|
|
862
|
-
separateLabel?: boolean
|
|
1086
|
+
separateLabel?: boolean;
|
|
863
1087
|
}
|
|
864
1088
|
|
|
865
1089
|
const Checkbox = React.forwardRef<
|
|
@@ -902,7 +1126,7 @@ const Checkbox = React.forwardRef<
|
|
|
902
1126
|
)}
|
|
903
1127
|
</CheckboxPrimitive.Indicator>
|
|
904
1128
|
</CheckboxPrimitive.Root>
|
|
905
|
-
)
|
|
1129
|
+
);
|
|
906
1130
|
|
|
907
1131
|
if (label) {
|
|
908
1132
|
// separateLabel mode: use htmlFor/id association instead of wrapping
|
|
@@ -937,33 +1161,52 @@ const Checkbox = React.forwardRef<
|
|
|
937
1161
|
</label>
|
|
938
1162
|
)}
|
|
939
1163
|
</div>
|
|
940
|
-
)
|
|
1164
|
+
);
|
|
941
1165
|
}
|
|
942
1166
|
|
|
943
1167
|
// Default: wrapping label
|
|
944
1168
|
return (
|
|
945
|
-
<label
|
|
1169
|
+
<label
|
|
1170
|
+
className={cn(
|
|
1171
|
+
"inline-flex items-center gap-2 cursor-pointer",
|
|
1172
|
+
disabled && "cursor-not-allowed"
|
|
1173
|
+
)}
|
|
1174
|
+
>
|
|
946
1175
|
{labelPosition === "left" && (
|
|
947
|
-
<span
|
|
1176
|
+
<span
|
|
1177
|
+
className={cn(
|
|
1178
|
+
labelSizeVariants({ size }),
|
|
1179
|
+
"text-[#181D27]",
|
|
1180
|
+
disabled && "opacity-50",
|
|
1181
|
+
labelClassName
|
|
1182
|
+
)}
|
|
1183
|
+
>
|
|
948
1184
|
{label}
|
|
949
1185
|
</span>
|
|
950
1186
|
)}
|
|
951
1187
|
{checkbox}
|
|
952
1188
|
{labelPosition === "right" && (
|
|
953
|
-
<span
|
|
1189
|
+
<span
|
|
1190
|
+
className={cn(
|
|
1191
|
+
labelSizeVariants({ size }),
|
|
1192
|
+
"text-[#181D27]",
|
|
1193
|
+
disabled && "opacity-50",
|
|
1194
|
+
labelClassName
|
|
1195
|
+
)}
|
|
1196
|
+
>
|
|
954
1197
|
{label}
|
|
955
1198
|
</span>
|
|
956
1199
|
)}
|
|
957
1200
|
</label>
|
|
958
|
-
)
|
|
1201
|
+
);
|
|
959
1202
|
}
|
|
960
1203
|
|
|
961
|
-
return checkbox
|
|
1204
|
+
return checkbox;
|
|
962
1205
|
}
|
|
963
|
-
)
|
|
964
|
-
Checkbox.displayName = "Checkbox"
|
|
1206
|
+
);
|
|
1207
|
+
Checkbox.displayName = "Checkbox";
|
|
965
1208
|
|
|
966
|
-
export { Checkbox, checkboxVariants }
|
|
1209
|
+
export { Checkbox, checkboxVariants };
|
|
967
1210
|
`, prefix)
|
|
968
1211
|
}
|
|
969
1212
|
]
|
|
@@ -980,11 +1223,11 @@ export { Checkbox, checkboxVariants }
|
|
|
980
1223
|
files: [
|
|
981
1224
|
{
|
|
982
1225
|
name: "switch.tsx",
|
|
983
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
984
|
-
import * as SwitchPrimitives from "@radix-ui/react-switch"
|
|
985
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1226
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
1227
|
+
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
|
1228
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
986
1229
|
|
|
987
|
-
import { cn } from "../../lib/utils"
|
|
1230
|
+
import { cn } from "../../lib/utils";
|
|
988
1231
|
|
|
989
1232
|
/**
|
|
990
1233
|
* Switch track variants (the outer container)
|
|
@@ -1003,7 +1246,7 @@ const switchVariants = cva(
|
|
|
1003
1246
|
size: "default",
|
|
1004
1247
|
},
|
|
1005
1248
|
}
|
|
1006
|
-
)
|
|
1249
|
+
);
|
|
1007
1250
|
|
|
1008
1251
|
/**
|
|
1009
1252
|
* Switch thumb variants (the sliding circle)
|
|
@@ -1022,7 +1265,7 @@ const switchThumbVariants = cva(
|
|
|
1022
1265
|
size: "default",
|
|
1023
1266
|
},
|
|
1024
1267
|
}
|
|
1025
|
-
)
|
|
1268
|
+
);
|
|
1026
1269
|
|
|
1027
1270
|
/**
|
|
1028
1271
|
* Label text size variants
|
|
@@ -1038,7 +1281,7 @@ const labelSizeVariants = cva("", {
|
|
|
1038
1281
|
defaultVariants: {
|
|
1039
1282
|
size: "default",
|
|
1040
1283
|
},
|
|
1041
|
-
})
|
|
1284
|
+
});
|
|
1042
1285
|
|
|
1043
1286
|
/**
|
|
1044
1287
|
* A switch/toggle component for boolean inputs with on/off states
|
|
@@ -1051,12 +1294,16 @@ const labelSizeVariants = cva("", {
|
|
|
1051
1294
|
* \`\`\`
|
|
1052
1295
|
*/
|
|
1053
1296
|
export interface SwitchProps
|
|
1054
|
-
extends
|
|
1297
|
+
extends
|
|
1298
|
+
Omit<
|
|
1299
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>,
|
|
1300
|
+
"onChange"
|
|
1301
|
+
>,
|
|
1055
1302
|
VariantProps<typeof switchVariants> {
|
|
1056
1303
|
/** Optional label text */
|
|
1057
|
-
label?: string
|
|
1304
|
+
label?: string;
|
|
1058
1305
|
/** Position of the label */
|
|
1059
|
-
labelPosition?: "left" | "right"
|
|
1306
|
+
labelPosition?: "left" | "right";
|
|
1060
1307
|
}
|
|
1061
1308
|
|
|
1062
1309
|
const Switch = React.forwardRef<
|
|
@@ -1064,14 +1311,7 @@ const Switch = React.forwardRef<
|
|
|
1064
1311
|
SwitchProps
|
|
1065
1312
|
>(
|
|
1066
1313
|
(
|
|
1067
|
-
{
|
|
1068
|
-
className,
|
|
1069
|
-
size,
|
|
1070
|
-
label,
|
|
1071
|
-
labelPosition = "right",
|
|
1072
|
-
disabled,
|
|
1073
|
-
...props
|
|
1074
|
-
},
|
|
1314
|
+
{ className, size, label, labelPosition = "right", disabled, ...props },
|
|
1075
1315
|
ref
|
|
1076
1316
|
) => {
|
|
1077
1317
|
const switchElement = (
|
|
@@ -1083,43 +1323,49 @@ const Switch = React.forwardRef<
|
|
|
1083
1323
|
>
|
|
1084
1324
|
<SwitchPrimitives.Thumb className={cn(switchThumbVariants({ size }))} />
|
|
1085
1325
|
</SwitchPrimitives.Root>
|
|
1086
|
-
)
|
|
1326
|
+
);
|
|
1087
1327
|
|
|
1088
1328
|
if (label) {
|
|
1089
1329
|
return (
|
|
1090
|
-
<label
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1330
|
+
<label
|
|
1331
|
+
className={cn(
|
|
1332
|
+
"inline-flex items-center gap-2 cursor-pointer",
|
|
1333
|
+
disabled && "cursor-not-allowed"
|
|
1334
|
+
)}
|
|
1335
|
+
>
|
|
1094
1336
|
{labelPosition === "left" && (
|
|
1095
|
-
<span
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1337
|
+
<span
|
|
1338
|
+
className={cn(
|
|
1339
|
+
labelSizeVariants({ size }),
|
|
1340
|
+
"text-[#181D27]",
|
|
1341
|
+
disabled && "opacity-50"
|
|
1342
|
+
)}
|
|
1343
|
+
>
|
|
1100
1344
|
{label}
|
|
1101
1345
|
</span>
|
|
1102
1346
|
)}
|
|
1103
1347
|
{switchElement}
|
|
1104
1348
|
{labelPosition === "right" && (
|
|
1105
|
-
<span
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1349
|
+
<span
|
|
1350
|
+
className={cn(
|
|
1351
|
+
labelSizeVariants({ size }),
|
|
1352
|
+
"text-[#181D27]",
|
|
1353
|
+
disabled && "opacity-50"
|
|
1354
|
+
)}
|
|
1355
|
+
>
|
|
1110
1356
|
{label}
|
|
1111
1357
|
</span>
|
|
1112
1358
|
)}
|
|
1113
1359
|
</label>
|
|
1114
|
-
)
|
|
1360
|
+
);
|
|
1115
1361
|
}
|
|
1116
1362
|
|
|
1117
|
-
return switchElement
|
|
1363
|
+
return switchElement;
|
|
1118
1364
|
}
|
|
1119
|
-
)
|
|
1120
|
-
Switch.displayName = "Switch"
|
|
1365
|
+
);
|
|
1366
|
+
Switch.displayName = "Switch";
|
|
1121
1367
|
|
|
1122
|
-
export { Switch, switchVariants }
|
|
1368
|
+
export { Switch, switchVariants };
|
|
1123
1369
|
`, prefix)
|
|
1124
1370
|
}
|
|
1125
1371
|
]
|
|
@@ -1136,11 +1382,11 @@ export { Switch, switchVariants }
|
|
|
1136
1382
|
files: [
|
|
1137
1383
|
{
|
|
1138
1384
|
name: "text-field.tsx",
|
|
1139
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
1140
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1141
|
-
import { Loader2 } from "lucide-react"
|
|
1385
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
1386
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
1387
|
+
import { Loader2 } from "lucide-react";
|
|
1142
1388
|
|
|
1143
|
-
import { cn } from "../../lib/utils"
|
|
1389
|
+
import { cn } from "../../lib/utils";
|
|
1144
1390
|
|
|
1145
1391
|
/**
|
|
1146
1392
|
* TextField container variants for when icons/prefix/suffix are present
|
|
@@ -1150,8 +1396,10 @@ const textFieldContainerVariants = cva(
|
|
|
1150
1396
|
{
|
|
1151
1397
|
variants: {
|
|
1152
1398
|
state: {
|
|
1153
|
-
default:
|
|
1154
|
-
|
|
1399
|
+
default:
|
|
1400
|
+
"border border-[#E9EAEB] focus-within:border-[#2BBCCA]/50 focus-within:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
|
|
1401
|
+
error:
|
|
1402
|
+
"border border-[#F04438]/40 focus-within:border-[#F04438]/60 focus-within:shadow-[0_0_0_1px_rgba(240,68,56,0.1)]",
|
|
1155
1403
|
},
|
|
1156
1404
|
disabled: {
|
|
1157
1405
|
true: "cursor-not-allowed opacity-50 bg-[#FAFAFA]",
|
|
@@ -1163,7 +1411,7 @@ const textFieldContainerVariants = cva(
|
|
|
1163
1411
|
disabled: false,
|
|
1164
1412
|
},
|
|
1165
1413
|
}
|
|
1166
|
-
)
|
|
1414
|
+
);
|
|
1167
1415
|
|
|
1168
1416
|
/**
|
|
1169
1417
|
* TextField input variants (standalone without container)
|
|
@@ -1173,15 +1421,17 @@ const textFieldInputVariants = cva(
|
|
|
1173
1421
|
{
|
|
1174
1422
|
variants: {
|
|
1175
1423
|
state: {
|
|
1176
|
-
default:
|
|
1177
|
-
|
|
1424
|
+
default:
|
|
1425
|
+
"border border-[#E9EAEB] focus:outline-none focus:border-[#2BBCCA]/50 focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
|
|
1426
|
+
error:
|
|
1427
|
+
"border border-[#F04438]/40 focus:outline-none focus:border-[#F04438]/60 focus:shadow-[0_0_0_1px_rgba(240,68,56,0.1)]",
|
|
1178
1428
|
},
|
|
1179
1429
|
},
|
|
1180
1430
|
defaultVariants: {
|
|
1181
1431
|
state: "default",
|
|
1182
1432
|
},
|
|
1183
1433
|
}
|
|
1184
|
-
)
|
|
1434
|
+
);
|
|
1185
1435
|
|
|
1186
1436
|
/**
|
|
1187
1437
|
* A comprehensive text field component with label, icons, validation states, and more.
|
|
@@ -1194,34 +1444,35 @@ const textFieldInputVariants = cva(
|
|
|
1194
1444
|
* \`\`\`
|
|
1195
1445
|
*/
|
|
1196
1446
|
export interface TextFieldProps
|
|
1197
|
-
extends
|
|
1447
|
+
extends
|
|
1448
|
+
Omit<React.ComponentProps<"input">, "size">,
|
|
1198
1449
|
VariantProps<typeof textFieldInputVariants> {
|
|
1199
1450
|
/** Label text displayed above the input */
|
|
1200
|
-
label?: string
|
|
1451
|
+
label?: string;
|
|
1201
1452
|
/** Shows red asterisk next to label when true */
|
|
1202
|
-
required?: boolean
|
|
1453
|
+
required?: boolean;
|
|
1203
1454
|
/** Helper text displayed below the input */
|
|
1204
|
-
helperText?: string
|
|
1455
|
+
helperText?: string;
|
|
1205
1456
|
/** Error message - shows error state with red styling */
|
|
1206
|
-
error?: string
|
|
1457
|
+
error?: string;
|
|
1207
1458
|
/** Icon displayed on the left inside the input */
|
|
1208
|
-
leftIcon?: React.ReactNode
|
|
1459
|
+
leftIcon?: React.ReactNode;
|
|
1209
1460
|
/** Icon displayed on the right inside the input */
|
|
1210
|
-
rightIcon?: React.ReactNode
|
|
1461
|
+
rightIcon?: React.ReactNode;
|
|
1211
1462
|
/** Text prefix inside input (e.g., "https://") */
|
|
1212
|
-
prefix?: string
|
|
1463
|
+
prefix?: string;
|
|
1213
1464
|
/** Text suffix inside input (e.g., ".com") */
|
|
1214
|
-
suffix?: string
|
|
1465
|
+
suffix?: string;
|
|
1215
1466
|
/** Shows character count when maxLength is set */
|
|
1216
|
-
showCount?: boolean
|
|
1467
|
+
showCount?: boolean;
|
|
1217
1468
|
/** Shows loading spinner inside input */
|
|
1218
|
-
loading?: boolean
|
|
1469
|
+
loading?: boolean;
|
|
1219
1470
|
/** Additional class for the wrapper container */
|
|
1220
|
-
wrapperClassName?: string
|
|
1471
|
+
wrapperClassName?: string;
|
|
1221
1472
|
/** Additional class for the label */
|
|
1222
|
-
labelClassName?: string
|
|
1473
|
+
labelClassName?: string;
|
|
1223
1474
|
/** Additional class for the input container (includes prefix/suffix/icons) */
|
|
1224
|
-
inputContainerClassName?: string
|
|
1475
|
+
inputContainerClassName?: string;
|
|
1225
1476
|
}
|
|
1226
1477
|
|
|
1227
1478
|
const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
@@ -1253,37 +1504,39 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
1253
1504
|
ref
|
|
1254
1505
|
) => {
|
|
1255
1506
|
// Internal state for character count in uncontrolled mode
|
|
1256
|
-
const [internalValue, setInternalValue] = React.useState(
|
|
1507
|
+
const [internalValue, setInternalValue] = React.useState(
|
|
1508
|
+
defaultValue ?? ""
|
|
1509
|
+
);
|
|
1257
1510
|
|
|
1258
1511
|
// Determine if controlled
|
|
1259
|
-
const isControlled = value !== undefined
|
|
1260
|
-
const currentValue = isControlled ? value : internalValue
|
|
1512
|
+
const isControlled = value !== undefined;
|
|
1513
|
+
const currentValue = isControlled ? value : internalValue;
|
|
1261
1514
|
|
|
1262
1515
|
// Derive state from props
|
|
1263
|
-
const derivedState = error ?
|
|
1516
|
+
const derivedState = error ? "error" : (state ?? "default");
|
|
1264
1517
|
|
|
1265
1518
|
// Handle change for both controlled and uncontrolled
|
|
1266
1519
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
1267
1520
|
if (!isControlled) {
|
|
1268
|
-
setInternalValue(e.target.value)
|
|
1521
|
+
setInternalValue(e.target.value);
|
|
1269
1522
|
}
|
|
1270
|
-
onChange?.(e)
|
|
1271
|
-
}
|
|
1523
|
+
onChange?.(e);
|
|
1524
|
+
};
|
|
1272
1525
|
|
|
1273
1526
|
// Determine if we need the container wrapper (for icons/prefix/suffix)
|
|
1274
|
-
const hasAddons = leftIcon || rightIcon || prefix || suffix || loading
|
|
1527
|
+
const hasAddons = leftIcon || rightIcon || prefix || suffix || loading;
|
|
1275
1528
|
|
|
1276
1529
|
// Character count
|
|
1277
|
-
const charCount = String(currentValue).length
|
|
1530
|
+
const charCount = String(currentValue).length;
|
|
1278
1531
|
|
|
1279
1532
|
// Generate unique IDs for accessibility
|
|
1280
|
-
const generatedId = React.useId()
|
|
1281
|
-
const inputId = id || generatedId
|
|
1282
|
-
const helperId = \`\${inputId}-helper
|
|
1283
|
-
const errorId = \`\${inputId}-error
|
|
1533
|
+
const generatedId = React.useId();
|
|
1534
|
+
const inputId = id || generatedId;
|
|
1535
|
+
const helperId = \`\${inputId}-helper\`;
|
|
1536
|
+
const errorId = \`\${inputId}-error\`;
|
|
1284
1537
|
|
|
1285
1538
|
// Determine aria-describedby
|
|
1286
|
-
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined
|
|
1539
|
+
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined;
|
|
1287
1540
|
|
|
1288
1541
|
// Render the input element
|
|
1289
1542
|
const inputElement = (
|
|
@@ -1304,7 +1557,7 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
1304
1557
|
aria-describedby={ariaDescribedBy}
|
|
1305
1558
|
{...props}
|
|
1306
1559
|
/>
|
|
1307
|
-
)
|
|
1560
|
+
);
|
|
1308
1561
|
|
|
1309
1562
|
return (
|
|
1310
1563
|
<div className={cn("flex flex-col gap-1", wrapperClassName)}>
|
|
@@ -1323,17 +1576,38 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
1323
1576
|
{hasAddons ? (
|
|
1324
1577
|
<div
|
|
1325
1578
|
className={cn(
|
|
1326
|
-
textFieldContainerVariants({
|
|
1579
|
+
textFieldContainerVariants({
|
|
1580
|
+
state: derivedState,
|
|
1581
|
+
disabled: disabled || loading,
|
|
1582
|
+
}),
|
|
1327
1583
|
"h-10 px-4",
|
|
1328
1584
|
inputContainerClassName
|
|
1329
1585
|
)}
|
|
1330
1586
|
>
|
|
1331
|
-
{prefix &&
|
|
1332
|
-
|
|
1587
|
+
{prefix && (
|
|
1588
|
+
<span className="text-sm text-[#717680] mr-2 select-none">
|
|
1589
|
+
{prefix}
|
|
1590
|
+
</span>
|
|
1591
|
+
)}
|
|
1592
|
+
{leftIcon && (
|
|
1593
|
+
<span className="mr-2 text-[#717680] [&_svg]:size-4 flex-shrink-0">
|
|
1594
|
+
{leftIcon}
|
|
1595
|
+
</span>
|
|
1596
|
+
)}
|
|
1333
1597
|
{inputElement}
|
|
1334
|
-
{loading &&
|
|
1335
|
-
|
|
1336
|
-
|
|
1598
|
+
{loading && (
|
|
1599
|
+
<Loader2 className="animate-spin size-4 text-[#717680] ml-2 flex-shrink-0" />
|
|
1600
|
+
)}
|
|
1601
|
+
{!loading && rightIcon && (
|
|
1602
|
+
<span className="ml-2 text-[#717680] [&_svg]:size-4 flex-shrink-0">
|
|
1603
|
+
{rightIcon}
|
|
1604
|
+
</span>
|
|
1605
|
+
)}
|
|
1606
|
+
{suffix && (
|
|
1607
|
+
<span className="text-sm text-[#717680] ml-2 select-none">
|
|
1608
|
+
{suffix}
|
|
1609
|
+
</span>
|
|
1610
|
+
)}
|
|
1337
1611
|
</div>
|
|
1338
1612
|
) : (
|
|
1339
1613
|
inputElement
|
|
@@ -1366,12 +1640,12 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
1366
1640
|
</div>
|
|
1367
1641
|
)}
|
|
1368
1642
|
</div>
|
|
1369
|
-
)
|
|
1643
|
+
);
|
|
1370
1644
|
}
|
|
1371
|
-
)
|
|
1372
|
-
TextField.displayName = "TextField"
|
|
1645
|
+
);
|
|
1646
|
+
TextField.displayName = "TextField";
|
|
1373
1647
|
|
|
1374
|
-
export { TextField, textFieldContainerVariants, textFieldInputVariants }
|
|
1648
|
+
export { TextField, textFieldContainerVariants, textFieldInputVariants };
|
|
1375
1649
|
`, prefix)
|
|
1376
1650
|
}
|
|
1377
1651
|
]
|
|
@@ -1388,10 +1662,10 @@ export { TextField, textFieldContainerVariants, textFieldInputVariants }
|
|
|
1388
1662
|
files: [
|
|
1389
1663
|
{
|
|
1390
1664
|
name: "select-field.tsx",
|
|
1391
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
1392
|
-
import { Loader2 } from "lucide-react"
|
|
1665
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
1666
|
+
import { Loader2 } from "lucide-react";
|
|
1393
1667
|
|
|
1394
|
-
import { cn } from "../../lib/utils"
|
|
1668
|
+
import { cn } from "../../lib/utils";
|
|
1395
1669
|
import {
|
|
1396
1670
|
Select,
|
|
1397
1671
|
SelectContent,
|
|
@@ -1400,56 +1674,56 @@ import {
|
|
|
1400
1674
|
SelectLabel,
|
|
1401
1675
|
SelectTrigger,
|
|
1402
1676
|
SelectValue,
|
|
1403
|
-
} from "./select"
|
|
1677
|
+
} from "./select";
|
|
1404
1678
|
|
|
1405
1679
|
export interface SelectOption {
|
|
1406
1680
|
/** The value of the option */
|
|
1407
|
-
value: string
|
|
1681
|
+
value: string;
|
|
1408
1682
|
/** The display label of the option */
|
|
1409
|
-
label: string
|
|
1683
|
+
label: string;
|
|
1410
1684
|
/** Whether the option is disabled */
|
|
1411
|
-
disabled?: boolean
|
|
1685
|
+
disabled?: boolean;
|
|
1412
1686
|
/** Group name for grouping options */
|
|
1413
|
-
group?: string
|
|
1687
|
+
group?: string;
|
|
1414
1688
|
}
|
|
1415
1689
|
|
|
1416
1690
|
export interface SelectFieldProps {
|
|
1417
1691
|
/** Label text displayed above the select */
|
|
1418
|
-
label?: string
|
|
1692
|
+
label?: string;
|
|
1419
1693
|
/** Shows red asterisk next to label when true */
|
|
1420
|
-
required?: boolean
|
|
1694
|
+
required?: boolean;
|
|
1421
1695
|
/** Helper text displayed below the select */
|
|
1422
|
-
helperText?: string
|
|
1696
|
+
helperText?: string;
|
|
1423
1697
|
/** Error message - shows error state with red styling */
|
|
1424
|
-
error?: string
|
|
1698
|
+
error?: string;
|
|
1425
1699
|
/** Disabled state */
|
|
1426
|
-
disabled?: boolean
|
|
1700
|
+
disabled?: boolean;
|
|
1427
1701
|
/** Loading state with spinner */
|
|
1428
|
-
loading?: boolean
|
|
1702
|
+
loading?: boolean;
|
|
1429
1703
|
/** Placeholder text when no value selected */
|
|
1430
|
-
placeholder?: string
|
|
1704
|
+
placeholder?: string;
|
|
1431
1705
|
/** Currently selected value (controlled) */
|
|
1432
|
-
value?: string
|
|
1706
|
+
value?: string;
|
|
1433
1707
|
/** Default value (uncontrolled) */
|
|
1434
|
-
defaultValue?: string
|
|
1708
|
+
defaultValue?: string;
|
|
1435
1709
|
/** Callback when value changes */
|
|
1436
|
-
onValueChange?: (value: string) => void
|
|
1710
|
+
onValueChange?: (value: string) => void;
|
|
1437
1711
|
/** Options to display */
|
|
1438
|
-
options: SelectOption[]
|
|
1712
|
+
options: SelectOption[];
|
|
1439
1713
|
/** Enable search/filter functionality */
|
|
1440
|
-
searchable?: boolean
|
|
1714
|
+
searchable?: boolean;
|
|
1441
1715
|
/** Search placeholder text */
|
|
1442
|
-
searchPlaceholder?: string
|
|
1716
|
+
searchPlaceholder?: string;
|
|
1443
1717
|
/** Additional class for wrapper */
|
|
1444
|
-
wrapperClassName?: string
|
|
1718
|
+
wrapperClassName?: string;
|
|
1445
1719
|
/** Additional class for trigger */
|
|
1446
|
-
triggerClassName?: string
|
|
1720
|
+
triggerClassName?: string;
|
|
1447
1721
|
/** Additional class for label */
|
|
1448
|
-
labelClassName?: string
|
|
1722
|
+
labelClassName?: string;
|
|
1449
1723
|
/** ID for the select */
|
|
1450
|
-
id?: string
|
|
1724
|
+
id?: string;
|
|
1451
1725
|
/** Name attribute for form submission */
|
|
1452
|
-
name?: string
|
|
1726
|
+
name?: string;
|
|
1453
1727
|
}
|
|
1454
1728
|
|
|
1455
1729
|
/**
|
|
@@ -1494,59 +1768,59 @@ const SelectField = React.forwardRef<HTMLButtonElement, SelectFieldProps>(
|
|
|
1494
1768
|
ref
|
|
1495
1769
|
) => {
|
|
1496
1770
|
// Internal state for search
|
|
1497
|
-
const [searchQuery, setSearchQuery] = React.useState("")
|
|
1771
|
+
const [searchQuery, setSearchQuery] = React.useState("");
|
|
1498
1772
|
|
|
1499
1773
|
// Derive state from props
|
|
1500
|
-
const derivedState = error ? "error" : "default"
|
|
1774
|
+
const derivedState = error ? "error" : "default";
|
|
1501
1775
|
|
|
1502
1776
|
// Generate unique IDs for accessibility
|
|
1503
|
-
const generatedId = React.useId()
|
|
1504
|
-
const selectId = id || generatedId
|
|
1505
|
-
const helperId = \`\${selectId}-helper
|
|
1506
|
-
const errorId = \`\${selectId}-error
|
|
1777
|
+
const generatedId = React.useId();
|
|
1778
|
+
const selectId = id || generatedId;
|
|
1779
|
+
const helperId = \`\${selectId}-helper\`;
|
|
1780
|
+
const errorId = \`\${selectId}-error\`;
|
|
1507
1781
|
|
|
1508
1782
|
// Determine aria-describedby
|
|
1509
|
-
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined
|
|
1783
|
+
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined;
|
|
1510
1784
|
|
|
1511
1785
|
// Group options by group property
|
|
1512
1786
|
const groupedOptions = React.useMemo(() => {
|
|
1513
|
-
const groups: Record<string, SelectOption[]> = {}
|
|
1514
|
-
const ungrouped: SelectOption[] = []
|
|
1787
|
+
const groups: Record<string, SelectOption[]> = {};
|
|
1788
|
+
const ungrouped: SelectOption[] = [];
|
|
1515
1789
|
|
|
1516
1790
|
options.forEach((option) => {
|
|
1517
1791
|
// Filter by search query if searchable
|
|
1518
1792
|
if (searchable && searchQuery) {
|
|
1519
1793
|
if (!option.label.toLowerCase().includes(searchQuery.toLowerCase())) {
|
|
1520
|
-
return
|
|
1794
|
+
return;
|
|
1521
1795
|
}
|
|
1522
1796
|
}
|
|
1523
1797
|
|
|
1524
1798
|
if (option.group) {
|
|
1525
1799
|
if (!groups[option.group]) {
|
|
1526
|
-
groups[option.group] = []
|
|
1800
|
+
groups[option.group] = [];
|
|
1527
1801
|
}
|
|
1528
|
-
groups[option.group].push(option)
|
|
1802
|
+
groups[option.group].push(option);
|
|
1529
1803
|
} else {
|
|
1530
|
-
ungrouped.push(option)
|
|
1804
|
+
ungrouped.push(option);
|
|
1531
1805
|
}
|
|
1532
|
-
})
|
|
1806
|
+
});
|
|
1533
1807
|
|
|
1534
|
-
return { groups, ungrouped }
|
|
1535
|
-
}, [options, searchable, searchQuery])
|
|
1808
|
+
return { groups, ungrouped };
|
|
1809
|
+
}, [options, searchable, searchQuery]);
|
|
1536
1810
|
|
|
1537
|
-
const hasGroups = Object.keys(groupedOptions.groups).length > 0
|
|
1811
|
+
const hasGroups = Object.keys(groupedOptions.groups).length > 0;
|
|
1538
1812
|
|
|
1539
1813
|
// Handle search input change
|
|
1540
1814
|
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
1541
|
-
setSearchQuery(e.target.value)
|
|
1542
|
-
}
|
|
1815
|
+
setSearchQuery(e.target.value);
|
|
1816
|
+
};
|
|
1543
1817
|
|
|
1544
1818
|
// Reset search when dropdown closes
|
|
1545
1819
|
const handleOpenChange = (open: boolean) => {
|
|
1546
1820
|
if (!open) {
|
|
1547
|
-
setSearchQuery("")
|
|
1821
|
+
setSearchQuery("");
|
|
1548
1822
|
}
|
|
1549
|
-
}
|
|
1823
|
+
};
|
|
1550
1824
|
|
|
1551
1825
|
return (
|
|
1552
1826
|
<div className={cn("flex flex-col gap-1", wrapperClassName)}>
|
|
@@ -1574,10 +1848,7 @@ const SelectField = React.forwardRef<HTMLButtonElement, SelectFieldProps>(
|
|
|
1574
1848
|
ref={ref}
|
|
1575
1849
|
id={selectId}
|
|
1576
1850
|
state={derivedState}
|
|
1577
|
-
className={cn(
|
|
1578
|
-
loading && "pr-10",
|
|
1579
|
-
triggerClassName
|
|
1580
|
-
)}
|
|
1851
|
+
className={cn(loading && "pr-10", triggerClassName)}
|
|
1581
1852
|
aria-invalid={!!error}
|
|
1582
1853
|
aria-describedby={ariaDescribedBy}
|
|
1583
1854
|
>
|
|
@@ -1616,20 +1887,22 @@ const SelectField = React.forwardRef<HTMLButtonElement, SelectFieldProps>(
|
|
|
1616
1887
|
|
|
1617
1888
|
{/* Grouped options */}
|
|
1618
1889
|
{hasGroups &&
|
|
1619
|
-
Object.entries(groupedOptions.groups).map(
|
|
1620
|
-
|
|
1621
|
-
<
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1890
|
+
Object.entries(groupedOptions.groups).map(
|
|
1891
|
+
([groupName, groupOptions]) => (
|
|
1892
|
+
<SelectGroup key={groupName}>
|
|
1893
|
+
<SelectLabel>{groupName}</SelectLabel>
|
|
1894
|
+
{groupOptions.map((option) => (
|
|
1895
|
+
<SelectItem
|
|
1896
|
+
key={option.value}
|
|
1897
|
+
value={option.value}
|
|
1898
|
+
disabled={option.disabled}
|
|
1899
|
+
>
|
|
1900
|
+
{option.label}
|
|
1901
|
+
</SelectItem>
|
|
1902
|
+
))}
|
|
1903
|
+
</SelectGroup>
|
|
1904
|
+
)
|
|
1905
|
+
)}
|
|
1633
1906
|
|
|
1634
1907
|
{/* No results message */}
|
|
1635
1908
|
{searchable &&
|
|
@@ -1658,12 +1931,12 @@ const SelectField = React.forwardRef<HTMLButtonElement, SelectFieldProps>(
|
|
|
1658
1931
|
</div>
|
|
1659
1932
|
)}
|
|
1660
1933
|
</div>
|
|
1661
|
-
)
|
|
1934
|
+
);
|
|
1662
1935
|
}
|
|
1663
|
-
)
|
|
1664
|
-
SelectField.displayName = "SelectField"
|
|
1936
|
+
);
|
|
1937
|
+
SelectField.displayName = "SelectField";
|
|
1665
1938
|
|
|
1666
|
-
export { SelectField }
|
|
1939
|
+
export { SelectField };
|
|
1667
1940
|
`, prefix)
|
|
1668
1941
|
}
|
|
1669
1942
|
]
|
|
@@ -1680,11 +1953,11 @@ export { SelectField }
|
|
|
1680
1953
|
files: [
|
|
1681
1954
|
{
|
|
1682
1955
|
name: "multi-select.tsx",
|
|
1683
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
1684
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1685
|
-
import { Check, ChevronDown, X, Loader2 } from "lucide-react"
|
|
1956
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
1957
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
1958
|
+
import { Check, ChevronDown, X, Loader2 } from "lucide-react";
|
|
1686
1959
|
|
|
1687
|
-
import { cn } from "../../lib/utils"
|
|
1960
|
+
import { cn } from "../../lib/utils";
|
|
1688
1961
|
|
|
1689
1962
|
/**
|
|
1690
1963
|
* MultiSelect trigger variants matching TextField styling
|
|
@@ -1694,64 +1967,68 @@ const multiSelectTriggerVariants = cva(
|
|
|
1694
1967
|
{
|
|
1695
1968
|
variants: {
|
|
1696
1969
|
state: {
|
|
1697
|
-
default:
|
|
1698
|
-
|
|
1970
|
+
default:
|
|
1971
|
+
"border border-[#E9EAEB] focus:outline-none focus:border-[#2BBCCA]/50 focus:shadow-[0_0_0_1px_rgba(43,188,202,0.15)]",
|
|
1972
|
+
error:
|
|
1973
|
+
"border border-[#F04438]/40 focus:outline-none focus:border-[#F04438]/60 focus:shadow-[0_0_0_1px_rgba(240,68,56,0.1)]",
|
|
1699
1974
|
},
|
|
1700
1975
|
},
|
|
1701
1976
|
defaultVariants: {
|
|
1702
1977
|
state: "default",
|
|
1703
1978
|
},
|
|
1704
1979
|
}
|
|
1705
|
-
)
|
|
1980
|
+
);
|
|
1706
1981
|
|
|
1707
1982
|
export interface MultiSelectOption {
|
|
1708
1983
|
/** The value of the option */
|
|
1709
|
-
value: string
|
|
1984
|
+
value: string;
|
|
1710
1985
|
/** The display label of the option */
|
|
1711
|
-
label: string
|
|
1986
|
+
label: string;
|
|
1712
1987
|
/** Whether the option is disabled */
|
|
1713
|
-
disabled?: boolean
|
|
1988
|
+
disabled?: boolean;
|
|
1714
1989
|
}
|
|
1715
1990
|
|
|
1716
|
-
export interface MultiSelectProps extends VariantProps<
|
|
1991
|
+
export interface MultiSelectProps extends VariantProps<
|
|
1992
|
+
typeof multiSelectTriggerVariants
|
|
1993
|
+
> {
|
|
1717
1994
|
/** Label text displayed above the select */
|
|
1718
|
-
label?: string
|
|
1995
|
+
label?: string;
|
|
1719
1996
|
/** Shows red asterisk next to label when true */
|
|
1720
|
-
required?: boolean
|
|
1997
|
+
required?: boolean;
|
|
1721
1998
|
/** Helper text displayed below the select */
|
|
1722
|
-
helperText?: string
|
|
1999
|
+
helperText?: string;
|
|
1723
2000
|
/** Error message - shows error state with red styling */
|
|
1724
|
-
error?: string
|
|
2001
|
+
error?: string;
|
|
1725
2002
|
/** Disabled state */
|
|
1726
|
-
disabled?: boolean
|
|
2003
|
+
disabled?: boolean;
|
|
1727
2004
|
/** Loading state with spinner */
|
|
1728
|
-
loading?: boolean
|
|
2005
|
+
loading?: boolean;
|
|
1729
2006
|
/** Placeholder text when no value selected */
|
|
1730
|
-
placeholder?: string
|
|
2007
|
+
placeholder?: string;
|
|
1731
2008
|
/** Currently selected values (controlled) */
|
|
1732
|
-
value?: string[]
|
|
2009
|
+
value?: string[];
|
|
1733
2010
|
/** Default values (uncontrolled) */
|
|
1734
|
-
defaultValue?: string[]
|
|
2011
|
+
defaultValue?: string[];
|
|
1735
2012
|
/** Callback when values change */
|
|
1736
|
-
onValueChange?: (value: string[]) => void
|
|
2013
|
+
onValueChange?: (value: string[]) => void;
|
|
1737
2014
|
/** Options to display */
|
|
1738
|
-
options: MultiSelectOption[]
|
|
2015
|
+
options: MultiSelectOption[];
|
|
1739
2016
|
/** Enable search/filter functionality */
|
|
1740
|
-
searchable?: boolean
|
|
2017
|
+
searchable?: boolean;
|
|
1741
2018
|
/** Search placeholder text */
|
|
1742
|
-
searchPlaceholder?: string
|
|
2019
|
+
searchPlaceholder?: string;
|
|
1743
2020
|
/** Maximum selections allowed */
|
|
1744
|
-
maxSelections?: number
|
|
2021
|
+
maxSelections?: number;
|
|
1745
2022
|
/** Additional class for wrapper */
|
|
1746
|
-
wrapperClassName?: string
|
|
2023
|
+
wrapperClassName?: string;
|
|
1747
2024
|
/** Additional class for trigger */
|
|
1748
|
-
triggerClassName?: string
|
|
2025
|
+
triggerClassName?: string;
|
|
1749
2026
|
/** Additional class for label */
|
|
1750
|
-
labelClassName?: string
|
|
2027
|
+
labelClassName?: string;
|
|
1751
2028
|
/** ID for the select */
|
|
1752
|
-
id?: string
|
|
2029
|
+
id?: string;
|
|
1753
2030
|
/** Name attribute for form submission */
|
|
1754
|
-
name?: string
|
|
2031
|
+
name?: string;
|
|
1755
2032
|
}
|
|
1756
2033
|
|
|
1757
2034
|
/**
|
|
@@ -1798,104 +2075,109 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
1798
2075
|
ref
|
|
1799
2076
|
) => {
|
|
1800
2077
|
// Internal state for selected values (uncontrolled mode)
|
|
1801
|
-
const [internalValue, setInternalValue] =
|
|
2078
|
+
const [internalValue, setInternalValue] =
|
|
2079
|
+
React.useState<string[]>(defaultValue);
|
|
1802
2080
|
// Dropdown open state
|
|
1803
|
-
const [isOpen, setIsOpen] = React.useState(false)
|
|
2081
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
1804
2082
|
// Search query
|
|
1805
|
-
const [searchQuery, setSearchQuery] = React.useState("")
|
|
2083
|
+
const [searchQuery, setSearchQuery] = React.useState("");
|
|
1806
2084
|
|
|
1807
2085
|
// Container ref for click outside detection
|
|
1808
|
-
const containerRef = React.useRef<HTMLDivElement>(null)
|
|
2086
|
+
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
1809
2087
|
|
|
1810
2088
|
// Determine if controlled
|
|
1811
|
-
const isControlled = value !== undefined
|
|
1812
|
-
const selectedValues = isControlled ? value : internalValue
|
|
2089
|
+
const isControlled = value !== undefined;
|
|
2090
|
+
const selectedValues = isControlled ? value : internalValue;
|
|
1813
2091
|
|
|
1814
2092
|
// Derive state from props
|
|
1815
|
-
const derivedState = error ? "error" : (state ?? "default")
|
|
2093
|
+
const derivedState = error ? "error" : (state ?? "default");
|
|
1816
2094
|
|
|
1817
2095
|
// Generate unique IDs for accessibility
|
|
1818
|
-
const generatedId = React.useId()
|
|
1819
|
-
const selectId = id || generatedId
|
|
1820
|
-
const helperId = \`\${selectId}-helper
|
|
1821
|
-
const errorId = \`\${selectId}-error
|
|
2096
|
+
const generatedId = React.useId();
|
|
2097
|
+
const selectId = id || generatedId;
|
|
2098
|
+
const helperId = \`\${selectId}-helper\`;
|
|
2099
|
+
const errorId = \`\${selectId}-error\`;
|
|
1822
2100
|
|
|
1823
2101
|
// Determine aria-describedby
|
|
1824
|
-
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined
|
|
2102
|
+
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined;
|
|
1825
2103
|
|
|
1826
2104
|
// Filter options by search query
|
|
1827
2105
|
const filteredOptions = React.useMemo(() => {
|
|
1828
|
-
if (!searchable || !searchQuery) return options
|
|
2106
|
+
if (!searchable || !searchQuery) return options;
|
|
1829
2107
|
return options.filter((option) =>
|
|
1830
2108
|
option.label.toLowerCase().includes(searchQuery.toLowerCase())
|
|
1831
|
-
)
|
|
1832
|
-
}, [options, searchable, searchQuery])
|
|
2109
|
+
);
|
|
2110
|
+
}, [options, searchable, searchQuery]);
|
|
1833
2111
|
|
|
1834
2112
|
// Get selected option labels
|
|
1835
2113
|
const selectedLabels = React.useMemo(() => {
|
|
1836
2114
|
return selectedValues
|
|
1837
2115
|
.map((v) => options.find((o) => o.value === v)?.label)
|
|
1838
|
-
.filter(Boolean) as string[]
|
|
1839
|
-
}, [selectedValues, options])
|
|
2116
|
+
.filter(Boolean) as string[];
|
|
2117
|
+
}, [selectedValues, options]);
|
|
1840
2118
|
|
|
1841
2119
|
// Handle toggle selection
|
|
1842
2120
|
const toggleOption = (optionValue: string) => {
|
|
1843
2121
|
const newValues = selectedValues.includes(optionValue)
|
|
1844
2122
|
? selectedValues.filter((v) => v !== optionValue)
|
|
1845
2123
|
: maxSelections && selectedValues.length >= maxSelections
|
|
1846
|
-
|
|
1847
|
-
|
|
2124
|
+
? selectedValues
|
|
2125
|
+
: [...selectedValues, optionValue];
|
|
1848
2126
|
|
|
1849
2127
|
if (!isControlled) {
|
|
1850
|
-
setInternalValue(newValues)
|
|
2128
|
+
setInternalValue(newValues);
|
|
1851
2129
|
}
|
|
1852
|
-
onValueChange?.(newValues)
|
|
1853
|
-
}
|
|
2130
|
+
onValueChange?.(newValues);
|
|
2131
|
+
};
|
|
1854
2132
|
|
|
1855
2133
|
// Handle remove tag
|
|
1856
2134
|
const removeValue = (valueToRemove: string, e: React.MouseEvent) => {
|
|
1857
|
-
e.stopPropagation()
|
|
1858
|
-
const newValues = selectedValues.filter((v) => v !== valueToRemove)
|
|
2135
|
+
e.stopPropagation();
|
|
2136
|
+
const newValues = selectedValues.filter((v) => v !== valueToRemove);
|
|
1859
2137
|
if (!isControlled) {
|
|
1860
|
-
setInternalValue(newValues)
|
|
2138
|
+
setInternalValue(newValues);
|
|
1861
2139
|
}
|
|
1862
|
-
onValueChange?.(newValues)
|
|
1863
|
-
}
|
|
2140
|
+
onValueChange?.(newValues);
|
|
2141
|
+
};
|
|
1864
2142
|
|
|
1865
2143
|
// Handle clear all
|
|
1866
2144
|
const clearAll = (e: React.MouseEvent) => {
|
|
1867
|
-
e.stopPropagation()
|
|
2145
|
+
e.stopPropagation();
|
|
1868
2146
|
if (!isControlled) {
|
|
1869
|
-
setInternalValue([])
|
|
2147
|
+
setInternalValue([]);
|
|
1870
2148
|
}
|
|
1871
|
-
onValueChange?.([])
|
|
1872
|
-
}
|
|
2149
|
+
onValueChange?.([]);
|
|
2150
|
+
};
|
|
1873
2151
|
|
|
1874
2152
|
// Close dropdown when clicking outside
|
|
1875
2153
|
React.useEffect(() => {
|
|
1876
2154
|
const handleClickOutside = (event: MouseEvent) => {
|
|
1877
|
-
if (
|
|
1878
|
-
|
|
1879
|
-
|
|
2155
|
+
if (
|
|
2156
|
+
containerRef.current &&
|
|
2157
|
+
!containerRef.current.contains(event.target as Node)
|
|
2158
|
+
) {
|
|
2159
|
+
setIsOpen(false);
|
|
2160
|
+
setSearchQuery("");
|
|
1880
2161
|
}
|
|
1881
|
-
}
|
|
2162
|
+
};
|
|
1882
2163
|
|
|
1883
|
-
document.addEventListener("mousedown", handleClickOutside)
|
|
1884
|
-
return () =>
|
|
1885
|
-
|
|
2164
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
2165
|
+
return () =>
|
|
2166
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
2167
|
+
}, []);
|
|
1886
2168
|
|
|
1887
2169
|
// Handle keyboard navigation
|
|
1888
2170
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
1889
2171
|
if (e.key === "Escape") {
|
|
1890
|
-
setIsOpen(false)
|
|
1891
|
-
setSearchQuery("")
|
|
2172
|
+
setIsOpen(false);
|
|
2173
|
+
setSearchQuery("");
|
|
1892
2174
|
} else if (e.key === "Enter" || e.key === " ") {
|
|
1893
2175
|
if (!isOpen) {
|
|
1894
|
-
e.preventDefault()
|
|
1895
|
-
setIsOpen(true)
|
|
2176
|
+
e.preventDefault();
|
|
2177
|
+
setIsOpen(true);
|
|
1896
2178
|
}
|
|
1897
2179
|
}
|
|
1898
|
-
}
|
|
2180
|
+
};
|
|
1899
2181
|
|
|
1900
2182
|
return (
|
|
1901
2183
|
<div
|
|
@@ -1947,9 +2229,12 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
1947
2229
|
tabIndex={0}
|
|
1948
2230
|
onClick={(e) => removeValue(selectedValues[index], e)}
|
|
1949
2231
|
onKeyDown={(e) => {
|
|
1950
|
-
if (e.key ===
|
|
1951
|
-
e.preventDefault()
|
|
1952
|
-
removeValue(
|
|
2232
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
2233
|
+
e.preventDefault();
|
|
2234
|
+
removeValue(
|
|
2235
|
+
selectedValues[index],
|
|
2236
|
+
e as unknown as React.MouseEvent
|
|
2237
|
+
);
|
|
1953
2238
|
}
|
|
1954
2239
|
}}
|
|
1955
2240
|
className="cursor-pointer hover:text-[#F04438] focus:outline-none"
|
|
@@ -1968,9 +2253,9 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
1968
2253
|
tabIndex={0}
|
|
1969
2254
|
onClick={clearAll}
|
|
1970
2255
|
onKeyDown={(e) => {
|
|
1971
|
-
if (e.key ===
|
|
1972
|
-
e.preventDefault()
|
|
1973
|
-
clearAll(e as unknown as React.MouseEvent)
|
|
2256
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
2257
|
+
e.preventDefault();
|
|
2258
|
+
clearAll(e as unknown as React.MouseEvent);
|
|
1974
2259
|
}
|
|
1975
2260
|
}}
|
|
1976
2261
|
className="p-0.5 cursor-pointer hover:text-[#F04438] focus:outline-none"
|
|
@@ -2024,10 +2309,13 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2024
2309
|
</div>
|
|
2025
2310
|
) : (
|
|
2026
2311
|
filteredOptions.map((option) => {
|
|
2027
|
-
const isSelected = selectedValues.includes(option.value)
|
|
2312
|
+
const isSelected = selectedValues.includes(option.value);
|
|
2028
2313
|
const isDisabled =
|
|
2029
2314
|
option.disabled ||
|
|
2030
|
-
(!isSelected &&
|
|
2315
|
+
(!isSelected &&
|
|
2316
|
+
maxSelections !== undefined &&
|
|
2317
|
+
maxSelections > 0 &&
|
|
2318
|
+
selectedValues.length >= maxSelections);
|
|
2031
2319
|
|
|
2032
2320
|
return (
|
|
2033
2321
|
<button
|
|
@@ -2045,11 +2333,13 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2045
2333
|
)}
|
|
2046
2334
|
>
|
|
2047
2335
|
<span className="absolute right-2 flex size-4 items-center justify-center">
|
|
2048
|
-
{isSelected &&
|
|
2336
|
+
{isSelected && (
|
|
2337
|
+
<Check className="size-4 text-[#2BBCCA]" />
|
|
2338
|
+
)}
|
|
2049
2339
|
</span>
|
|
2050
2340
|
{option.label}
|
|
2051
2341
|
</button>
|
|
2052
|
-
)
|
|
2342
|
+
);
|
|
2053
2343
|
})
|
|
2054
2344
|
)}
|
|
2055
2345
|
</div>
|
|
@@ -2064,9 +2354,10 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2064
2354
|
)}
|
|
2065
2355
|
|
|
2066
2356
|
{/* Hidden input for form submission */}
|
|
2067
|
-
{name &&
|
|
2068
|
-
|
|
2069
|
-
|
|
2357
|
+
{name &&
|
|
2358
|
+
selectedValues.map((v) => (
|
|
2359
|
+
<input key={v} type="hidden" name={name} value={v} />
|
|
2360
|
+
))}
|
|
2070
2361
|
|
|
2071
2362
|
{/* Helper text / Error message */}
|
|
2072
2363
|
{(error || helperText) && (
|
|
@@ -2083,12 +2374,12 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2083
2374
|
</div>
|
|
2084
2375
|
)}
|
|
2085
2376
|
</div>
|
|
2086
|
-
)
|
|
2377
|
+
);
|
|
2087
2378
|
}
|
|
2088
|
-
)
|
|
2089
|
-
MultiSelect.displayName = "MultiSelect"
|
|
2379
|
+
);
|
|
2380
|
+
MultiSelect.displayName = "MultiSelect";
|
|
2090
2381
|
|
|
2091
|
-
export { MultiSelect, multiSelectTriggerVariants }
|
|
2382
|
+
export { MultiSelect, multiSelectTriggerVariants };
|
|
2092
2383
|
`, prefix)
|
|
2093
2384
|
}
|
|
2094
2385
|
]
|
|
@@ -2104,30 +2395,27 @@ export { MultiSelect, multiSelectTriggerVariants }
|
|
|
2104
2395
|
files: [
|
|
2105
2396
|
{
|
|
2106
2397
|
name: "table.tsx",
|
|
2107
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
2108
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
2398
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
2399
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2109
2400
|
|
|
2110
|
-
import { cn } from "../../lib/utils"
|
|
2111
|
-
import { Switch, type SwitchProps } from "./switch"
|
|
2401
|
+
import { cn } from "../../lib/utils";
|
|
2402
|
+
import { Switch, type SwitchProps } from "./switch";
|
|
2112
2403
|
|
|
2113
2404
|
/**
|
|
2114
2405
|
* Table size variants for row height.
|
|
2115
2406
|
*/
|
|
2116
|
-
const tableVariants = cva(
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
md: "[&_td]:py-3 [&_th]:py-3",
|
|
2123
|
-
lg: "[&_td]:py-4 [&_th]:py-4",
|
|
2124
|
-
},
|
|
2125
|
-
},
|
|
2126
|
-
defaultVariants: {
|
|
2127
|
-
size: "md",
|
|
2407
|
+
const tableVariants = cva("w-full caption-bottom text-sm", {
|
|
2408
|
+
variants: {
|
|
2409
|
+
size: {
|
|
2410
|
+
sm: "[&_td]:py-2 [&_th]:py-2",
|
|
2411
|
+
md: "[&_td]:py-3 [&_th]:py-3",
|
|
2412
|
+
lg: "[&_td]:py-4 [&_th]:py-4",
|
|
2128
2413
|
},
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2414
|
+
},
|
|
2415
|
+
defaultVariants: {
|
|
2416
|
+
size: "md",
|
|
2417
|
+
},
|
|
2418
|
+
});
|
|
2131
2419
|
|
|
2132
2420
|
/**
|
|
2133
2421
|
* Table component for displaying tabular data.
|
|
@@ -2152,18 +2440,21 @@ const tableVariants = cva(
|
|
|
2152
2440
|
*/
|
|
2153
2441
|
|
|
2154
2442
|
export interface TableProps
|
|
2155
|
-
extends
|
|
2443
|
+
extends
|
|
2444
|
+
React.HTMLAttributes<HTMLTableElement>,
|
|
2156
2445
|
VariantProps<typeof tableVariants> {
|
|
2157
2446
|
/** Remove outer border from the table */
|
|
2158
|
-
withoutBorder?: boolean
|
|
2447
|
+
withoutBorder?: boolean;
|
|
2159
2448
|
}
|
|
2160
2449
|
|
|
2161
2450
|
const Table = React.forwardRef<HTMLTableElement, TableProps>(
|
|
2162
2451
|
({ className, size, withoutBorder, ...props }, ref) => (
|
|
2163
|
-
<div
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2452
|
+
<div
|
|
2453
|
+
className={cn(
|
|
2454
|
+
"relative w-full overflow-auto",
|
|
2455
|
+
!withoutBorder && "rounded-lg border border-[#E9EAEB]"
|
|
2456
|
+
)}
|
|
2457
|
+
>
|
|
2167
2458
|
<table
|
|
2168
2459
|
ref={ref}
|
|
2169
2460
|
className={cn(tableVariants({ size, className }))}
|
|
@@ -2171,8 +2462,8 @@ const Table = React.forwardRef<HTMLTableElement, TableProps>(
|
|
|
2171
2462
|
/>
|
|
2172
2463
|
</div>
|
|
2173
2464
|
)
|
|
2174
|
-
)
|
|
2175
|
-
Table.displayName = "Table"
|
|
2465
|
+
);
|
|
2466
|
+
Table.displayName = "Table";
|
|
2176
2467
|
|
|
2177
2468
|
const TableHeader = React.forwardRef<
|
|
2178
2469
|
HTMLTableSectionElement,
|
|
@@ -2183,8 +2474,8 @@ const TableHeader = React.forwardRef<
|
|
|
2183
2474
|
className={cn("bg-[#FAFAFA] [&_tr]:border-b", className)}
|
|
2184
2475
|
{...props}
|
|
2185
2476
|
/>
|
|
2186
|
-
))
|
|
2187
|
-
TableHeader.displayName = "TableHeader"
|
|
2477
|
+
));
|
|
2478
|
+
TableHeader.displayName = "TableHeader";
|
|
2188
2479
|
|
|
2189
2480
|
const TableBody = React.forwardRef<
|
|
2190
2481
|
HTMLTableSectionElement,
|
|
@@ -2195,8 +2486,8 @@ const TableBody = React.forwardRef<
|
|
|
2195
2486
|
className={cn("[&_tr:last-child]:border-0", className)}
|
|
2196
2487
|
{...props}
|
|
2197
2488
|
/>
|
|
2198
|
-
))
|
|
2199
|
-
TableBody.displayName = "TableBody"
|
|
2489
|
+
));
|
|
2490
|
+
TableBody.displayName = "TableBody";
|
|
2200
2491
|
|
|
2201
2492
|
const TableFooter = React.forwardRef<
|
|
2202
2493
|
HTMLTableSectionElement,
|
|
@@ -2210,12 +2501,12 @@ const TableFooter = React.forwardRef<
|
|
|
2210
2501
|
)}
|
|
2211
2502
|
{...props}
|
|
2212
2503
|
/>
|
|
2213
|
-
))
|
|
2214
|
-
TableFooter.displayName = "TableFooter"
|
|
2504
|
+
));
|
|
2505
|
+
TableFooter.displayName = "TableFooter";
|
|
2215
2506
|
|
|
2216
2507
|
export interface TableRowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
|
2217
2508
|
/** Highlight the row with a colored background */
|
|
2218
|
-
highlighted?: boolean
|
|
2509
|
+
highlighted?: boolean;
|
|
2219
2510
|
}
|
|
2220
2511
|
|
|
2221
2512
|
const TableRow = React.forwardRef<HTMLTableRowElement, TableRowProps>(
|
|
@@ -2232,20 +2523,23 @@ const TableRow = React.forwardRef<HTMLTableRowElement, TableRowProps>(
|
|
|
2232
2523
|
{...props}
|
|
2233
2524
|
/>
|
|
2234
2525
|
)
|
|
2235
|
-
)
|
|
2236
|
-
TableRow.displayName = "TableRow"
|
|
2526
|
+
);
|
|
2527
|
+
TableRow.displayName = "TableRow";
|
|
2237
2528
|
|
|
2238
2529
|
export interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
|
|
2239
2530
|
/** Make this column sticky on horizontal scroll */
|
|
2240
|
-
sticky?: boolean
|
|
2531
|
+
sticky?: boolean;
|
|
2241
2532
|
/** Sort direction indicator */
|
|
2242
|
-
sortDirection?:
|
|
2533
|
+
sortDirection?: "asc" | "desc" | null;
|
|
2243
2534
|
/** Show info icon with tooltip */
|
|
2244
|
-
infoTooltip?: string
|
|
2535
|
+
infoTooltip?: string;
|
|
2245
2536
|
}
|
|
2246
2537
|
|
|
2247
2538
|
const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
2248
|
-
(
|
|
2539
|
+
(
|
|
2540
|
+
{ className, sticky, sortDirection, infoTooltip, children, ...props },
|
|
2541
|
+
ref
|
|
2542
|
+
) => (
|
|
2249
2543
|
<th
|
|
2250
2544
|
ref={ref}
|
|
2251
2545
|
className={cn(
|
|
@@ -2260,7 +2554,7 @@ const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
|
2260
2554
|
{children}
|
|
2261
2555
|
{sortDirection && (
|
|
2262
2556
|
<span className="text-[#A4A7AE]">
|
|
2263
|
-
{sortDirection ===
|
|
2557
|
+
{sortDirection === "asc" ? "\u2191" : "\u2193"}
|
|
2264
2558
|
</span>
|
|
2265
2559
|
)}
|
|
2266
2560
|
{infoTooltip && (
|
|
@@ -2271,12 +2565,12 @@ const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
|
2271
2565
|
</div>
|
|
2272
2566
|
</th>
|
|
2273
2567
|
)
|
|
2274
|
-
)
|
|
2275
|
-
TableHead.displayName = "TableHead"
|
|
2568
|
+
);
|
|
2569
|
+
TableHead.displayName = "TableHead";
|
|
2276
2570
|
|
|
2277
2571
|
export interface TableCellProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
|
|
2278
2572
|
/** Make this cell sticky on horizontal scroll */
|
|
2279
|
-
sticky?: boolean
|
|
2573
|
+
sticky?: boolean;
|
|
2280
2574
|
}
|
|
2281
2575
|
|
|
2282
2576
|
const TableCell = React.forwardRef<HTMLTableCellElement, TableCellProps>(
|
|
@@ -2291,8 +2585,8 @@ const TableCell = React.forwardRef<HTMLTableCellElement, TableCellProps>(
|
|
|
2291
2585
|
{...props}
|
|
2292
2586
|
/>
|
|
2293
2587
|
)
|
|
2294
|
-
)
|
|
2295
|
-
TableCell.displayName = "TableCell"
|
|
2588
|
+
);
|
|
2589
|
+
TableCell.displayName = "TableCell";
|
|
2296
2590
|
|
|
2297
2591
|
const TableCaption = React.forwardRef<
|
|
2298
2592
|
HTMLTableCaptionElement,
|
|
@@ -2303,17 +2597,17 @@ const TableCaption = React.forwardRef<
|
|
|
2303
2597
|
className={cn("mt-4 text-sm text-[#717680]", className)}
|
|
2304
2598
|
{...props}
|
|
2305
2599
|
/>
|
|
2306
|
-
))
|
|
2307
|
-
TableCaption.displayName = "TableCaption"
|
|
2600
|
+
));
|
|
2601
|
+
TableCaption.displayName = "TableCaption";
|
|
2308
2602
|
|
|
2309
2603
|
/**
|
|
2310
2604
|
* TableSkeleton - Loading state for table rows
|
|
2311
2605
|
*/
|
|
2312
2606
|
export interface TableSkeletonProps {
|
|
2313
2607
|
/** Number of rows to show */
|
|
2314
|
-
rows?: number
|
|
2608
|
+
rows?: number;
|
|
2315
2609
|
/** Number of columns to show */
|
|
2316
|
-
columns?: number
|
|
2610
|
+
columns?: number;
|
|
2317
2611
|
}
|
|
2318
2612
|
|
|
2319
2613
|
const TableSkeleton = ({ rows = 5, columns = 5 }: TableSkeletonProps) => (
|
|
@@ -2322,24 +2616,28 @@ const TableSkeleton = ({ rows = 5, columns = 5 }: TableSkeletonProps) => (
|
|
|
2322
2616
|
<TableRow key={rowIndex}>
|
|
2323
2617
|
{Array.from({ length: columns }).map((_, colIndex) => (
|
|
2324
2618
|
<TableCell key={colIndex}>
|
|
2325
|
-
<div
|
|
2326
|
-
|
|
2619
|
+
<div
|
|
2620
|
+
className="h-4 bg-[#E9EAEB] rounded animate-pulse"
|
|
2621
|
+
style={{
|
|
2622
|
+
width: colIndex === 1 ? "80%" : colIndex === 2 ? "30%" : "60%",
|
|
2623
|
+
}}
|
|
2624
|
+
/>
|
|
2327
2625
|
</TableCell>
|
|
2328
2626
|
))}
|
|
2329
2627
|
</TableRow>
|
|
2330
2628
|
))}
|
|
2331
2629
|
</>
|
|
2332
|
-
)
|
|
2333
|
-
TableSkeleton.displayName = "TableSkeleton"
|
|
2630
|
+
);
|
|
2631
|
+
TableSkeleton.displayName = "TableSkeleton";
|
|
2334
2632
|
|
|
2335
2633
|
/**
|
|
2336
2634
|
* TableEmpty - Empty state message
|
|
2337
2635
|
*/
|
|
2338
2636
|
export interface TableEmptyProps {
|
|
2339
2637
|
/** Number of columns to span */
|
|
2340
|
-
colSpan: number
|
|
2638
|
+
colSpan: number;
|
|
2341
2639
|
/** Custom message or component */
|
|
2342
|
-
children?: React.ReactNode
|
|
2640
|
+
children?: React.ReactNode;
|
|
2343
2641
|
}
|
|
2344
2642
|
|
|
2345
2643
|
const TableEmpty = ({ colSpan, children }: TableEmptyProps) => (
|
|
@@ -2348,17 +2646,17 @@ const TableEmpty = ({ colSpan, children }: TableEmptyProps) => (
|
|
|
2348
2646
|
{children || "No data available"}
|
|
2349
2647
|
</TableCell>
|
|
2350
2648
|
</TableRow>
|
|
2351
|
-
)
|
|
2352
|
-
TableEmpty.displayName = "TableEmpty"
|
|
2649
|
+
);
|
|
2650
|
+
TableEmpty.displayName = "TableEmpty";
|
|
2353
2651
|
|
|
2354
2652
|
/**
|
|
2355
2653
|
* Avatar component for table cells
|
|
2356
2654
|
*/
|
|
2357
2655
|
export interface TableAvatarProps {
|
|
2358
2656
|
/** Initials to display */
|
|
2359
|
-
initials: string
|
|
2657
|
+
initials: string;
|
|
2360
2658
|
/** Background color */
|
|
2361
|
-
color?: string
|
|
2659
|
+
color?: string;
|
|
2362
2660
|
}
|
|
2363
2661
|
|
|
2364
2662
|
const TableAvatar = ({ initials, color = "#7C3AED" }: TableAvatarProps) => (
|
|
@@ -2368,23 +2666,23 @@ const TableAvatar = ({ initials, color = "#7C3AED" }: TableAvatarProps) => (
|
|
|
2368
2666
|
>
|
|
2369
2667
|
{initials}
|
|
2370
2668
|
</div>
|
|
2371
|
-
)
|
|
2372
|
-
TableAvatar.displayName = "TableAvatar"
|
|
2669
|
+
);
|
|
2670
|
+
TableAvatar.displayName = "TableAvatar";
|
|
2373
2671
|
|
|
2374
2672
|
/**
|
|
2375
2673
|
* Switch component optimized for table cells (previously TableToggle)
|
|
2376
2674
|
*/
|
|
2377
|
-
export interface TableToggleProps extends Omit<SwitchProps,
|
|
2675
|
+
export interface TableToggleProps extends Omit<SwitchProps, "size"> {
|
|
2378
2676
|
/** Size of the switch - defaults to 'sm' for tables */
|
|
2379
|
-
size?:
|
|
2677
|
+
size?: "sm" | "default";
|
|
2380
2678
|
}
|
|
2381
2679
|
|
|
2382
2680
|
const TableToggle = React.forwardRef<HTMLButtonElement, TableToggleProps>(
|
|
2383
|
-
({ size =
|
|
2681
|
+
({ size = "sm", ...props }, ref) => (
|
|
2384
2682
|
<Switch ref={ref} size={size} {...props} />
|
|
2385
2683
|
)
|
|
2386
|
-
)
|
|
2387
|
-
TableToggle.displayName = "TableToggle"
|
|
2684
|
+
);
|
|
2685
|
+
TableToggle.displayName = "TableToggle";
|
|
2388
2686
|
|
|
2389
2687
|
export {
|
|
2390
2688
|
Table,
|
|
@@ -2400,7 +2698,7 @@ export {
|
|
|
2400
2698
|
TableAvatar,
|
|
2401
2699
|
TableToggle,
|
|
2402
2700
|
tableVariants,
|
|
2403
|
-
}
|
|
2701
|
+
};
|
|
2404
2702
|
`, prefix)
|
|
2405
2703
|
}
|
|
2406
2704
|
]
|
|
@@ -2417,28 +2715,28 @@ export {
|
|
|
2417
2715
|
files: [
|
|
2418
2716
|
{
|
|
2419
2717
|
name: "dropdown-menu.tsx",
|
|
2420
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
2421
|
-
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
2422
|
-
import { Check, ChevronRight, Circle } from "lucide-react"
|
|
2718
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
2719
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
2720
|
+
import { Check, ChevronRight, Circle } from "lucide-react";
|
|
2423
2721
|
|
|
2424
|
-
import { cn } from "../../lib/utils"
|
|
2722
|
+
import { cn } from "../../lib/utils";
|
|
2425
2723
|
|
|
2426
|
-
const DropdownMenu = DropdownMenuPrimitive.Root
|
|
2724
|
+
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
2427
2725
|
|
|
2428
|
-
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
|
2726
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
2429
2727
|
|
|
2430
|
-
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
|
2728
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
2431
2729
|
|
|
2432
|
-
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
|
2730
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
2433
2731
|
|
|
2434
|
-
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
|
2732
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
2435
2733
|
|
|
2436
|
-
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
|
2734
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
2437
2735
|
|
|
2438
2736
|
const DropdownMenuSubTrigger = React.forwardRef<
|
|
2439
2737
|
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
2440
2738
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
2441
|
-
inset?: boolean
|
|
2739
|
+
inset?: boolean;
|
|
2442
2740
|
}
|
|
2443
2741
|
>(({ className, inset, children, ...props }, ref) => (
|
|
2444
2742
|
<DropdownMenuPrimitive.SubTrigger
|
|
@@ -2453,9 +2751,9 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
|
|
2453
2751
|
{children}
|
|
2454
2752
|
<ChevronRight className="ml-auto h-4 w-4" />
|
|
2455
2753
|
</DropdownMenuPrimitive.SubTrigger>
|
|
2456
|
-
))
|
|
2754
|
+
));
|
|
2457
2755
|
DropdownMenuSubTrigger.displayName =
|
|
2458
|
-
DropdownMenuPrimitive.SubTrigger.displayName
|
|
2756
|
+
DropdownMenuPrimitive.SubTrigger.displayName;
|
|
2459
2757
|
|
|
2460
2758
|
const DropdownMenuSubContent = React.forwardRef<
|
|
2461
2759
|
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
|
@@ -2469,9 +2767,9 @@ const DropdownMenuSubContent = React.forwardRef<
|
|
|
2469
2767
|
)}
|
|
2470
2768
|
{...props}
|
|
2471
2769
|
/>
|
|
2472
|
-
))
|
|
2770
|
+
));
|
|
2473
2771
|
DropdownMenuSubContent.displayName =
|
|
2474
|
-
DropdownMenuPrimitive.SubContent.displayName
|
|
2772
|
+
DropdownMenuPrimitive.SubContent.displayName;
|
|
2475
2773
|
|
|
2476
2774
|
const DropdownMenuContent = React.forwardRef<
|
|
2477
2775
|
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
@@ -2489,13 +2787,13 @@ const DropdownMenuContent = React.forwardRef<
|
|
|
2489
2787
|
{...props}
|
|
2490
2788
|
/>
|
|
2491
2789
|
</DropdownMenuPrimitive.Portal>
|
|
2492
|
-
))
|
|
2493
|
-
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
|
2790
|
+
));
|
|
2791
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
2494
2792
|
|
|
2495
2793
|
const DropdownMenuItem = React.forwardRef<
|
|
2496
2794
|
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
2497
2795
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
|
2498
|
-
inset?: boolean
|
|
2796
|
+
inset?: boolean;
|
|
2499
2797
|
}
|
|
2500
2798
|
>(({ className, inset, ...props }, ref) => (
|
|
2501
2799
|
<DropdownMenuPrimitive.Item
|
|
@@ -2507,8 +2805,8 @@ const DropdownMenuItem = React.forwardRef<
|
|
|
2507
2805
|
)}
|
|
2508
2806
|
{...props}
|
|
2509
2807
|
/>
|
|
2510
|
-
))
|
|
2511
|
-
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
|
2808
|
+
));
|
|
2809
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
2512
2810
|
|
|
2513
2811
|
const DropdownMenuCheckboxItem = React.forwardRef<
|
|
2514
2812
|
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
|
@@ -2530,9 +2828,9 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|
|
2530
2828
|
</span>
|
|
2531
2829
|
{children}
|
|
2532
2830
|
</DropdownMenuPrimitive.CheckboxItem>
|
|
2533
|
-
))
|
|
2831
|
+
));
|
|
2534
2832
|
DropdownMenuCheckboxItem.displayName =
|
|
2535
|
-
DropdownMenuPrimitive.CheckboxItem.displayName
|
|
2833
|
+
DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
2536
2834
|
|
|
2537
2835
|
const DropdownMenuRadioItem = React.forwardRef<
|
|
2538
2836
|
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
|
@@ -2553,13 +2851,13 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|
|
2553
2851
|
</span>
|
|
2554
2852
|
{children}
|
|
2555
2853
|
</DropdownMenuPrimitive.RadioItem>
|
|
2556
|
-
))
|
|
2557
|
-
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
|
2854
|
+
));
|
|
2855
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
2558
2856
|
|
|
2559
2857
|
const DropdownMenuLabel = React.forwardRef<
|
|
2560
2858
|
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
|
2561
2859
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
|
2562
|
-
inset?: boolean
|
|
2860
|
+
inset?: boolean;
|
|
2563
2861
|
}
|
|
2564
2862
|
>(({ className, inset, ...props }, ref) => (
|
|
2565
2863
|
<DropdownMenuPrimitive.Label
|
|
@@ -2571,8 +2869,8 @@ const DropdownMenuLabel = React.forwardRef<
|
|
|
2571
2869
|
)}
|
|
2572
2870
|
{...props}
|
|
2573
2871
|
/>
|
|
2574
|
-
))
|
|
2575
|
-
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
|
2872
|
+
));
|
|
2873
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
2576
2874
|
|
|
2577
2875
|
const DropdownMenuSeparator = React.forwardRef<
|
|
2578
2876
|
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
|
@@ -2583,8 +2881,8 @@ const DropdownMenuSeparator = React.forwardRef<
|
|
|
2583
2881
|
className={cn("-mx-1 my-1 h-px bg-[#E9EAEB]", className)}
|
|
2584
2882
|
{...props}
|
|
2585
2883
|
/>
|
|
2586
|
-
))
|
|
2587
|
-
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
|
2884
|
+
));
|
|
2885
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
2588
2886
|
|
|
2589
2887
|
const DropdownMenuShortcut = ({
|
|
2590
2888
|
className,
|
|
@@ -2595,9 +2893,9 @@ const DropdownMenuShortcut = ({
|
|
|
2595
2893
|
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
|
2596
2894
|
{...props}
|
|
2597
2895
|
/>
|
|
2598
|
-
)
|
|
2599
|
-
}
|
|
2600
|
-
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
|
2896
|
+
);
|
|
2897
|
+
};
|
|
2898
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
2601
2899
|
|
|
2602
2900
|
export {
|
|
2603
2901
|
DropdownMenu,
|
|
@@ -2615,7 +2913,7 @@ export {
|
|
|
2615
2913
|
DropdownMenuSubContent,
|
|
2616
2914
|
DropdownMenuSubTrigger,
|
|
2617
2915
|
DropdownMenuRadioGroup,
|
|
2618
|
-
}
|
|
2916
|
+
};
|
|
2619
2917
|
`, prefix)
|
|
2620
2918
|
}
|
|
2621
2919
|
]
|
|
@@ -2631,16 +2929,16 @@ export {
|
|
|
2631
2929
|
files: [
|
|
2632
2930
|
{
|
|
2633
2931
|
name: "tooltip.tsx",
|
|
2634
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
2635
|
-
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
|
2932
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
2933
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
2636
2934
|
|
|
2637
|
-
import { cn } from "../../lib/utils"
|
|
2935
|
+
import { cn } from "../../lib/utils";
|
|
2638
2936
|
|
|
2639
|
-
const TooltipProvider = TooltipPrimitive.Provider
|
|
2937
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
2640
2938
|
|
|
2641
|
-
const Tooltip = TooltipPrimitive.Root
|
|
2939
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
2642
2940
|
|
|
2643
|
-
const TooltipTrigger = TooltipPrimitive.Trigger
|
|
2941
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
2644
2942
|
|
|
2645
2943
|
const TooltipContent = React.forwardRef<
|
|
2646
2944
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
@@ -2657,8 +2955,8 @@ const TooltipContent = React.forwardRef<
|
|
|
2657
2955
|
{...props}
|
|
2658
2956
|
/>
|
|
2659
2957
|
</TooltipPrimitive.Portal>
|
|
2660
|
-
))
|
|
2661
|
-
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
|
2958
|
+
));
|
|
2959
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
2662
2960
|
|
|
2663
2961
|
const TooltipArrow = React.forwardRef<
|
|
2664
2962
|
React.ElementRef<typeof TooltipPrimitive.Arrow>,
|
|
@@ -2669,8 +2967,8 @@ const TooltipArrow = React.forwardRef<
|
|
|
2669
2967
|
className={cn("fill-[#343E55]", className)}
|
|
2670
2968
|
{...props}
|
|
2671
2969
|
/>
|
|
2672
|
-
))
|
|
2673
|
-
TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName
|
|
2970
|
+
));
|
|
2971
|
+
TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName;
|
|
2674
2972
|
|
|
2675
2973
|
export {
|
|
2676
2974
|
Tooltip,
|
|
@@ -2678,7 +2976,7 @@ export {
|
|
|
2678
2976
|
TooltipContent,
|
|
2679
2977
|
TooltipArrow,
|
|
2680
2978
|
TooltipProvider,
|
|
2681
|
-
}
|
|
2979
|
+
};
|
|
2682
2980
|
`, prefix)
|
|
2683
2981
|
}
|
|
2684
2982
|
]
|
|
@@ -2694,41 +2992,38 @@ export {
|
|
|
2694
2992
|
files: [
|
|
2695
2993
|
{
|
|
2696
2994
|
name: "tag.tsx",
|
|
2697
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
2698
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
2995
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
2996
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
2699
2997
|
|
|
2700
|
-
import { cn } from "../../lib/utils"
|
|
2998
|
+
import { cn } from "../../lib/utils";
|
|
2701
2999
|
|
|
2702
3000
|
/**
|
|
2703
3001
|
* Tag variants for event labels and categories.
|
|
2704
3002
|
* Rounded rectangle tags with optional bold labels.
|
|
2705
3003
|
*/
|
|
2706
|
-
const tagVariants = cva(
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
error: "bg-[#FEF3F2] text-[#F04438]",
|
|
2718
|
-
destructive: "bg-[#FEF3F2] text-[#F04438]",
|
|
2719
|
-
},
|
|
2720
|
-
size: {
|
|
2721
|
-
default: "px-2 py-1",
|
|
2722
|
-
sm: "px-1.5 py-0.5 text-xs",
|
|
2723
|
-
lg: "px-3 py-1.5",
|
|
2724
|
-
},
|
|
3004
|
+
const tagVariants = cva("inline-flex items-center rounded text-sm", {
|
|
3005
|
+
variants: {
|
|
3006
|
+
variant: {
|
|
3007
|
+
default: "bg-[#F5F5F5] text-[#181D27]",
|
|
3008
|
+
primary: "bg-[#F5F5F5] text-[#181D27]",
|
|
3009
|
+
accent: "bg-[#EBECEE] text-[#343E55]",
|
|
3010
|
+
secondary: "bg-[#E9EAEB] text-[#414651]",
|
|
3011
|
+
success: "bg-[#ECFDF3] text-[#17B26A]",
|
|
3012
|
+
warning: "bg-[#FFFAEB] text-[#F79009]",
|
|
3013
|
+
error: "bg-[#FEF3F2] text-[#F04438]",
|
|
3014
|
+
destructive: "bg-[#FEF3F2] text-[#F04438]",
|
|
2725
3015
|
},
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
3016
|
+
size: {
|
|
3017
|
+
default: "px-2 py-1",
|
|
3018
|
+
sm: "px-1.5 py-0.5 text-xs",
|
|
3019
|
+
lg: "px-3 py-1.5",
|
|
2729
3020
|
},
|
|
2730
|
-
}
|
|
2731
|
-
|
|
3021
|
+
},
|
|
3022
|
+
defaultVariants: {
|
|
3023
|
+
variant: "default",
|
|
3024
|
+
size: "default",
|
|
3025
|
+
},
|
|
3026
|
+
});
|
|
2732
3027
|
|
|
2733
3028
|
/**
|
|
2734
3029
|
* Tag component for displaying event labels and categories.
|
|
@@ -2740,10 +3035,11 @@ const tagVariants = cva(
|
|
|
2740
3035
|
* \`\`\`
|
|
2741
3036
|
*/
|
|
2742
3037
|
export interface TagProps
|
|
2743
|
-
extends
|
|
3038
|
+
extends
|
|
3039
|
+
React.HTMLAttributes<HTMLSpanElement>,
|
|
2744
3040
|
VariantProps<typeof tagVariants> {
|
|
2745
3041
|
/** Bold label prefix displayed before the content */
|
|
2746
|
-
label?: string
|
|
3042
|
+
label?: string;
|
|
2747
3043
|
}
|
|
2748
3044
|
|
|
2749
3045
|
const Tag = React.forwardRef<HTMLSpanElement, TagProps>(
|
|
@@ -2754,15 +3050,13 @@ const Tag = React.forwardRef<HTMLSpanElement, TagProps>(
|
|
|
2754
3050
|
ref={ref}
|
|
2755
3051
|
{...props}
|
|
2756
3052
|
>
|
|
2757
|
-
{label &&
|
|
2758
|
-
<span className="font-semibold mr-1">{label}</span>
|
|
2759
|
-
)}
|
|
3053
|
+
{label && <span className="font-semibold mr-1">{label}</span>}
|
|
2760
3054
|
<span className="font-normal">{children}</span>
|
|
2761
3055
|
</span>
|
|
2762
|
-
)
|
|
3056
|
+
);
|
|
2763
3057
|
}
|
|
2764
|
-
)
|
|
2765
|
-
Tag.displayName = "Tag"
|
|
3058
|
+
);
|
|
3059
|
+
Tag.displayName = "Tag";
|
|
2766
3060
|
|
|
2767
3061
|
/**
|
|
2768
3062
|
* TagGroup component for displaying multiple tags with overflow indicator.
|
|
@@ -2781,15 +3075,15 @@ Tag.displayName = "Tag"
|
|
|
2781
3075
|
*/
|
|
2782
3076
|
export interface TagGroupProps {
|
|
2783
3077
|
/** Array of tags to display */
|
|
2784
|
-
tags: Array<{ label?: string; value: string }
|
|
3078
|
+
tags: Array<{ label?: string; value: string }>;
|
|
2785
3079
|
/** Maximum number of tags to show before overflow (default: 2) */
|
|
2786
|
-
maxVisible?: number
|
|
3080
|
+
maxVisible?: number;
|
|
2787
3081
|
/** Tag variant */
|
|
2788
|
-
variant?: TagProps[
|
|
3082
|
+
variant?: TagProps["variant"];
|
|
2789
3083
|
/** Tag size */
|
|
2790
|
-
size?: TagProps[
|
|
3084
|
+
size?: TagProps["size"];
|
|
2791
3085
|
/** Additional className for the container */
|
|
2792
|
-
className?: string
|
|
3086
|
+
className?: string;
|
|
2793
3087
|
}
|
|
2794
3088
|
|
|
2795
3089
|
const TagGroup = ({
|
|
@@ -2799,13 +3093,14 @@ const TagGroup = ({
|
|
|
2799
3093
|
size,
|
|
2800
3094
|
className,
|
|
2801
3095
|
}: TagGroupProps) => {
|
|
2802
|
-
const visibleTags = tags.slice(0, maxVisible)
|
|
2803
|
-
const overflowCount = tags.length - maxVisible
|
|
3096
|
+
const visibleTags = tags.slice(0, maxVisible);
|
|
3097
|
+
const overflowCount = tags.length - maxVisible;
|
|
2804
3098
|
|
|
2805
3099
|
return (
|
|
2806
3100
|
<div className={cn("flex flex-col items-start gap-2", className)}>
|
|
2807
3101
|
{visibleTags.map((tag, index) => {
|
|
2808
|
-
const isLastVisible =
|
|
3102
|
+
const isLastVisible =
|
|
3103
|
+
index === visibleTags.length - 1 && overflowCount > 0;
|
|
2809
3104
|
|
|
2810
3105
|
if (isLastVisible) {
|
|
2811
3106
|
return (
|
|
@@ -2817,21 +3112,21 @@ const TagGroup = ({
|
|
|
2817
3112
|
+{overflowCount} more
|
|
2818
3113
|
</Tag>
|
|
2819
3114
|
</div>
|
|
2820
|
-
)
|
|
3115
|
+
);
|
|
2821
3116
|
}
|
|
2822
3117
|
|
|
2823
3118
|
return (
|
|
2824
3119
|
<Tag key={index} label={tag.label} variant={variant} size={size}>
|
|
2825
3120
|
{tag.value}
|
|
2826
3121
|
</Tag>
|
|
2827
|
-
)
|
|
3122
|
+
);
|
|
2828
3123
|
})}
|
|
2829
3124
|
</div>
|
|
2830
|
-
)
|
|
2831
|
-
}
|
|
2832
|
-
TagGroup.displayName = "TagGroup"
|
|
3125
|
+
);
|
|
3126
|
+
};
|
|
3127
|
+
TagGroup.displayName = "TagGroup";
|
|
2833
3128
|
|
|
2834
|
-
export { Tag, TagGroup, tagVariants }
|
|
3129
|
+
export { Tag, TagGroup, tagVariants };
|
|
2835
3130
|
`, prefix)
|
|
2836
3131
|
}
|
|
2837
3132
|
]
|
|
@@ -2848,11 +3143,11 @@ export { Tag, TagGroup, tagVariants }
|
|
|
2848
3143
|
files: [
|
|
2849
3144
|
{
|
|
2850
3145
|
name: "accordion.tsx",
|
|
2851
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
2852
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
2853
|
-
import { ChevronDown } from "lucide-react"
|
|
3146
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
3147
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3148
|
+
import { ChevronDown } from "lucide-react";
|
|
2854
3149
|
|
|
2855
|
-
import { cn } from "../../lib/utils"
|
|
3150
|
+
import { cn } from "../../lib/utils";
|
|
2856
3151
|
|
|
2857
3152
|
/**
|
|
2858
3153
|
* Accordion root variants
|
|
@@ -2867,7 +3162,7 @@ const accordionVariants = cva("w-full", {
|
|
|
2867
3162
|
defaultVariants: {
|
|
2868
3163
|
variant: "default",
|
|
2869
3164
|
},
|
|
2870
|
-
})
|
|
3165
|
+
});
|
|
2871
3166
|
|
|
2872
3167
|
/**
|
|
2873
3168
|
* Accordion item variants
|
|
@@ -2882,7 +3177,7 @@ const accordionItemVariants = cva("", {
|
|
|
2882
3177
|
defaultVariants: {
|
|
2883
3178
|
variant: "default",
|
|
2884
3179
|
},
|
|
2885
|
-
})
|
|
3180
|
+
});
|
|
2886
3181
|
|
|
2887
3182
|
/**
|
|
2888
3183
|
* Accordion trigger variants
|
|
@@ -2900,7 +3195,7 @@ const accordionTriggerVariants = cva(
|
|
|
2900
3195
|
variant: "default",
|
|
2901
3196
|
},
|
|
2902
3197
|
}
|
|
2903
|
-
)
|
|
3198
|
+
);
|
|
2904
3199
|
|
|
2905
3200
|
/**
|
|
2906
3201
|
* Accordion content variants
|
|
@@ -2918,58 +3213,64 @@ const accordionContentVariants = cva(
|
|
|
2918
3213
|
variant: "default",
|
|
2919
3214
|
},
|
|
2920
3215
|
}
|
|
2921
|
-
)
|
|
3216
|
+
);
|
|
2922
3217
|
|
|
2923
3218
|
// Types
|
|
2924
|
-
type AccordionType = "single" | "multiple"
|
|
3219
|
+
type AccordionType = "single" | "multiple";
|
|
2925
3220
|
|
|
2926
3221
|
interface AccordionContextValue {
|
|
2927
|
-
type: AccordionType
|
|
2928
|
-
value: string[]
|
|
2929
|
-
onValueChange: (value: string[]) => void
|
|
2930
|
-
variant: "default" | "bordered"
|
|
3222
|
+
type: AccordionType;
|
|
3223
|
+
value: string[];
|
|
3224
|
+
onValueChange: (value: string[]) => void;
|
|
3225
|
+
variant: "default" | "bordered";
|
|
2931
3226
|
}
|
|
2932
3227
|
|
|
2933
3228
|
interface AccordionItemContextValue {
|
|
2934
|
-
value: string
|
|
2935
|
-
isOpen: boolean
|
|
2936
|
-
disabled?: boolean
|
|
3229
|
+
value: string;
|
|
3230
|
+
isOpen: boolean;
|
|
3231
|
+
disabled?: boolean;
|
|
2937
3232
|
}
|
|
2938
3233
|
|
|
2939
3234
|
// Contexts
|
|
2940
|
-
const AccordionContext = React.createContext<AccordionContextValue | null>(
|
|
2941
|
-
|
|
3235
|
+
const AccordionContext = React.createContext<AccordionContextValue | null>(
|
|
3236
|
+
null
|
|
3237
|
+
);
|
|
3238
|
+
const AccordionItemContext =
|
|
3239
|
+
React.createContext<AccordionItemContextValue | null>(null);
|
|
2942
3240
|
|
|
2943
3241
|
function useAccordionContext() {
|
|
2944
|
-
const context = React.useContext(AccordionContext)
|
|
3242
|
+
const context = React.useContext(AccordionContext);
|
|
2945
3243
|
if (!context) {
|
|
2946
|
-
throw new Error("Accordion components must be used within an Accordion")
|
|
3244
|
+
throw new Error("Accordion components must be used within an Accordion");
|
|
2947
3245
|
}
|
|
2948
|
-
return context
|
|
3246
|
+
return context;
|
|
2949
3247
|
}
|
|
2950
3248
|
|
|
2951
3249
|
function useAccordionItemContext() {
|
|
2952
|
-
const context = React.useContext(AccordionItemContext)
|
|
3250
|
+
const context = React.useContext(AccordionItemContext);
|
|
2953
3251
|
if (!context) {
|
|
2954
|
-
throw new Error(
|
|
3252
|
+
throw new Error(
|
|
3253
|
+
"AccordionTrigger/AccordionContent must be used within an AccordionItem"
|
|
3254
|
+
);
|
|
2955
3255
|
}
|
|
2956
|
-
return context
|
|
3256
|
+
return context;
|
|
2957
3257
|
}
|
|
2958
3258
|
|
|
2959
3259
|
/**
|
|
2960
3260
|
* Root accordion component that manages state
|
|
2961
3261
|
*/
|
|
2962
3262
|
export interface AccordionProps
|
|
2963
|
-
extends
|
|
3263
|
+
extends
|
|
3264
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
2964
3265
|
VariantProps<typeof accordionVariants> {
|
|
2965
3266
|
/** Whether only one item can be open at a time ('single') or multiple ('multiple') */
|
|
2966
|
-
type?: AccordionType
|
|
3267
|
+
type?: AccordionType;
|
|
2967
3268
|
/** Controlled value - array of open item values */
|
|
2968
|
-
value?: string[]
|
|
3269
|
+
value?: string[];
|
|
2969
3270
|
/** Default open items for uncontrolled usage */
|
|
2970
|
-
defaultValue?: string[]
|
|
3271
|
+
defaultValue?: string[];
|
|
2971
3272
|
/** Callback when open items change */
|
|
2972
|
-
onValueChange?: (value: string[]) => void
|
|
3273
|
+
onValueChange?: (value: string[]) => void;
|
|
2973
3274
|
}
|
|
2974
3275
|
|
|
2975
3276
|
const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
@@ -2986,20 +3287,21 @@ const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
|
2986
3287
|
},
|
|
2987
3288
|
ref
|
|
2988
3289
|
) => {
|
|
2989
|
-
const [internalValue, setInternalValue] =
|
|
3290
|
+
const [internalValue, setInternalValue] =
|
|
3291
|
+
React.useState<string[]>(defaultValue);
|
|
2990
3292
|
|
|
2991
|
-
const isControlled = controlledValue !== undefined
|
|
2992
|
-
const currentValue = isControlled ? controlledValue : internalValue
|
|
3293
|
+
const isControlled = controlledValue !== undefined;
|
|
3294
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
2993
3295
|
|
|
2994
3296
|
const handleValueChange = React.useCallback(
|
|
2995
3297
|
(newValue: string[]) => {
|
|
2996
3298
|
if (!isControlled) {
|
|
2997
|
-
setInternalValue(newValue)
|
|
3299
|
+
setInternalValue(newValue);
|
|
2998
3300
|
}
|
|
2999
|
-
onValueChange?.(newValue)
|
|
3301
|
+
onValueChange?.(newValue);
|
|
3000
3302
|
},
|
|
3001
3303
|
[isControlled, onValueChange]
|
|
3002
|
-
)
|
|
3304
|
+
);
|
|
3003
3305
|
|
|
3004
3306
|
const contextValue = React.useMemo(
|
|
3005
3307
|
() => ({
|
|
@@ -3009,7 +3311,7 @@ const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
|
3009
3311
|
variant: variant || "default",
|
|
3010
3312
|
}),
|
|
3011
3313
|
[type, currentValue, handleValueChange, variant]
|
|
3012
|
-
)
|
|
3314
|
+
);
|
|
3013
3315
|
|
|
3014
3316
|
return (
|
|
3015
3317
|
<AccordionContext.Provider value={contextValue}>
|
|
@@ -3021,27 +3323,28 @@ const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
|
3021
3323
|
{children}
|
|
3022
3324
|
</div>
|
|
3023
3325
|
</AccordionContext.Provider>
|
|
3024
|
-
)
|
|
3326
|
+
);
|
|
3025
3327
|
}
|
|
3026
|
-
)
|
|
3027
|
-
Accordion.displayName = "Accordion"
|
|
3328
|
+
);
|
|
3329
|
+
Accordion.displayName = "Accordion";
|
|
3028
3330
|
|
|
3029
3331
|
/**
|
|
3030
3332
|
* Individual accordion item
|
|
3031
3333
|
*/
|
|
3032
3334
|
export interface AccordionItemProps
|
|
3033
|
-
extends
|
|
3335
|
+
extends
|
|
3336
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
3034
3337
|
VariantProps<typeof accordionItemVariants> {
|
|
3035
3338
|
/** Unique value for this item */
|
|
3036
|
-
value: string
|
|
3339
|
+
value: string;
|
|
3037
3340
|
/** Whether this item is disabled */
|
|
3038
|
-
disabled?: boolean
|
|
3341
|
+
disabled?: boolean;
|
|
3039
3342
|
}
|
|
3040
3343
|
|
|
3041
3344
|
const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
|
|
3042
3345
|
({ className, value, disabled, children, ...props }, ref) => {
|
|
3043
|
-
const { value: openValues, variant } = useAccordionContext()
|
|
3044
|
-
const isOpen = openValues.includes(value)
|
|
3346
|
+
const { value: openValues, variant } = useAccordionContext();
|
|
3347
|
+
const isOpen = openValues.includes(value);
|
|
3045
3348
|
|
|
3046
3349
|
const contextValue = React.useMemo(
|
|
3047
3350
|
() => ({
|
|
@@ -3050,7 +3353,7 @@ const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
|
|
|
3050
3353
|
disabled,
|
|
3051
3354
|
}),
|
|
3052
3355
|
[value, isOpen, disabled]
|
|
3053
|
-
)
|
|
3356
|
+
);
|
|
3054
3357
|
|
|
3055
3358
|
return (
|
|
3056
3359
|
<AccordionItemContext.Provider value={contextValue}>
|
|
@@ -3063,106 +3366,115 @@ const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
|
|
|
3063
3366
|
{children}
|
|
3064
3367
|
</div>
|
|
3065
3368
|
</AccordionItemContext.Provider>
|
|
3066
|
-
)
|
|
3369
|
+
);
|
|
3067
3370
|
}
|
|
3068
|
-
)
|
|
3069
|
-
AccordionItem.displayName = "AccordionItem"
|
|
3371
|
+
);
|
|
3372
|
+
AccordionItem.displayName = "AccordionItem";
|
|
3070
3373
|
|
|
3071
3374
|
/**
|
|
3072
3375
|
* Trigger button that toggles the accordion item
|
|
3073
3376
|
*/
|
|
3074
3377
|
export interface AccordionTriggerProps
|
|
3075
|
-
extends
|
|
3378
|
+
extends
|
|
3379
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
3076
3380
|
VariantProps<typeof accordionTriggerVariants> {
|
|
3077
3381
|
/** Whether to show the chevron icon */
|
|
3078
|
-
showChevron?: boolean
|
|
3382
|
+
showChevron?: boolean;
|
|
3079
3383
|
}
|
|
3080
3384
|
|
|
3081
|
-
const AccordionTrigger = React.forwardRef<
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3385
|
+
const AccordionTrigger = React.forwardRef<
|
|
3386
|
+
HTMLButtonElement,
|
|
3387
|
+
AccordionTriggerProps
|
|
3388
|
+
>(({ className, showChevron = true, children, ...props }, ref) => {
|
|
3389
|
+
const {
|
|
3390
|
+
type,
|
|
3391
|
+
value: openValues,
|
|
3392
|
+
onValueChange,
|
|
3393
|
+
variant,
|
|
3394
|
+
} = useAccordionContext();
|
|
3395
|
+
const { value, isOpen, disabled } = useAccordionItemContext();
|
|
3085
3396
|
|
|
3086
|
-
|
|
3087
|
-
|
|
3397
|
+
const handleClick = () => {
|
|
3398
|
+
if (disabled) return;
|
|
3088
3399
|
|
|
3089
|
-
|
|
3400
|
+
let newValue: string[];
|
|
3090
3401
|
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
}
|
|
3100
|
-
|
|
3101
|
-
onValueChange(newValue)
|
|
3402
|
+
if (type === "single") {
|
|
3403
|
+
// In single mode, toggle current item (close if open, open if closed)
|
|
3404
|
+
newValue = isOpen ? [] : [value];
|
|
3405
|
+
} else {
|
|
3406
|
+
// In multiple mode, toggle the item in the array
|
|
3407
|
+
newValue = isOpen
|
|
3408
|
+
? openValues.filter((v) => v !== value)
|
|
3409
|
+
: [...openValues, value];
|
|
3102
3410
|
}
|
|
3103
3411
|
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3412
|
+
onValueChange(newValue);
|
|
3413
|
+
};
|
|
3414
|
+
|
|
3415
|
+
return (
|
|
3416
|
+
<button
|
|
3417
|
+
ref={ref}
|
|
3418
|
+
type="button"
|
|
3419
|
+
aria-expanded={isOpen}
|
|
3420
|
+
disabled={disabled}
|
|
3421
|
+
onClick={handleClick}
|
|
3422
|
+
className={cn(accordionTriggerVariants({ variant, className }))}
|
|
3423
|
+
{...props}
|
|
3424
|
+
>
|
|
3425
|
+
<span className="flex-1">{children}</span>
|
|
3426
|
+
{showChevron && (
|
|
3427
|
+
<ChevronDown
|
|
3428
|
+
className={cn(
|
|
3429
|
+
"h-4 w-4 shrink-0 text-[#717680] transition-transform duration-300",
|
|
3430
|
+
isOpen && "rotate-180"
|
|
3431
|
+
)}
|
|
3432
|
+
/>
|
|
3433
|
+
)}
|
|
3434
|
+
</button>
|
|
3435
|
+
);
|
|
3436
|
+
});
|
|
3437
|
+
AccordionTrigger.displayName = "AccordionTrigger";
|
|
3128
3438
|
|
|
3129
3439
|
/**
|
|
3130
3440
|
* Content that is shown/hidden when the item is toggled
|
|
3131
3441
|
*/
|
|
3132
3442
|
export interface AccordionContentProps
|
|
3133
|
-
extends
|
|
3443
|
+
extends
|
|
3444
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
3134
3445
|
VariantProps<typeof accordionContentVariants> {}
|
|
3135
3446
|
|
|
3136
|
-
const AccordionContent = React.forwardRef<
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3447
|
+
const AccordionContent = React.forwardRef<
|
|
3448
|
+
HTMLDivElement,
|
|
3449
|
+
AccordionContentProps
|
|
3450
|
+
>(({ className, children, ...props }, ref) => {
|
|
3451
|
+
const { variant } = useAccordionContext();
|
|
3452
|
+
const { isOpen } = useAccordionItemContext();
|
|
3453
|
+
const contentRef = React.useRef<HTMLDivElement>(null);
|
|
3454
|
+
const [height, setHeight] = React.useState<number | undefined>(undefined);
|
|
3455
|
+
|
|
3456
|
+
React.useEffect(() => {
|
|
3457
|
+
if (contentRef.current) {
|
|
3458
|
+
const contentHeight = contentRef.current.scrollHeight;
|
|
3459
|
+
setHeight(isOpen ? contentHeight : 0);
|
|
3460
|
+
}
|
|
3461
|
+
}, [isOpen, children]);
|
|
3149
3462
|
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
</div>
|
|
3463
|
+
return (
|
|
3464
|
+
<div
|
|
3465
|
+
ref={ref}
|
|
3466
|
+
className={cn(accordionContentVariants({ variant, className }))}
|
|
3467
|
+
style={{ height: height !== undefined ? \`\${height}px\` : undefined }}
|
|
3468
|
+
aria-hidden={!isOpen}
|
|
3469
|
+
{...props}
|
|
3470
|
+
>
|
|
3471
|
+
<div ref={contentRef} className="pb-4">
|
|
3472
|
+
{children}
|
|
3161
3473
|
</div>
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
)
|
|
3165
|
-
AccordionContent.displayName = "AccordionContent"
|
|
3474
|
+
</div>
|
|
3475
|
+
);
|
|
3476
|
+
});
|
|
3477
|
+
AccordionContent.displayName = "AccordionContent";
|
|
3166
3478
|
|
|
3167
3479
|
export {
|
|
3168
3480
|
Accordion,
|
|
@@ -3173,7 +3485,7 @@ export {
|
|
|
3173
3485
|
accordionItemVariants,
|
|
3174
3486
|
accordionTriggerVariants,
|
|
3175
3487
|
accordionContentVariants,
|
|
3176
|
-
}
|
|
3488
|
+
};
|
|
3177
3489
|
`, prefix)
|
|
3178
3490
|
}
|
|
3179
3491
|
]
|
|
@@ -3190,12 +3502,12 @@ export {
|
|
|
3190
3502
|
files: [
|
|
3191
3503
|
{
|
|
3192
3504
|
name: "page-header.tsx",
|
|
3193
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
3194
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3195
|
-
import { ArrowLeft, MoreHorizontal, X } from "lucide-react"
|
|
3505
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
3506
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3507
|
+
import { ArrowLeft, MoreHorizontal, X } from "lucide-react";
|
|
3196
3508
|
|
|
3197
|
-
import { cn } from "../../lib/utils"
|
|
3198
|
-
import { Button } from "./button"
|
|
3509
|
+
import { cn } from "../../lib/utils";
|
|
3510
|
+
import { Button } from "./button";
|
|
3199
3511
|
|
|
3200
3512
|
/**
|
|
3201
3513
|
* PageHeader variants for layout styles.
|
|
@@ -3206,7 +3518,7 @@ const pageHeaderVariants = cva(
|
|
|
3206
3518
|
variants: {},
|
|
3207
3519
|
defaultVariants: {},
|
|
3208
3520
|
}
|
|
3209
|
-
)
|
|
3521
|
+
);
|
|
3210
3522
|
|
|
3211
3523
|
/**
|
|
3212
3524
|
* Page header component for displaying page titles with optional icons and actions.
|
|
@@ -3239,47 +3551,51 @@ const pageHeaderVariants = cva(
|
|
|
3239
3551
|
* \`\`\`
|
|
3240
3552
|
*/
|
|
3241
3553
|
export interface PageHeaderProps
|
|
3242
|
-
extends
|
|
3554
|
+
extends
|
|
3555
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
3243
3556
|
VariantProps<typeof pageHeaderVariants> {
|
|
3244
3557
|
/** Page title (required) */
|
|
3245
|
-
title: string
|
|
3558
|
+
title: string;
|
|
3246
3559
|
/** Optional description/subtitle displayed below the title */
|
|
3247
|
-
description?: string
|
|
3560
|
+
description?: string;
|
|
3248
3561
|
/** Icon displayed on the left side (hidden when showBackButton is true) */
|
|
3249
|
-
icon?: React.ReactNode
|
|
3562
|
+
icon?: React.ReactNode;
|
|
3250
3563
|
/** Shows back arrow button instead of icon */
|
|
3251
|
-
showBackButton?: boolean
|
|
3564
|
+
showBackButton?: boolean;
|
|
3252
3565
|
/** Callback when back button is clicked */
|
|
3253
|
-
onBackClick?: () => void
|
|
3566
|
+
onBackClick?: () => void;
|
|
3254
3567
|
/** Optional info icon displayed next to the title (e.g., tooltip trigger) */
|
|
3255
|
-
infoIcon?: React.ReactNode
|
|
3568
|
+
infoIcon?: React.ReactNode;
|
|
3256
3569
|
/** Action buttons/elements rendered on the right side */
|
|
3257
|
-
actions?: React.ReactNode
|
|
3570
|
+
actions?: React.ReactNode;
|
|
3258
3571
|
/** Show bottom border (default: true) */
|
|
3259
|
-
showBorder?: boolean
|
|
3572
|
+
showBorder?: boolean;
|
|
3260
3573
|
/** Layout mode: 'horizontal' (single row), 'vertical' (stacked), 'responsive' (auto based on screen size, default) */
|
|
3261
|
-
layout?:
|
|
3574
|
+
layout?: "horizontal" | "vertical" | "responsive";
|
|
3262
3575
|
/** Max actions to show on mobile before overflow (default: 2) */
|
|
3263
|
-
mobileOverflowLimit?: number
|
|
3576
|
+
mobileOverflowLimit?: number;
|
|
3264
3577
|
}
|
|
3265
3578
|
|
|
3266
3579
|
const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
3267
|
-
(
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3580
|
+
(
|
|
3581
|
+
{
|
|
3582
|
+
className,
|
|
3583
|
+
title,
|
|
3584
|
+
description,
|
|
3585
|
+
icon,
|
|
3586
|
+
showBackButton = false,
|
|
3587
|
+
onBackClick,
|
|
3588
|
+
infoIcon,
|
|
3589
|
+
actions,
|
|
3590
|
+
showBorder = true,
|
|
3591
|
+
layout = "responsive",
|
|
3592
|
+
mobileOverflowLimit = 2,
|
|
3593
|
+
...props
|
|
3594
|
+
},
|
|
3595
|
+
ref
|
|
3596
|
+
) => {
|
|
3281
3597
|
// State for overflow expansion (moved to top level)
|
|
3282
|
-
const [isOverflowExpanded, setIsOverflowExpanded] = React.useState(false)
|
|
3598
|
+
const [isOverflowExpanded, setIsOverflowExpanded] = React.useState(false);
|
|
3283
3599
|
|
|
3284
3600
|
// Determine what to show on the left: back button, icon, or nothing
|
|
3285
3601
|
const renderLeftElement = () => {
|
|
@@ -3293,66 +3609,68 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3293
3609
|
>
|
|
3294
3610
|
<ArrowLeft className="w-5 h-5" />
|
|
3295
3611
|
</button>
|
|
3296
|
-
)
|
|
3612
|
+
);
|
|
3297
3613
|
}
|
|
3298
3614
|
if (icon) {
|
|
3299
3615
|
return (
|
|
3300
3616
|
<div className="flex items-center justify-center w-10 h-10 [&_svg]:w-6 [&_svg]:h-6 text-[#717680]">
|
|
3301
3617
|
{icon}
|
|
3302
3618
|
</div>
|
|
3303
|
-
)
|
|
3619
|
+
);
|
|
3304
3620
|
}
|
|
3305
|
-
return null
|
|
3306
|
-
}
|
|
3621
|
+
return null;
|
|
3622
|
+
};
|
|
3307
3623
|
|
|
3308
|
-
const leftElement = renderLeftElement()
|
|
3624
|
+
const leftElement = renderLeftElement();
|
|
3309
3625
|
|
|
3310
3626
|
// Flatten children recursively to handle fragments
|
|
3311
3627
|
const flattenChildren = (children: React.ReactNode): React.ReactNode[] => {
|
|
3312
|
-
const result: React.ReactNode[] = []
|
|
3628
|
+
const result: React.ReactNode[] = [];
|
|
3313
3629
|
React.Children.forEach(children, (child) => {
|
|
3314
3630
|
if (React.isValidElement(child) && child.type === React.Fragment) {
|
|
3315
|
-
result.push(...flattenChildren(child.props.children))
|
|
3631
|
+
result.push(...flattenChildren(child.props.children));
|
|
3316
3632
|
} else if (child !== null && child !== undefined) {
|
|
3317
|
-
result.push(child)
|
|
3633
|
+
result.push(child);
|
|
3318
3634
|
}
|
|
3319
|
-
})
|
|
3320
|
-
return result
|
|
3321
|
-
}
|
|
3635
|
+
});
|
|
3636
|
+
return result;
|
|
3637
|
+
};
|
|
3322
3638
|
|
|
3323
3639
|
// Convert actions to array for overflow handling
|
|
3324
|
-
const actionsArray = flattenChildren(actions)
|
|
3325
|
-
const hasOverflow = actionsArray.length > mobileOverflowLimit
|
|
3326
|
-
const visibleActions = hasOverflow
|
|
3327
|
-
|
|
3640
|
+
const actionsArray = flattenChildren(actions);
|
|
3641
|
+
const hasOverflow = actionsArray.length > mobileOverflowLimit;
|
|
3642
|
+
const visibleActions = hasOverflow
|
|
3643
|
+
? actionsArray.slice(0, mobileOverflowLimit)
|
|
3644
|
+
: actionsArray;
|
|
3645
|
+
const overflowActions = hasOverflow
|
|
3646
|
+
? actionsArray.slice(mobileOverflowLimit)
|
|
3647
|
+
: [];
|
|
3328
3648
|
|
|
3329
3649
|
// Layout classes based on prop
|
|
3330
3650
|
const layoutClasses = {
|
|
3331
|
-
horizontal:
|
|
3332
|
-
vertical:
|
|
3333
|
-
responsive:
|
|
3334
|
-
}
|
|
3651
|
+
horizontal: "flex-row items-center",
|
|
3652
|
+
vertical: "flex-col",
|
|
3653
|
+
responsive: "flex-col sm:flex-row sm:items-center",
|
|
3654
|
+
};
|
|
3335
3655
|
|
|
3336
3656
|
const heightClasses = {
|
|
3337
|
-
horizontal:
|
|
3338
|
-
vertical:
|
|
3339
|
-
responsive:
|
|
3340
|
-
}
|
|
3657
|
+
horizontal: "h-[76px]",
|
|
3658
|
+
vertical: "min-h-[76px] py-4",
|
|
3659
|
+
responsive: "min-h-[76px] py-4 sm:py-0 sm:h-[76px]",
|
|
3660
|
+
};
|
|
3341
3661
|
|
|
3342
3662
|
// Render actions for desktop (all inline)
|
|
3343
3663
|
const renderDesktopActions = () => (
|
|
3344
|
-
<div className="hidden sm:flex items-center gap-2">
|
|
3345
|
-
|
|
3346
|
-
</div>
|
|
3347
|
-
)
|
|
3664
|
+
<div className="hidden sm:flex items-center gap-2">{actionsArray}</div>
|
|
3665
|
+
);
|
|
3348
3666
|
|
|
3349
3667
|
// Render expandable actions (for mobile and vertical layout)
|
|
3350
3668
|
const renderExpandableActions = (additionalClasses?: string) => {
|
|
3351
3669
|
// Calculate grid columns: equal width for visible actions, smaller for overflow button
|
|
3352
|
-
const hasOverflowBtn = overflowActions.length > 0
|
|
3670
|
+
const hasOverflowBtn = overflowActions.length > 0;
|
|
3353
3671
|
const gridCols = hasOverflowBtn
|
|
3354
3672
|
? \`repeat(\${visibleActions.length}, 1fr) auto\`
|
|
3355
|
-
: \`repeat(\${visibleActions.length}, 1fr)
|
|
3673
|
+
: \`repeat(\${visibleActions.length}, 1fr)\`;
|
|
3356
3674
|
|
|
3357
3675
|
return (
|
|
3358
3676
|
<div className={cn("flex flex-col gap-2 w-full", additionalClasses)}>
|
|
@@ -3372,7 +3690,11 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3372
3690
|
aria-label={isOverflowExpanded ? "Show less" : "More actions"}
|
|
3373
3691
|
aria-expanded={isOverflowExpanded}
|
|
3374
3692
|
>
|
|
3375
|
-
{isOverflowExpanded ?
|
|
3693
|
+
{isOverflowExpanded ? (
|
|
3694
|
+
<X className="w-4 h-4" />
|
|
3695
|
+
) : (
|
|
3696
|
+
<MoreHorizontal className="w-4 h-4" />
|
|
3697
|
+
)}
|
|
3376
3698
|
</Button>
|
|
3377
3699
|
)}
|
|
3378
3700
|
</div>
|
|
@@ -3388,25 +3710,23 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3388
3710
|
</div>
|
|
3389
3711
|
)}
|
|
3390
3712
|
</div>
|
|
3391
|
-
)
|
|
3392
|
-
}
|
|
3713
|
+
);
|
|
3714
|
+
};
|
|
3393
3715
|
|
|
3394
3716
|
// For horizontal layout, always show all actions inline
|
|
3395
3717
|
const renderHorizontalActions = () => (
|
|
3396
|
-
<div className="flex items-center gap-2 ml-4">
|
|
3397
|
-
|
|
3398
|
-
</div>
|
|
3399
|
-
)
|
|
3718
|
+
<div className="flex items-center gap-2 ml-4">{actionsArray}</div>
|
|
3719
|
+
);
|
|
3400
3720
|
|
|
3401
3721
|
const renderActions = () => {
|
|
3402
|
-
if (!actions) return null
|
|
3722
|
+
if (!actions) return null;
|
|
3403
3723
|
|
|
3404
|
-
if (layout ===
|
|
3405
|
-
return renderHorizontalActions()
|
|
3724
|
+
if (layout === "horizontal") {
|
|
3725
|
+
return renderHorizontalActions();
|
|
3406
3726
|
}
|
|
3407
3727
|
|
|
3408
|
-
if (layout ===
|
|
3409
|
-
return renderExpandableActions("mt-3")
|
|
3728
|
+
if (layout === "vertical") {
|
|
3729
|
+
return renderExpandableActions("mt-3");
|
|
3410
3730
|
}
|
|
3411
3731
|
|
|
3412
3732
|
// Responsive: render both, CSS handles visibility
|
|
@@ -3417,8 +3737,8 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3417
3737
|
{renderExpandableActions()}
|
|
3418
3738
|
</div>
|
|
3419
3739
|
</>
|
|
3420
|
-
)
|
|
3421
|
-
}
|
|
3740
|
+
);
|
|
3741
|
+
};
|
|
3422
3742
|
|
|
3423
3743
|
return (
|
|
3424
3744
|
<div
|
|
@@ -3436,9 +3756,7 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3436
3756
|
<div className="flex items-center flex-1 min-w-0">
|
|
3437
3757
|
{/* Left Section: Icon or Back Button */}
|
|
3438
3758
|
{leftElement && (
|
|
3439
|
-
<div className="flex-shrink-0 mr-4">
|
|
3440
|
-
{leftElement}
|
|
3441
|
-
</div>
|
|
3759
|
+
<div className="flex-shrink-0 mr-4">{leftElement}</div>
|
|
3442
3760
|
)}
|
|
3443
3761
|
|
|
3444
3762
|
{/* Content Section: Title + Description */}
|
|
@@ -3464,12 +3782,12 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3464
3782
|
{/* Actions Section */}
|
|
3465
3783
|
{renderActions()}
|
|
3466
3784
|
</div>
|
|
3467
|
-
)
|
|
3785
|
+
);
|
|
3468
3786
|
}
|
|
3469
|
-
)
|
|
3470
|
-
PageHeader.displayName = "PageHeader"
|
|
3787
|
+
);
|
|
3788
|
+
PageHeader.displayName = "PageHeader";
|
|
3471
3789
|
|
|
3472
|
-
export { PageHeader, pageHeaderVariants }
|
|
3790
|
+
export { PageHeader, pageHeaderVariants };
|
|
3473
3791
|
`, prefix)
|
|
3474
3792
|
}
|
|
3475
3793
|
]
|