myoperator-ui 0.0.93 → 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 +884 -750
- 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,12 @@ 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";
|
|
491
506
|
|
|
492
|
-
export { Badge, badgeVariants }
|
|
507
|
+
export { Badge, badgeVariants };
|
|
493
508
|
`, prefix)
|
|
494
509
|
}
|
|
495
510
|
]
|
|
@@ -504,19 +519,27 @@ export { Badge, badgeVariants }
|
|
|
504
519
|
files: [
|
|
505
520
|
{
|
|
506
521
|
name: "typography.tsx",
|
|
507
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
508
|
-
import { cn } from "../../lib/utils"
|
|
522
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
523
|
+
import { cn } from "../../lib/utils";
|
|
509
524
|
|
|
510
525
|
// =============================================================================
|
|
511
526
|
// TYPES
|
|
512
527
|
// =============================================================================
|
|
513
528
|
|
|
514
|
-
export type Kind = "display" | "headline" | "title" | "label" | "body"
|
|
515
|
-
export type Variant = "large" | "medium" | "small"
|
|
516
|
-
export type Color =
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
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}\`;
|
|
520
543
|
|
|
521
544
|
// =============================================================================
|
|
522
545
|
// MAPPINGS
|
|
@@ -541,7 +564,7 @@ const mapTagName: { [key in Key]: keyof JSX.IntrinsicElements } = {
|
|
|
541
564
|
"body-large": "span",
|
|
542
565
|
"body-medium": "span",
|
|
543
566
|
"body-small": "span",
|
|
544
|
-
}
|
|
567
|
+
};
|
|
545
568
|
|
|
546
569
|
/**
|
|
547
570
|
* Maps kind-variant combinations to Tailwind typography classes
|
|
@@ -562,7 +585,7 @@ const mapClassName: { [key in Key]: string } = {
|
|
|
562
585
|
"body-large": "text-base leading-5 font-normal",
|
|
563
586
|
"body-medium": "text-sm leading-[18px] font-normal",
|
|
564
587
|
"body-small": "text-xs leading-4 font-normal",
|
|
565
|
-
}
|
|
588
|
+
};
|
|
566
589
|
|
|
567
590
|
/**
|
|
568
591
|
* Maps color variants to Tailwind text color classes
|
|
@@ -576,7 +599,7 @@ const mapColorClassName: { [key in Color]: string } = {
|
|
|
576
599
|
inverted: "text-white",
|
|
577
600
|
error: "text-[#F04438]",
|
|
578
601
|
success: "text-[#17B26A]",
|
|
579
|
-
}
|
|
602
|
+
};
|
|
580
603
|
|
|
581
604
|
/**
|
|
582
605
|
* Maps alignment to Tailwind text alignment classes
|
|
@@ -585,7 +608,7 @@ const mapAlignClassName: { [key in Align]: string } = {
|
|
|
585
608
|
left: "text-left",
|
|
586
609
|
center: "text-center",
|
|
587
610
|
right: "text-right",
|
|
588
|
-
}
|
|
611
|
+
};
|
|
589
612
|
|
|
590
613
|
// =============================================================================
|
|
591
614
|
// COMPONENT
|
|
@@ -596,21 +619,21 @@ const mapAlignClassName: { [key in Align]: string } = {
|
|
|
596
619
|
*/
|
|
597
620
|
export interface TypographyProps extends React.HTMLAttributes<HTMLElement> {
|
|
598
621
|
/** Text content */
|
|
599
|
-
children: React.ReactNode
|
|
622
|
+
children: React.ReactNode;
|
|
600
623
|
/** Typography kind - determines base styling and semantic tag */
|
|
601
|
-
kind?: Kind
|
|
624
|
+
kind?: Kind;
|
|
602
625
|
/** Size variant */
|
|
603
|
-
variant?: Variant
|
|
626
|
+
variant?: Variant;
|
|
604
627
|
/** Text color */
|
|
605
|
-
color?: Color
|
|
628
|
+
color?: Color;
|
|
606
629
|
/** Text alignment */
|
|
607
|
-
align?: Align
|
|
630
|
+
align?: Align;
|
|
608
631
|
/** Enable text truncation with ellipsis */
|
|
609
|
-
truncate?: boolean
|
|
632
|
+
truncate?: boolean;
|
|
610
633
|
/** Override the default HTML tag */
|
|
611
|
-
tag?: keyof JSX.IntrinsicElements
|
|
634
|
+
tag?: keyof JSX.IntrinsicElements;
|
|
612
635
|
/** For label elements - associates with form input */
|
|
613
|
-
htmlFor?: string
|
|
636
|
+
htmlFor?: string;
|
|
614
637
|
}
|
|
615
638
|
|
|
616
639
|
/**
|
|
@@ -647,8 +670,8 @@ const Typography = React.forwardRef<HTMLElement, TypographyProps>(
|
|
|
647
670
|
},
|
|
648
671
|
ref
|
|
649
672
|
) => {
|
|
650
|
-
const key: Key = \`\${kind}-\${variant}
|
|
651
|
-
const Tag = tag || mapTagName[key]
|
|
673
|
+
const key: Key = \`\${kind}-\${variant}\`;
|
|
674
|
+
const Tag = tag || mapTagName[key];
|
|
652
675
|
|
|
653
676
|
const classes = cn(
|
|
654
677
|
"m-0", // Reset margin
|
|
@@ -657,7 +680,7 @@ const Typography = React.forwardRef<HTMLElement, TypographyProps>(
|
|
|
657
680
|
align && mapAlignClassName[align],
|
|
658
681
|
truncate && "truncate",
|
|
659
682
|
className
|
|
660
|
-
)
|
|
683
|
+
);
|
|
661
684
|
|
|
662
685
|
return (
|
|
663
686
|
<Tag
|
|
@@ -668,12 +691,18 @@ const Typography = React.forwardRef<HTMLElement, TypographyProps>(
|
|
|
668
691
|
>
|
|
669
692
|
{children}
|
|
670
693
|
</Tag>
|
|
671
|
-
)
|
|
694
|
+
);
|
|
672
695
|
}
|
|
673
|
-
)
|
|
674
|
-
Typography.displayName = "Typography"
|
|
696
|
+
);
|
|
697
|
+
Typography.displayName = "Typography";
|
|
675
698
|
|
|
676
|
-
export {
|
|
699
|
+
export {
|
|
700
|
+
Typography,
|
|
701
|
+
mapTagName,
|
|
702
|
+
mapClassName,
|
|
703
|
+
mapColorClassName,
|
|
704
|
+
mapAlignClassName,
|
|
705
|
+
};
|
|
677
706
|
`, prefix)
|
|
678
707
|
}
|
|
679
708
|
]
|
|
@@ -689,10 +718,10 @@ export { Typography, mapTagName, mapClassName, mapColorClassName, mapAlignClassN
|
|
|
689
718
|
files: [
|
|
690
719
|
{
|
|
691
720
|
name: "input.tsx",
|
|
692
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
693
|
-
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";
|
|
694
723
|
|
|
695
|
-
import { cn } from "../../lib/utils"
|
|
724
|
+
import { cn } from "../../lib/utils";
|
|
696
725
|
|
|
697
726
|
/**
|
|
698
727
|
* Input variants for different visual states
|
|
@@ -702,15 +731,17 @@ const inputVariants = cva(
|
|
|
702
731
|
{
|
|
703
732
|
variants: {
|
|
704
733
|
state: {
|
|
705
|
-
default:
|
|
706
|
-
|
|
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)]",
|
|
707
738
|
},
|
|
708
739
|
},
|
|
709
740
|
defaultVariants: {
|
|
710
741
|
state: "default",
|
|
711
742
|
},
|
|
712
743
|
}
|
|
713
|
-
)
|
|
744
|
+
);
|
|
714
745
|
|
|
715
746
|
/**
|
|
716
747
|
* A flexible input component for text entry with state variants.
|
|
@@ -723,7 +754,8 @@ const inputVariants = cva(
|
|
|
723
754
|
* \`\`\`
|
|
724
755
|
*/
|
|
725
756
|
export interface InputProps
|
|
726
|
-
extends
|
|
757
|
+
extends
|
|
758
|
+
Omit<React.ComponentProps<"input">, "size">,
|
|
727
759
|
VariantProps<typeof inputVariants> {}
|
|
728
760
|
|
|
729
761
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
@@ -735,12 +767,12 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
735
767
|
ref={ref}
|
|
736
768
|
{...props}
|
|
737
769
|
/>
|
|
738
|
-
)
|
|
770
|
+
);
|
|
739
771
|
}
|
|
740
|
-
)
|
|
741
|
-
Input.displayName = "Input"
|
|
772
|
+
);
|
|
773
|
+
Input.displayName = "Input";
|
|
742
774
|
|
|
743
|
-
export { Input, inputVariants }
|
|
775
|
+
export { Input, inputVariants };
|
|
744
776
|
`, prefix)
|
|
745
777
|
}
|
|
746
778
|
]
|
|
@@ -758,12 +790,12 @@ export { Input, inputVariants }
|
|
|
758
790
|
files: [
|
|
759
791
|
{
|
|
760
792
|
name: "select.tsx",
|
|
761
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
762
|
-
import * as SelectPrimitive from "@radix-ui/react-select"
|
|
763
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
764
|
-
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";
|
|
765
797
|
|
|
766
|
-
import { cn } from "../../lib/utils"
|
|
798
|
+
import { cn } from "../../lib/utils";
|
|
767
799
|
|
|
768
800
|
/**
|
|
769
801
|
* SelectTrigger variants matching TextField styling
|
|
@@ -773,24 +805,27 @@ const selectTriggerVariants = cva(
|
|
|
773
805
|
{
|
|
774
806
|
variants: {
|
|
775
807
|
state: {
|
|
776
|
-
default:
|
|
777
|
-
|
|
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)]",
|
|
778
812
|
},
|
|
779
813
|
},
|
|
780
814
|
defaultVariants: {
|
|
781
815
|
state: "default",
|
|
782
816
|
},
|
|
783
817
|
}
|
|
784
|
-
)
|
|
818
|
+
);
|
|
785
819
|
|
|
786
|
-
const Select = SelectPrimitive.Root
|
|
820
|
+
const Select = SelectPrimitive.Root;
|
|
787
821
|
|
|
788
|
-
const SelectGroup = SelectPrimitive.Group
|
|
822
|
+
const SelectGroup = SelectPrimitive.Group;
|
|
789
823
|
|
|
790
|
-
const SelectValue = SelectPrimitive.Value
|
|
824
|
+
const SelectValue = SelectPrimitive.Value;
|
|
791
825
|
|
|
792
826
|
export interface SelectTriggerProps
|
|
793
|
-
extends
|
|
827
|
+
extends
|
|
828
|
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>,
|
|
794
829
|
VariantProps<typeof selectTriggerVariants> {}
|
|
795
830
|
|
|
796
831
|
const SelectTrigger = React.forwardRef<
|
|
@@ -807,8 +842,8 @@ const SelectTrigger = React.forwardRef<
|
|
|
807
842
|
<ChevronDown className="size-4 text-[#717680] opacity-70" />
|
|
808
843
|
</SelectPrimitive.Icon>
|
|
809
844
|
</SelectPrimitive.Trigger>
|
|
810
|
-
))
|
|
811
|
-
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
|
845
|
+
));
|
|
846
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
812
847
|
|
|
813
848
|
const SelectScrollUpButton = React.forwardRef<
|
|
814
849
|
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
|
@@ -824,8 +859,8 @@ const SelectScrollUpButton = React.forwardRef<
|
|
|
824
859
|
>
|
|
825
860
|
<ChevronUp className="size-4 text-[#717680]" />
|
|
826
861
|
</SelectPrimitive.ScrollUpButton>
|
|
827
|
-
))
|
|
828
|
-
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
|
862
|
+
));
|
|
863
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
829
864
|
|
|
830
865
|
const SelectScrollDownButton = React.forwardRef<
|
|
831
866
|
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
|
@@ -841,8 +876,9 @@ const SelectScrollDownButton = React.forwardRef<
|
|
|
841
876
|
>
|
|
842
877
|
<ChevronDown className="size-4 text-[#717680]" />
|
|
843
878
|
</SelectPrimitive.ScrollDownButton>
|
|
844
|
-
))
|
|
845
|
-
SelectScrollDownButton.displayName =
|
|
879
|
+
));
|
|
880
|
+
SelectScrollDownButton.displayName =
|
|
881
|
+
SelectPrimitive.ScrollDownButton.displayName;
|
|
846
882
|
|
|
847
883
|
const SelectContent = React.forwardRef<
|
|
848
884
|
React.ElementRef<typeof SelectPrimitive.Content>,
|
|
@@ -878,8 +914,8 @@ const SelectContent = React.forwardRef<
|
|
|
878
914
|
<SelectScrollDownButton />
|
|
879
915
|
</SelectPrimitive.Content>
|
|
880
916
|
</SelectPrimitive.Portal>
|
|
881
|
-
))
|
|
882
|
-
SelectContent.displayName = SelectPrimitive.Content.displayName
|
|
917
|
+
));
|
|
918
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
883
919
|
|
|
884
920
|
const SelectLabel = React.forwardRef<
|
|
885
921
|
React.ElementRef<typeof SelectPrimitive.Label>,
|
|
@@ -890,8 +926,8 @@ const SelectLabel = React.forwardRef<
|
|
|
890
926
|
className={cn("px-4 py-1.5 text-xs font-medium text-[#717680]", className)}
|
|
891
927
|
{...props}
|
|
892
928
|
/>
|
|
893
|
-
))
|
|
894
|
-
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
|
929
|
+
));
|
|
930
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
895
931
|
|
|
896
932
|
const SelectItem = React.forwardRef<
|
|
897
933
|
React.ElementRef<typeof SelectPrimitive.Item>,
|
|
@@ -914,8 +950,8 @@ const SelectItem = React.forwardRef<
|
|
|
914
950
|
</span>
|
|
915
951
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
|
916
952
|
</SelectPrimitive.Item>
|
|
917
|
-
))
|
|
918
|
-
SelectItem.displayName = SelectPrimitive.Item.displayName
|
|
953
|
+
));
|
|
954
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
919
955
|
|
|
920
956
|
const SelectSeparator = React.forwardRef<
|
|
921
957
|
React.ElementRef<typeof SelectPrimitive.Separator>,
|
|
@@ -926,8 +962,8 @@ const SelectSeparator = React.forwardRef<
|
|
|
926
962
|
className={cn("-mx-1 my-1 h-px bg-[#E9EAEB]", className)}
|
|
927
963
|
{...props}
|
|
928
964
|
/>
|
|
929
|
-
))
|
|
930
|
-
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
|
965
|
+
));
|
|
966
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
931
967
|
|
|
932
968
|
export {
|
|
933
969
|
Select,
|
|
@@ -941,7 +977,7 @@ export {
|
|
|
941
977
|
SelectScrollUpButton,
|
|
942
978
|
SelectScrollDownButton,
|
|
943
979
|
selectTriggerVariants,
|
|
944
|
-
}
|
|
980
|
+
};
|
|
945
981
|
`, prefix)
|
|
946
982
|
}
|
|
947
983
|
]
|
|
@@ -959,12 +995,12 @@ export {
|
|
|
959
995
|
files: [
|
|
960
996
|
{
|
|
961
997
|
name: "checkbox.tsx",
|
|
962
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
963
|
-
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
|
964
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
965
|
-
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";
|
|
966
1002
|
|
|
967
|
-
import { cn } from "../../lib/utils"
|
|
1003
|
+
import { cn } from "../../lib/utils";
|
|
968
1004
|
|
|
969
1005
|
/**
|
|
970
1006
|
* Checkbox box variants (the outer container)
|
|
@@ -983,7 +1019,7 @@ const checkboxVariants = cva(
|
|
|
983
1019
|
size: "default",
|
|
984
1020
|
},
|
|
985
1021
|
}
|
|
986
|
-
)
|
|
1022
|
+
);
|
|
987
1023
|
|
|
988
1024
|
/**
|
|
989
1025
|
* Icon size variants based on checkbox size
|
|
@@ -999,7 +1035,7 @@ const iconSizeVariants = cva("", {
|
|
|
999
1035
|
defaultVariants: {
|
|
1000
1036
|
size: "default",
|
|
1001
1037
|
},
|
|
1002
|
-
})
|
|
1038
|
+
});
|
|
1003
1039
|
|
|
1004
1040
|
/**
|
|
1005
1041
|
* Label text size variants
|
|
@@ -1015,9 +1051,9 @@ const labelSizeVariants = cva("", {
|
|
|
1015
1051
|
defaultVariants: {
|
|
1016
1052
|
size: "default",
|
|
1017
1053
|
},
|
|
1018
|
-
})
|
|
1054
|
+
});
|
|
1019
1055
|
|
|
1020
|
-
export type CheckedState = boolean | "indeterminate"
|
|
1056
|
+
export type CheckedState = boolean | "indeterminate";
|
|
1021
1057
|
|
|
1022
1058
|
/**
|
|
1023
1059
|
* A tri-state checkbox component with label support. Built on Radix UI Checkbox primitive.
|
|
@@ -1032,18 +1068,22 @@ export type CheckedState = boolean | "indeterminate"
|
|
|
1032
1068
|
* \`\`\`
|
|
1033
1069
|
*/
|
|
1034
1070
|
export interface CheckboxProps
|
|
1035
|
-
extends
|
|
1071
|
+
extends
|
|
1072
|
+
Omit<
|
|
1073
|
+
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>,
|
|
1074
|
+
"onChange"
|
|
1075
|
+
>,
|
|
1036
1076
|
VariantProps<typeof checkboxVariants> {
|
|
1037
1077
|
/** Optional label text */
|
|
1038
|
-
label?: string
|
|
1078
|
+
label?: string;
|
|
1039
1079
|
/** Position of the label */
|
|
1040
|
-
labelPosition?: "left" | "right"
|
|
1080
|
+
labelPosition?: "left" | "right";
|
|
1041
1081
|
/** Class name applied to the checkbox element */
|
|
1042
|
-
checkboxClassName?: string
|
|
1082
|
+
checkboxClassName?: string;
|
|
1043
1083
|
/** Class name applied to the label element */
|
|
1044
|
-
labelClassName?: string
|
|
1084
|
+
labelClassName?: string;
|
|
1045
1085
|
/** If true, uses separate labels with htmlFor/id association instead of wrapping the input. Requires id prop. */
|
|
1046
|
-
separateLabel?: boolean
|
|
1086
|
+
separateLabel?: boolean;
|
|
1047
1087
|
}
|
|
1048
1088
|
|
|
1049
1089
|
const Checkbox = React.forwardRef<
|
|
@@ -1086,7 +1126,7 @@ const Checkbox = React.forwardRef<
|
|
|
1086
1126
|
)}
|
|
1087
1127
|
</CheckboxPrimitive.Indicator>
|
|
1088
1128
|
</CheckboxPrimitive.Root>
|
|
1089
|
-
)
|
|
1129
|
+
);
|
|
1090
1130
|
|
|
1091
1131
|
if (label) {
|
|
1092
1132
|
// separateLabel mode: use htmlFor/id association instead of wrapping
|
|
@@ -1121,33 +1161,52 @@ const Checkbox = React.forwardRef<
|
|
|
1121
1161
|
</label>
|
|
1122
1162
|
)}
|
|
1123
1163
|
</div>
|
|
1124
|
-
)
|
|
1164
|
+
);
|
|
1125
1165
|
}
|
|
1126
1166
|
|
|
1127
1167
|
// Default: wrapping label
|
|
1128
1168
|
return (
|
|
1129
|
-
<label
|
|
1169
|
+
<label
|
|
1170
|
+
className={cn(
|
|
1171
|
+
"inline-flex items-center gap-2 cursor-pointer",
|
|
1172
|
+
disabled && "cursor-not-allowed"
|
|
1173
|
+
)}
|
|
1174
|
+
>
|
|
1130
1175
|
{labelPosition === "left" && (
|
|
1131
|
-
<span
|
|
1176
|
+
<span
|
|
1177
|
+
className={cn(
|
|
1178
|
+
labelSizeVariants({ size }),
|
|
1179
|
+
"text-[#181D27]",
|
|
1180
|
+
disabled && "opacity-50",
|
|
1181
|
+
labelClassName
|
|
1182
|
+
)}
|
|
1183
|
+
>
|
|
1132
1184
|
{label}
|
|
1133
1185
|
</span>
|
|
1134
1186
|
)}
|
|
1135
1187
|
{checkbox}
|
|
1136
1188
|
{labelPosition === "right" && (
|
|
1137
|
-
<span
|
|
1189
|
+
<span
|
|
1190
|
+
className={cn(
|
|
1191
|
+
labelSizeVariants({ size }),
|
|
1192
|
+
"text-[#181D27]",
|
|
1193
|
+
disabled && "opacity-50",
|
|
1194
|
+
labelClassName
|
|
1195
|
+
)}
|
|
1196
|
+
>
|
|
1138
1197
|
{label}
|
|
1139
1198
|
</span>
|
|
1140
1199
|
)}
|
|
1141
1200
|
</label>
|
|
1142
|
-
)
|
|
1201
|
+
);
|
|
1143
1202
|
}
|
|
1144
1203
|
|
|
1145
|
-
return checkbox
|
|
1204
|
+
return checkbox;
|
|
1146
1205
|
}
|
|
1147
|
-
)
|
|
1148
|
-
Checkbox.displayName = "Checkbox"
|
|
1206
|
+
);
|
|
1207
|
+
Checkbox.displayName = "Checkbox";
|
|
1149
1208
|
|
|
1150
|
-
export { Checkbox, checkboxVariants }
|
|
1209
|
+
export { Checkbox, checkboxVariants };
|
|
1151
1210
|
`, prefix)
|
|
1152
1211
|
}
|
|
1153
1212
|
]
|
|
@@ -1164,11 +1223,11 @@ export { Checkbox, checkboxVariants }
|
|
|
1164
1223
|
files: [
|
|
1165
1224
|
{
|
|
1166
1225
|
name: "switch.tsx",
|
|
1167
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
1168
|
-
import * as SwitchPrimitives from "@radix-ui/react-switch"
|
|
1169
|
-
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";
|
|
1170
1229
|
|
|
1171
|
-
import { cn } from "../../lib/utils"
|
|
1230
|
+
import { cn } from "../../lib/utils";
|
|
1172
1231
|
|
|
1173
1232
|
/**
|
|
1174
1233
|
* Switch track variants (the outer container)
|
|
@@ -1187,7 +1246,7 @@ const switchVariants = cva(
|
|
|
1187
1246
|
size: "default",
|
|
1188
1247
|
},
|
|
1189
1248
|
}
|
|
1190
|
-
)
|
|
1249
|
+
);
|
|
1191
1250
|
|
|
1192
1251
|
/**
|
|
1193
1252
|
* Switch thumb variants (the sliding circle)
|
|
@@ -1206,7 +1265,7 @@ const switchThumbVariants = cva(
|
|
|
1206
1265
|
size: "default",
|
|
1207
1266
|
},
|
|
1208
1267
|
}
|
|
1209
|
-
)
|
|
1268
|
+
);
|
|
1210
1269
|
|
|
1211
1270
|
/**
|
|
1212
1271
|
* Label text size variants
|
|
@@ -1222,7 +1281,7 @@ const labelSizeVariants = cva("", {
|
|
|
1222
1281
|
defaultVariants: {
|
|
1223
1282
|
size: "default",
|
|
1224
1283
|
},
|
|
1225
|
-
})
|
|
1284
|
+
});
|
|
1226
1285
|
|
|
1227
1286
|
/**
|
|
1228
1287
|
* A switch/toggle component for boolean inputs with on/off states
|
|
@@ -1235,12 +1294,16 @@ const labelSizeVariants = cva("", {
|
|
|
1235
1294
|
* \`\`\`
|
|
1236
1295
|
*/
|
|
1237
1296
|
export interface SwitchProps
|
|
1238
|
-
extends
|
|
1297
|
+
extends
|
|
1298
|
+
Omit<
|
|
1299
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>,
|
|
1300
|
+
"onChange"
|
|
1301
|
+
>,
|
|
1239
1302
|
VariantProps<typeof switchVariants> {
|
|
1240
1303
|
/** Optional label text */
|
|
1241
|
-
label?: string
|
|
1304
|
+
label?: string;
|
|
1242
1305
|
/** Position of the label */
|
|
1243
|
-
labelPosition?: "left" | "right"
|
|
1306
|
+
labelPosition?: "left" | "right";
|
|
1244
1307
|
}
|
|
1245
1308
|
|
|
1246
1309
|
const Switch = React.forwardRef<
|
|
@@ -1248,14 +1311,7 @@ const Switch = React.forwardRef<
|
|
|
1248
1311
|
SwitchProps
|
|
1249
1312
|
>(
|
|
1250
1313
|
(
|
|
1251
|
-
{
|
|
1252
|
-
className,
|
|
1253
|
-
size,
|
|
1254
|
-
label,
|
|
1255
|
-
labelPosition = "right",
|
|
1256
|
-
disabled,
|
|
1257
|
-
...props
|
|
1258
|
-
},
|
|
1314
|
+
{ className, size, label, labelPosition = "right", disabled, ...props },
|
|
1259
1315
|
ref
|
|
1260
1316
|
) => {
|
|
1261
1317
|
const switchElement = (
|
|
@@ -1267,43 +1323,49 @@ const Switch = React.forwardRef<
|
|
|
1267
1323
|
>
|
|
1268
1324
|
<SwitchPrimitives.Thumb className={cn(switchThumbVariants({ size }))} />
|
|
1269
1325
|
</SwitchPrimitives.Root>
|
|
1270
|
-
)
|
|
1326
|
+
);
|
|
1271
1327
|
|
|
1272
1328
|
if (label) {
|
|
1273
1329
|
return (
|
|
1274
|
-
<label
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1330
|
+
<label
|
|
1331
|
+
className={cn(
|
|
1332
|
+
"inline-flex items-center gap-2 cursor-pointer",
|
|
1333
|
+
disabled && "cursor-not-allowed"
|
|
1334
|
+
)}
|
|
1335
|
+
>
|
|
1278
1336
|
{labelPosition === "left" && (
|
|
1279
|
-
<span
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1337
|
+
<span
|
|
1338
|
+
className={cn(
|
|
1339
|
+
labelSizeVariants({ size }),
|
|
1340
|
+
"text-[#181D27]",
|
|
1341
|
+
disabled && "opacity-50"
|
|
1342
|
+
)}
|
|
1343
|
+
>
|
|
1284
1344
|
{label}
|
|
1285
1345
|
</span>
|
|
1286
1346
|
)}
|
|
1287
1347
|
{switchElement}
|
|
1288
1348
|
{labelPosition === "right" && (
|
|
1289
|
-
<span
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1349
|
+
<span
|
|
1350
|
+
className={cn(
|
|
1351
|
+
labelSizeVariants({ size }),
|
|
1352
|
+
"text-[#181D27]",
|
|
1353
|
+
disabled && "opacity-50"
|
|
1354
|
+
)}
|
|
1355
|
+
>
|
|
1294
1356
|
{label}
|
|
1295
1357
|
</span>
|
|
1296
1358
|
)}
|
|
1297
1359
|
</label>
|
|
1298
|
-
)
|
|
1360
|
+
);
|
|
1299
1361
|
}
|
|
1300
1362
|
|
|
1301
|
-
return switchElement
|
|
1363
|
+
return switchElement;
|
|
1302
1364
|
}
|
|
1303
|
-
)
|
|
1304
|
-
Switch.displayName = "Switch"
|
|
1365
|
+
);
|
|
1366
|
+
Switch.displayName = "Switch";
|
|
1305
1367
|
|
|
1306
|
-
export { Switch, switchVariants }
|
|
1368
|
+
export { Switch, switchVariants };
|
|
1307
1369
|
`, prefix)
|
|
1308
1370
|
}
|
|
1309
1371
|
]
|
|
@@ -1320,11 +1382,11 @@ export { Switch, switchVariants }
|
|
|
1320
1382
|
files: [
|
|
1321
1383
|
{
|
|
1322
1384
|
name: "text-field.tsx",
|
|
1323
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
1324
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1325
|
-
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";
|
|
1326
1388
|
|
|
1327
|
-
import { cn } from "../../lib/utils"
|
|
1389
|
+
import { cn } from "../../lib/utils";
|
|
1328
1390
|
|
|
1329
1391
|
/**
|
|
1330
1392
|
* TextField container variants for when icons/prefix/suffix are present
|
|
@@ -1334,8 +1396,10 @@ const textFieldContainerVariants = cva(
|
|
|
1334
1396
|
{
|
|
1335
1397
|
variants: {
|
|
1336
1398
|
state: {
|
|
1337
|
-
default:
|
|
1338
|
-
|
|
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)]",
|
|
1339
1403
|
},
|
|
1340
1404
|
disabled: {
|
|
1341
1405
|
true: "cursor-not-allowed opacity-50 bg-[#FAFAFA]",
|
|
@@ -1347,7 +1411,7 @@ const textFieldContainerVariants = cva(
|
|
|
1347
1411
|
disabled: false,
|
|
1348
1412
|
},
|
|
1349
1413
|
}
|
|
1350
|
-
)
|
|
1414
|
+
);
|
|
1351
1415
|
|
|
1352
1416
|
/**
|
|
1353
1417
|
* TextField input variants (standalone without container)
|
|
@@ -1357,15 +1421,17 @@ const textFieldInputVariants = cva(
|
|
|
1357
1421
|
{
|
|
1358
1422
|
variants: {
|
|
1359
1423
|
state: {
|
|
1360
|
-
default:
|
|
1361
|
-
|
|
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)]",
|
|
1362
1428
|
},
|
|
1363
1429
|
},
|
|
1364
1430
|
defaultVariants: {
|
|
1365
1431
|
state: "default",
|
|
1366
1432
|
},
|
|
1367
1433
|
}
|
|
1368
|
-
)
|
|
1434
|
+
);
|
|
1369
1435
|
|
|
1370
1436
|
/**
|
|
1371
1437
|
* A comprehensive text field component with label, icons, validation states, and more.
|
|
@@ -1378,34 +1444,35 @@ const textFieldInputVariants = cva(
|
|
|
1378
1444
|
* \`\`\`
|
|
1379
1445
|
*/
|
|
1380
1446
|
export interface TextFieldProps
|
|
1381
|
-
extends
|
|
1447
|
+
extends
|
|
1448
|
+
Omit<React.ComponentProps<"input">, "size">,
|
|
1382
1449
|
VariantProps<typeof textFieldInputVariants> {
|
|
1383
1450
|
/** Label text displayed above the input */
|
|
1384
|
-
label?: string
|
|
1451
|
+
label?: string;
|
|
1385
1452
|
/** Shows red asterisk next to label when true */
|
|
1386
|
-
required?: boolean
|
|
1453
|
+
required?: boolean;
|
|
1387
1454
|
/** Helper text displayed below the input */
|
|
1388
|
-
helperText?: string
|
|
1455
|
+
helperText?: string;
|
|
1389
1456
|
/** Error message - shows error state with red styling */
|
|
1390
|
-
error?: string
|
|
1457
|
+
error?: string;
|
|
1391
1458
|
/** Icon displayed on the left inside the input */
|
|
1392
|
-
leftIcon?: React.ReactNode
|
|
1459
|
+
leftIcon?: React.ReactNode;
|
|
1393
1460
|
/** Icon displayed on the right inside the input */
|
|
1394
|
-
rightIcon?: React.ReactNode
|
|
1461
|
+
rightIcon?: React.ReactNode;
|
|
1395
1462
|
/** Text prefix inside input (e.g., "https://") */
|
|
1396
|
-
prefix?: string
|
|
1463
|
+
prefix?: string;
|
|
1397
1464
|
/** Text suffix inside input (e.g., ".com") */
|
|
1398
|
-
suffix?: string
|
|
1465
|
+
suffix?: string;
|
|
1399
1466
|
/** Shows character count when maxLength is set */
|
|
1400
|
-
showCount?: boolean
|
|
1467
|
+
showCount?: boolean;
|
|
1401
1468
|
/** Shows loading spinner inside input */
|
|
1402
|
-
loading?: boolean
|
|
1469
|
+
loading?: boolean;
|
|
1403
1470
|
/** Additional class for the wrapper container */
|
|
1404
|
-
wrapperClassName?: string
|
|
1471
|
+
wrapperClassName?: string;
|
|
1405
1472
|
/** Additional class for the label */
|
|
1406
|
-
labelClassName?: string
|
|
1473
|
+
labelClassName?: string;
|
|
1407
1474
|
/** Additional class for the input container (includes prefix/suffix/icons) */
|
|
1408
|
-
inputContainerClassName?: string
|
|
1475
|
+
inputContainerClassName?: string;
|
|
1409
1476
|
}
|
|
1410
1477
|
|
|
1411
1478
|
const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
@@ -1437,37 +1504,39 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
1437
1504
|
ref
|
|
1438
1505
|
) => {
|
|
1439
1506
|
// Internal state for character count in uncontrolled mode
|
|
1440
|
-
const [internalValue, setInternalValue] = React.useState(
|
|
1507
|
+
const [internalValue, setInternalValue] = React.useState(
|
|
1508
|
+
defaultValue ?? ""
|
|
1509
|
+
);
|
|
1441
1510
|
|
|
1442
1511
|
// Determine if controlled
|
|
1443
|
-
const isControlled = value !== undefined
|
|
1444
|
-
const currentValue = isControlled ? value : internalValue
|
|
1512
|
+
const isControlled = value !== undefined;
|
|
1513
|
+
const currentValue = isControlled ? value : internalValue;
|
|
1445
1514
|
|
|
1446
1515
|
// Derive state from props
|
|
1447
|
-
const derivedState = error ?
|
|
1516
|
+
const derivedState = error ? "error" : (state ?? "default");
|
|
1448
1517
|
|
|
1449
1518
|
// Handle change for both controlled and uncontrolled
|
|
1450
1519
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
1451
1520
|
if (!isControlled) {
|
|
1452
|
-
setInternalValue(e.target.value)
|
|
1521
|
+
setInternalValue(e.target.value);
|
|
1453
1522
|
}
|
|
1454
|
-
onChange?.(e)
|
|
1455
|
-
}
|
|
1523
|
+
onChange?.(e);
|
|
1524
|
+
};
|
|
1456
1525
|
|
|
1457
1526
|
// Determine if we need the container wrapper (for icons/prefix/suffix)
|
|
1458
|
-
const hasAddons = leftIcon || rightIcon || prefix || suffix || loading
|
|
1527
|
+
const hasAddons = leftIcon || rightIcon || prefix || suffix || loading;
|
|
1459
1528
|
|
|
1460
1529
|
// Character count
|
|
1461
|
-
const charCount = String(currentValue).length
|
|
1530
|
+
const charCount = String(currentValue).length;
|
|
1462
1531
|
|
|
1463
1532
|
// Generate unique IDs for accessibility
|
|
1464
|
-
const generatedId = React.useId()
|
|
1465
|
-
const inputId = id || generatedId
|
|
1466
|
-
const helperId = \`\${inputId}-helper
|
|
1467
|
-
const errorId = \`\${inputId}-error
|
|
1533
|
+
const generatedId = React.useId();
|
|
1534
|
+
const inputId = id || generatedId;
|
|
1535
|
+
const helperId = \`\${inputId}-helper\`;
|
|
1536
|
+
const errorId = \`\${inputId}-error\`;
|
|
1468
1537
|
|
|
1469
1538
|
// Determine aria-describedby
|
|
1470
|
-
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined
|
|
1539
|
+
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined;
|
|
1471
1540
|
|
|
1472
1541
|
// Render the input element
|
|
1473
1542
|
const inputElement = (
|
|
@@ -1488,7 +1557,7 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
1488
1557
|
aria-describedby={ariaDescribedBy}
|
|
1489
1558
|
{...props}
|
|
1490
1559
|
/>
|
|
1491
|
-
)
|
|
1560
|
+
);
|
|
1492
1561
|
|
|
1493
1562
|
return (
|
|
1494
1563
|
<div className={cn("flex flex-col gap-1", wrapperClassName)}>
|
|
@@ -1507,17 +1576,38 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
1507
1576
|
{hasAddons ? (
|
|
1508
1577
|
<div
|
|
1509
1578
|
className={cn(
|
|
1510
|
-
textFieldContainerVariants({
|
|
1579
|
+
textFieldContainerVariants({
|
|
1580
|
+
state: derivedState,
|
|
1581
|
+
disabled: disabled || loading,
|
|
1582
|
+
}),
|
|
1511
1583
|
"h-10 px-4",
|
|
1512
1584
|
inputContainerClassName
|
|
1513
1585
|
)}
|
|
1514
1586
|
>
|
|
1515
|
-
{prefix &&
|
|
1516
|
-
|
|
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
|
+
)}
|
|
1517
1597
|
{inputElement}
|
|
1518
|
-
{loading &&
|
|
1519
|
-
|
|
1520
|
-
|
|
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
|
+
)}
|
|
1521
1611
|
</div>
|
|
1522
1612
|
) : (
|
|
1523
1613
|
inputElement
|
|
@@ -1550,12 +1640,12 @@ const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
1550
1640
|
</div>
|
|
1551
1641
|
)}
|
|
1552
1642
|
</div>
|
|
1553
|
-
)
|
|
1643
|
+
);
|
|
1554
1644
|
}
|
|
1555
|
-
)
|
|
1556
|
-
TextField.displayName = "TextField"
|
|
1645
|
+
);
|
|
1646
|
+
TextField.displayName = "TextField";
|
|
1557
1647
|
|
|
1558
|
-
export { TextField, textFieldContainerVariants, textFieldInputVariants }
|
|
1648
|
+
export { TextField, textFieldContainerVariants, textFieldInputVariants };
|
|
1559
1649
|
`, prefix)
|
|
1560
1650
|
}
|
|
1561
1651
|
]
|
|
@@ -1572,10 +1662,10 @@ export { TextField, textFieldContainerVariants, textFieldInputVariants }
|
|
|
1572
1662
|
files: [
|
|
1573
1663
|
{
|
|
1574
1664
|
name: "select-field.tsx",
|
|
1575
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
1576
|
-
import { Loader2 } from "lucide-react"
|
|
1665
|
+
content: prefixTailwindClasses(`import * as React from "react";
|
|
1666
|
+
import { Loader2 } from "lucide-react";
|
|
1577
1667
|
|
|
1578
|
-
import { cn } from "../../lib/utils"
|
|
1668
|
+
import { cn } from "../../lib/utils";
|
|
1579
1669
|
import {
|
|
1580
1670
|
Select,
|
|
1581
1671
|
SelectContent,
|
|
@@ -1584,56 +1674,56 @@ import {
|
|
|
1584
1674
|
SelectLabel,
|
|
1585
1675
|
SelectTrigger,
|
|
1586
1676
|
SelectValue,
|
|
1587
|
-
} from "./select"
|
|
1677
|
+
} from "./select";
|
|
1588
1678
|
|
|
1589
1679
|
export interface SelectOption {
|
|
1590
1680
|
/** The value of the option */
|
|
1591
|
-
value: string
|
|
1681
|
+
value: string;
|
|
1592
1682
|
/** The display label of the option */
|
|
1593
|
-
label: string
|
|
1683
|
+
label: string;
|
|
1594
1684
|
/** Whether the option is disabled */
|
|
1595
|
-
disabled?: boolean
|
|
1685
|
+
disabled?: boolean;
|
|
1596
1686
|
/** Group name for grouping options */
|
|
1597
|
-
group?: string
|
|
1687
|
+
group?: string;
|
|
1598
1688
|
}
|
|
1599
1689
|
|
|
1600
1690
|
export interface SelectFieldProps {
|
|
1601
1691
|
/** Label text displayed above the select */
|
|
1602
|
-
label?: string
|
|
1692
|
+
label?: string;
|
|
1603
1693
|
/** Shows red asterisk next to label when true */
|
|
1604
|
-
required?: boolean
|
|
1694
|
+
required?: boolean;
|
|
1605
1695
|
/** Helper text displayed below the select */
|
|
1606
|
-
helperText?: string
|
|
1696
|
+
helperText?: string;
|
|
1607
1697
|
/** Error message - shows error state with red styling */
|
|
1608
|
-
error?: string
|
|
1698
|
+
error?: string;
|
|
1609
1699
|
/** Disabled state */
|
|
1610
|
-
disabled?: boolean
|
|
1700
|
+
disabled?: boolean;
|
|
1611
1701
|
/** Loading state with spinner */
|
|
1612
|
-
loading?: boolean
|
|
1702
|
+
loading?: boolean;
|
|
1613
1703
|
/** Placeholder text when no value selected */
|
|
1614
|
-
placeholder?: string
|
|
1704
|
+
placeholder?: string;
|
|
1615
1705
|
/** Currently selected value (controlled) */
|
|
1616
|
-
value?: string
|
|
1706
|
+
value?: string;
|
|
1617
1707
|
/** Default value (uncontrolled) */
|
|
1618
|
-
defaultValue?: string
|
|
1708
|
+
defaultValue?: string;
|
|
1619
1709
|
/** Callback when value changes */
|
|
1620
|
-
onValueChange?: (value: string) => void
|
|
1710
|
+
onValueChange?: (value: string) => void;
|
|
1621
1711
|
/** Options to display */
|
|
1622
|
-
options: SelectOption[]
|
|
1712
|
+
options: SelectOption[];
|
|
1623
1713
|
/** Enable search/filter functionality */
|
|
1624
|
-
searchable?: boolean
|
|
1714
|
+
searchable?: boolean;
|
|
1625
1715
|
/** Search placeholder text */
|
|
1626
|
-
searchPlaceholder?: string
|
|
1716
|
+
searchPlaceholder?: string;
|
|
1627
1717
|
/** Additional class for wrapper */
|
|
1628
|
-
wrapperClassName?: string
|
|
1718
|
+
wrapperClassName?: string;
|
|
1629
1719
|
/** Additional class for trigger */
|
|
1630
|
-
triggerClassName?: string
|
|
1720
|
+
triggerClassName?: string;
|
|
1631
1721
|
/** Additional class for label */
|
|
1632
|
-
labelClassName?: string
|
|
1722
|
+
labelClassName?: string;
|
|
1633
1723
|
/** ID for the select */
|
|
1634
|
-
id?: string
|
|
1724
|
+
id?: string;
|
|
1635
1725
|
/** Name attribute for form submission */
|
|
1636
|
-
name?: string
|
|
1726
|
+
name?: string;
|
|
1637
1727
|
}
|
|
1638
1728
|
|
|
1639
1729
|
/**
|
|
@@ -1678,59 +1768,59 @@ const SelectField = React.forwardRef<HTMLButtonElement, SelectFieldProps>(
|
|
|
1678
1768
|
ref
|
|
1679
1769
|
) => {
|
|
1680
1770
|
// Internal state for search
|
|
1681
|
-
const [searchQuery, setSearchQuery] = React.useState("")
|
|
1771
|
+
const [searchQuery, setSearchQuery] = React.useState("");
|
|
1682
1772
|
|
|
1683
1773
|
// Derive state from props
|
|
1684
|
-
const derivedState = error ? "error" : "default"
|
|
1774
|
+
const derivedState = error ? "error" : "default";
|
|
1685
1775
|
|
|
1686
1776
|
// Generate unique IDs for accessibility
|
|
1687
|
-
const generatedId = React.useId()
|
|
1688
|
-
const selectId = id || generatedId
|
|
1689
|
-
const helperId = \`\${selectId}-helper
|
|
1690
|
-
const errorId = \`\${selectId}-error
|
|
1777
|
+
const generatedId = React.useId();
|
|
1778
|
+
const selectId = id || generatedId;
|
|
1779
|
+
const helperId = \`\${selectId}-helper\`;
|
|
1780
|
+
const errorId = \`\${selectId}-error\`;
|
|
1691
1781
|
|
|
1692
1782
|
// Determine aria-describedby
|
|
1693
|
-
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined
|
|
1783
|
+
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined;
|
|
1694
1784
|
|
|
1695
1785
|
// Group options by group property
|
|
1696
1786
|
const groupedOptions = React.useMemo(() => {
|
|
1697
|
-
const groups: Record<string, SelectOption[]> = {}
|
|
1698
|
-
const ungrouped: SelectOption[] = []
|
|
1787
|
+
const groups: Record<string, SelectOption[]> = {};
|
|
1788
|
+
const ungrouped: SelectOption[] = [];
|
|
1699
1789
|
|
|
1700
1790
|
options.forEach((option) => {
|
|
1701
1791
|
// Filter by search query if searchable
|
|
1702
1792
|
if (searchable && searchQuery) {
|
|
1703
1793
|
if (!option.label.toLowerCase().includes(searchQuery.toLowerCase())) {
|
|
1704
|
-
return
|
|
1794
|
+
return;
|
|
1705
1795
|
}
|
|
1706
1796
|
}
|
|
1707
1797
|
|
|
1708
1798
|
if (option.group) {
|
|
1709
1799
|
if (!groups[option.group]) {
|
|
1710
|
-
groups[option.group] = []
|
|
1800
|
+
groups[option.group] = [];
|
|
1711
1801
|
}
|
|
1712
|
-
groups[option.group].push(option)
|
|
1802
|
+
groups[option.group].push(option);
|
|
1713
1803
|
} else {
|
|
1714
|
-
ungrouped.push(option)
|
|
1804
|
+
ungrouped.push(option);
|
|
1715
1805
|
}
|
|
1716
|
-
})
|
|
1806
|
+
});
|
|
1717
1807
|
|
|
1718
|
-
return { groups, ungrouped }
|
|
1719
|
-
}, [options, searchable, searchQuery])
|
|
1808
|
+
return { groups, ungrouped };
|
|
1809
|
+
}, [options, searchable, searchQuery]);
|
|
1720
1810
|
|
|
1721
|
-
const hasGroups = Object.keys(groupedOptions.groups).length > 0
|
|
1811
|
+
const hasGroups = Object.keys(groupedOptions.groups).length > 0;
|
|
1722
1812
|
|
|
1723
1813
|
// Handle search input change
|
|
1724
1814
|
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
1725
|
-
setSearchQuery(e.target.value)
|
|
1726
|
-
}
|
|
1815
|
+
setSearchQuery(e.target.value);
|
|
1816
|
+
};
|
|
1727
1817
|
|
|
1728
1818
|
// Reset search when dropdown closes
|
|
1729
1819
|
const handleOpenChange = (open: boolean) => {
|
|
1730
1820
|
if (!open) {
|
|
1731
|
-
setSearchQuery("")
|
|
1821
|
+
setSearchQuery("");
|
|
1732
1822
|
}
|
|
1733
|
-
}
|
|
1823
|
+
};
|
|
1734
1824
|
|
|
1735
1825
|
return (
|
|
1736
1826
|
<div className={cn("flex flex-col gap-1", wrapperClassName)}>
|
|
@@ -1758,10 +1848,7 @@ const SelectField = React.forwardRef<HTMLButtonElement, SelectFieldProps>(
|
|
|
1758
1848
|
ref={ref}
|
|
1759
1849
|
id={selectId}
|
|
1760
1850
|
state={derivedState}
|
|
1761
|
-
className={cn(
|
|
1762
|
-
loading && "pr-10",
|
|
1763
|
-
triggerClassName
|
|
1764
|
-
)}
|
|
1851
|
+
className={cn(loading && "pr-10", triggerClassName)}
|
|
1765
1852
|
aria-invalid={!!error}
|
|
1766
1853
|
aria-describedby={ariaDescribedBy}
|
|
1767
1854
|
>
|
|
@@ -1800,20 +1887,22 @@ const SelectField = React.forwardRef<HTMLButtonElement, SelectFieldProps>(
|
|
|
1800
1887
|
|
|
1801
1888
|
{/* Grouped options */}
|
|
1802
1889
|
{hasGroups &&
|
|
1803
|
-
Object.entries(groupedOptions.groups).map(
|
|
1804
|
-
|
|
1805
|
-
<
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
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
|
+
)}
|
|
1817
1906
|
|
|
1818
1907
|
{/* No results message */}
|
|
1819
1908
|
{searchable &&
|
|
@@ -1842,12 +1931,12 @@ const SelectField = React.forwardRef<HTMLButtonElement, SelectFieldProps>(
|
|
|
1842
1931
|
</div>
|
|
1843
1932
|
)}
|
|
1844
1933
|
</div>
|
|
1845
|
-
)
|
|
1934
|
+
);
|
|
1846
1935
|
}
|
|
1847
|
-
)
|
|
1848
|
-
SelectField.displayName = "SelectField"
|
|
1936
|
+
);
|
|
1937
|
+
SelectField.displayName = "SelectField";
|
|
1849
1938
|
|
|
1850
|
-
export { SelectField }
|
|
1939
|
+
export { SelectField };
|
|
1851
1940
|
`, prefix)
|
|
1852
1941
|
}
|
|
1853
1942
|
]
|
|
@@ -1864,11 +1953,11 @@ export { SelectField }
|
|
|
1864
1953
|
files: [
|
|
1865
1954
|
{
|
|
1866
1955
|
name: "multi-select.tsx",
|
|
1867
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
1868
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
1869
|
-
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";
|
|
1870
1959
|
|
|
1871
|
-
import { cn } from "../../lib/utils"
|
|
1960
|
+
import { cn } from "../../lib/utils";
|
|
1872
1961
|
|
|
1873
1962
|
/**
|
|
1874
1963
|
* MultiSelect trigger variants matching TextField styling
|
|
@@ -1878,64 +1967,68 @@ const multiSelectTriggerVariants = cva(
|
|
|
1878
1967
|
{
|
|
1879
1968
|
variants: {
|
|
1880
1969
|
state: {
|
|
1881
|
-
default:
|
|
1882
|
-
|
|
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)]",
|
|
1883
1974
|
},
|
|
1884
1975
|
},
|
|
1885
1976
|
defaultVariants: {
|
|
1886
1977
|
state: "default",
|
|
1887
1978
|
},
|
|
1888
1979
|
}
|
|
1889
|
-
)
|
|
1980
|
+
);
|
|
1890
1981
|
|
|
1891
1982
|
export interface MultiSelectOption {
|
|
1892
1983
|
/** The value of the option */
|
|
1893
|
-
value: string
|
|
1984
|
+
value: string;
|
|
1894
1985
|
/** The display label of the option */
|
|
1895
|
-
label: string
|
|
1986
|
+
label: string;
|
|
1896
1987
|
/** Whether the option is disabled */
|
|
1897
|
-
disabled?: boolean
|
|
1988
|
+
disabled?: boolean;
|
|
1898
1989
|
}
|
|
1899
1990
|
|
|
1900
|
-
export interface MultiSelectProps extends VariantProps<
|
|
1991
|
+
export interface MultiSelectProps extends VariantProps<
|
|
1992
|
+
typeof multiSelectTriggerVariants
|
|
1993
|
+
> {
|
|
1901
1994
|
/** Label text displayed above the select */
|
|
1902
|
-
label?: string
|
|
1995
|
+
label?: string;
|
|
1903
1996
|
/** Shows red asterisk next to label when true */
|
|
1904
|
-
required?: boolean
|
|
1997
|
+
required?: boolean;
|
|
1905
1998
|
/** Helper text displayed below the select */
|
|
1906
|
-
helperText?: string
|
|
1999
|
+
helperText?: string;
|
|
1907
2000
|
/** Error message - shows error state with red styling */
|
|
1908
|
-
error?: string
|
|
2001
|
+
error?: string;
|
|
1909
2002
|
/** Disabled state */
|
|
1910
|
-
disabled?: boolean
|
|
2003
|
+
disabled?: boolean;
|
|
1911
2004
|
/** Loading state with spinner */
|
|
1912
|
-
loading?: boolean
|
|
2005
|
+
loading?: boolean;
|
|
1913
2006
|
/** Placeholder text when no value selected */
|
|
1914
|
-
placeholder?: string
|
|
2007
|
+
placeholder?: string;
|
|
1915
2008
|
/** Currently selected values (controlled) */
|
|
1916
|
-
value?: string[]
|
|
2009
|
+
value?: string[];
|
|
1917
2010
|
/** Default values (uncontrolled) */
|
|
1918
|
-
defaultValue?: string[]
|
|
2011
|
+
defaultValue?: string[];
|
|
1919
2012
|
/** Callback when values change */
|
|
1920
|
-
onValueChange?: (value: string[]) => void
|
|
2013
|
+
onValueChange?: (value: string[]) => void;
|
|
1921
2014
|
/** Options to display */
|
|
1922
|
-
options: MultiSelectOption[]
|
|
2015
|
+
options: MultiSelectOption[];
|
|
1923
2016
|
/** Enable search/filter functionality */
|
|
1924
|
-
searchable?: boolean
|
|
2017
|
+
searchable?: boolean;
|
|
1925
2018
|
/** Search placeholder text */
|
|
1926
|
-
searchPlaceholder?: string
|
|
2019
|
+
searchPlaceholder?: string;
|
|
1927
2020
|
/** Maximum selections allowed */
|
|
1928
|
-
maxSelections?: number
|
|
2021
|
+
maxSelections?: number;
|
|
1929
2022
|
/** Additional class for wrapper */
|
|
1930
|
-
wrapperClassName?: string
|
|
2023
|
+
wrapperClassName?: string;
|
|
1931
2024
|
/** Additional class for trigger */
|
|
1932
|
-
triggerClassName?: string
|
|
2025
|
+
triggerClassName?: string;
|
|
1933
2026
|
/** Additional class for label */
|
|
1934
|
-
labelClassName?: string
|
|
2027
|
+
labelClassName?: string;
|
|
1935
2028
|
/** ID for the select */
|
|
1936
|
-
id?: string
|
|
2029
|
+
id?: string;
|
|
1937
2030
|
/** Name attribute for form submission */
|
|
1938
|
-
name?: string
|
|
2031
|
+
name?: string;
|
|
1939
2032
|
}
|
|
1940
2033
|
|
|
1941
2034
|
/**
|
|
@@ -1982,104 +2075,109 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
1982
2075
|
ref
|
|
1983
2076
|
) => {
|
|
1984
2077
|
// Internal state for selected values (uncontrolled mode)
|
|
1985
|
-
const [internalValue, setInternalValue] =
|
|
2078
|
+
const [internalValue, setInternalValue] =
|
|
2079
|
+
React.useState<string[]>(defaultValue);
|
|
1986
2080
|
// Dropdown open state
|
|
1987
|
-
const [isOpen, setIsOpen] = React.useState(false)
|
|
2081
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
1988
2082
|
// Search query
|
|
1989
|
-
const [searchQuery, setSearchQuery] = React.useState("")
|
|
2083
|
+
const [searchQuery, setSearchQuery] = React.useState("");
|
|
1990
2084
|
|
|
1991
2085
|
// Container ref for click outside detection
|
|
1992
|
-
const containerRef = React.useRef<HTMLDivElement>(null)
|
|
2086
|
+
const containerRef = React.useRef<HTMLDivElement>(null);
|
|
1993
2087
|
|
|
1994
2088
|
// Determine if controlled
|
|
1995
|
-
const isControlled = value !== undefined
|
|
1996
|
-
const selectedValues = isControlled ? value : internalValue
|
|
2089
|
+
const isControlled = value !== undefined;
|
|
2090
|
+
const selectedValues = isControlled ? value : internalValue;
|
|
1997
2091
|
|
|
1998
2092
|
// Derive state from props
|
|
1999
|
-
const derivedState = error ? "error" : (state ?? "default")
|
|
2093
|
+
const derivedState = error ? "error" : (state ?? "default");
|
|
2000
2094
|
|
|
2001
2095
|
// Generate unique IDs for accessibility
|
|
2002
|
-
const generatedId = React.useId()
|
|
2003
|
-
const selectId = id || generatedId
|
|
2004
|
-
const helperId = \`\${selectId}-helper
|
|
2005
|
-
const errorId = \`\${selectId}-error
|
|
2096
|
+
const generatedId = React.useId();
|
|
2097
|
+
const selectId = id || generatedId;
|
|
2098
|
+
const helperId = \`\${selectId}-helper\`;
|
|
2099
|
+
const errorId = \`\${selectId}-error\`;
|
|
2006
2100
|
|
|
2007
2101
|
// Determine aria-describedby
|
|
2008
|
-
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined
|
|
2102
|
+
const ariaDescribedBy = error ? errorId : helperText ? helperId : undefined;
|
|
2009
2103
|
|
|
2010
2104
|
// Filter options by search query
|
|
2011
2105
|
const filteredOptions = React.useMemo(() => {
|
|
2012
|
-
if (!searchable || !searchQuery) return options
|
|
2106
|
+
if (!searchable || !searchQuery) return options;
|
|
2013
2107
|
return options.filter((option) =>
|
|
2014
2108
|
option.label.toLowerCase().includes(searchQuery.toLowerCase())
|
|
2015
|
-
)
|
|
2016
|
-
}, [options, searchable, searchQuery])
|
|
2109
|
+
);
|
|
2110
|
+
}, [options, searchable, searchQuery]);
|
|
2017
2111
|
|
|
2018
2112
|
// Get selected option labels
|
|
2019
2113
|
const selectedLabels = React.useMemo(() => {
|
|
2020
2114
|
return selectedValues
|
|
2021
2115
|
.map((v) => options.find((o) => o.value === v)?.label)
|
|
2022
|
-
.filter(Boolean) as string[]
|
|
2023
|
-
}, [selectedValues, options])
|
|
2116
|
+
.filter(Boolean) as string[];
|
|
2117
|
+
}, [selectedValues, options]);
|
|
2024
2118
|
|
|
2025
2119
|
// Handle toggle selection
|
|
2026
2120
|
const toggleOption = (optionValue: string) => {
|
|
2027
2121
|
const newValues = selectedValues.includes(optionValue)
|
|
2028
2122
|
? selectedValues.filter((v) => v !== optionValue)
|
|
2029
2123
|
: maxSelections && selectedValues.length >= maxSelections
|
|
2030
|
-
|
|
2031
|
-
|
|
2124
|
+
? selectedValues
|
|
2125
|
+
: [...selectedValues, optionValue];
|
|
2032
2126
|
|
|
2033
2127
|
if (!isControlled) {
|
|
2034
|
-
setInternalValue(newValues)
|
|
2128
|
+
setInternalValue(newValues);
|
|
2035
2129
|
}
|
|
2036
|
-
onValueChange?.(newValues)
|
|
2037
|
-
}
|
|
2130
|
+
onValueChange?.(newValues);
|
|
2131
|
+
};
|
|
2038
2132
|
|
|
2039
2133
|
// Handle remove tag
|
|
2040
2134
|
const removeValue = (valueToRemove: string, e: React.MouseEvent) => {
|
|
2041
|
-
e.stopPropagation()
|
|
2042
|
-
const newValues = selectedValues.filter((v) => v !== valueToRemove)
|
|
2135
|
+
e.stopPropagation();
|
|
2136
|
+
const newValues = selectedValues.filter((v) => v !== valueToRemove);
|
|
2043
2137
|
if (!isControlled) {
|
|
2044
|
-
setInternalValue(newValues)
|
|
2138
|
+
setInternalValue(newValues);
|
|
2045
2139
|
}
|
|
2046
|
-
onValueChange?.(newValues)
|
|
2047
|
-
}
|
|
2140
|
+
onValueChange?.(newValues);
|
|
2141
|
+
};
|
|
2048
2142
|
|
|
2049
2143
|
// Handle clear all
|
|
2050
2144
|
const clearAll = (e: React.MouseEvent) => {
|
|
2051
|
-
e.stopPropagation()
|
|
2145
|
+
e.stopPropagation();
|
|
2052
2146
|
if (!isControlled) {
|
|
2053
|
-
setInternalValue([])
|
|
2147
|
+
setInternalValue([]);
|
|
2054
2148
|
}
|
|
2055
|
-
onValueChange?.([])
|
|
2056
|
-
}
|
|
2149
|
+
onValueChange?.([]);
|
|
2150
|
+
};
|
|
2057
2151
|
|
|
2058
2152
|
// Close dropdown when clicking outside
|
|
2059
2153
|
React.useEffect(() => {
|
|
2060
2154
|
const handleClickOutside = (event: MouseEvent) => {
|
|
2061
|
-
if (
|
|
2062
|
-
|
|
2063
|
-
|
|
2155
|
+
if (
|
|
2156
|
+
containerRef.current &&
|
|
2157
|
+
!containerRef.current.contains(event.target as Node)
|
|
2158
|
+
) {
|
|
2159
|
+
setIsOpen(false);
|
|
2160
|
+
setSearchQuery("");
|
|
2064
2161
|
}
|
|
2065
|
-
}
|
|
2162
|
+
};
|
|
2066
2163
|
|
|
2067
|
-
document.addEventListener("mousedown", handleClickOutside)
|
|
2068
|
-
return () =>
|
|
2069
|
-
|
|
2164
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
2165
|
+
return () =>
|
|
2166
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
2167
|
+
}, []);
|
|
2070
2168
|
|
|
2071
2169
|
// Handle keyboard navigation
|
|
2072
2170
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
2073
2171
|
if (e.key === "Escape") {
|
|
2074
|
-
setIsOpen(false)
|
|
2075
|
-
setSearchQuery("")
|
|
2172
|
+
setIsOpen(false);
|
|
2173
|
+
setSearchQuery("");
|
|
2076
2174
|
} else if (e.key === "Enter" || e.key === " ") {
|
|
2077
2175
|
if (!isOpen) {
|
|
2078
|
-
e.preventDefault()
|
|
2079
|
-
setIsOpen(true)
|
|
2176
|
+
e.preventDefault();
|
|
2177
|
+
setIsOpen(true);
|
|
2080
2178
|
}
|
|
2081
2179
|
}
|
|
2082
|
-
}
|
|
2180
|
+
};
|
|
2083
2181
|
|
|
2084
2182
|
return (
|
|
2085
2183
|
<div
|
|
@@ -2131,9 +2229,12 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2131
2229
|
tabIndex={0}
|
|
2132
2230
|
onClick={(e) => removeValue(selectedValues[index], e)}
|
|
2133
2231
|
onKeyDown={(e) => {
|
|
2134
|
-
if (e.key ===
|
|
2135
|
-
e.preventDefault()
|
|
2136
|
-
removeValue(
|
|
2232
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
2233
|
+
e.preventDefault();
|
|
2234
|
+
removeValue(
|
|
2235
|
+
selectedValues[index],
|
|
2236
|
+
e as unknown as React.MouseEvent
|
|
2237
|
+
);
|
|
2137
2238
|
}
|
|
2138
2239
|
}}
|
|
2139
2240
|
className="cursor-pointer hover:text-[#F04438] focus:outline-none"
|
|
@@ -2152,9 +2253,9 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2152
2253
|
tabIndex={0}
|
|
2153
2254
|
onClick={clearAll}
|
|
2154
2255
|
onKeyDown={(e) => {
|
|
2155
|
-
if (e.key ===
|
|
2156
|
-
e.preventDefault()
|
|
2157
|
-
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);
|
|
2158
2259
|
}
|
|
2159
2260
|
}}
|
|
2160
2261
|
className="p-0.5 cursor-pointer hover:text-[#F04438] focus:outline-none"
|
|
@@ -2208,10 +2309,13 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2208
2309
|
</div>
|
|
2209
2310
|
) : (
|
|
2210
2311
|
filteredOptions.map((option) => {
|
|
2211
|
-
const isSelected = selectedValues.includes(option.value)
|
|
2312
|
+
const isSelected = selectedValues.includes(option.value);
|
|
2212
2313
|
const isDisabled =
|
|
2213
2314
|
option.disabled ||
|
|
2214
|
-
(!isSelected &&
|
|
2315
|
+
(!isSelected &&
|
|
2316
|
+
maxSelections !== undefined &&
|
|
2317
|
+
maxSelections > 0 &&
|
|
2318
|
+
selectedValues.length >= maxSelections);
|
|
2215
2319
|
|
|
2216
2320
|
return (
|
|
2217
2321
|
<button
|
|
@@ -2229,11 +2333,13 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2229
2333
|
)}
|
|
2230
2334
|
>
|
|
2231
2335
|
<span className="absolute right-2 flex size-4 items-center justify-center">
|
|
2232
|
-
{isSelected &&
|
|
2336
|
+
{isSelected && (
|
|
2337
|
+
<Check className="size-4 text-[#2BBCCA]" />
|
|
2338
|
+
)}
|
|
2233
2339
|
</span>
|
|
2234
2340
|
{option.label}
|
|
2235
2341
|
</button>
|
|
2236
|
-
)
|
|
2342
|
+
);
|
|
2237
2343
|
})
|
|
2238
2344
|
)}
|
|
2239
2345
|
</div>
|
|
@@ -2248,9 +2354,10 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2248
2354
|
)}
|
|
2249
2355
|
|
|
2250
2356
|
{/* Hidden input for form submission */}
|
|
2251
|
-
{name &&
|
|
2252
|
-
|
|
2253
|
-
|
|
2357
|
+
{name &&
|
|
2358
|
+
selectedValues.map((v) => (
|
|
2359
|
+
<input key={v} type="hidden" name={name} value={v} />
|
|
2360
|
+
))}
|
|
2254
2361
|
|
|
2255
2362
|
{/* Helper text / Error message */}
|
|
2256
2363
|
{(error || helperText) && (
|
|
@@ -2267,12 +2374,12 @@ const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
|
|
2267
2374
|
</div>
|
|
2268
2375
|
)}
|
|
2269
2376
|
</div>
|
|
2270
|
-
)
|
|
2377
|
+
);
|
|
2271
2378
|
}
|
|
2272
|
-
)
|
|
2273
|
-
MultiSelect.displayName = "MultiSelect"
|
|
2379
|
+
);
|
|
2380
|
+
MultiSelect.displayName = "MultiSelect";
|
|
2274
2381
|
|
|
2275
|
-
export { MultiSelect, multiSelectTriggerVariants }
|
|
2382
|
+
export { MultiSelect, multiSelectTriggerVariants };
|
|
2276
2383
|
`, prefix)
|
|
2277
2384
|
}
|
|
2278
2385
|
]
|
|
@@ -2288,30 +2395,27 @@ export { MultiSelect, multiSelectTriggerVariants }
|
|
|
2288
2395
|
files: [
|
|
2289
2396
|
{
|
|
2290
2397
|
name: "table.tsx",
|
|
2291
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
2292
|
-
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";
|
|
2293
2400
|
|
|
2294
|
-
import { cn } from "../../lib/utils"
|
|
2295
|
-
import { Switch, type SwitchProps } from "./switch"
|
|
2401
|
+
import { cn } from "../../lib/utils";
|
|
2402
|
+
import { Switch, type SwitchProps } from "./switch";
|
|
2296
2403
|
|
|
2297
2404
|
/**
|
|
2298
2405
|
* Table size variants for row height.
|
|
2299
2406
|
*/
|
|
2300
|
-
const tableVariants = cva(
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
md: "[&_td]:py-3 [&_th]:py-3",
|
|
2307
|
-
lg: "[&_td]:py-4 [&_th]:py-4",
|
|
2308
|
-
},
|
|
2309
|
-
},
|
|
2310
|
-
defaultVariants: {
|
|
2311
|
-
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",
|
|
2312
2413
|
},
|
|
2313
|
-
}
|
|
2314
|
-
|
|
2414
|
+
},
|
|
2415
|
+
defaultVariants: {
|
|
2416
|
+
size: "md",
|
|
2417
|
+
},
|
|
2418
|
+
});
|
|
2315
2419
|
|
|
2316
2420
|
/**
|
|
2317
2421
|
* Table component for displaying tabular data.
|
|
@@ -2336,18 +2440,21 @@ const tableVariants = cva(
|
|
|
2336
2440
|
*/
|
|
2337
2441
|
|
|
2338
2442
|
export interface TableProps
|
|
2339
|
-
extends
|
|
2443
|
+
extends
|
|
2444
|
+
React.HTMLAttributes<HTMLTableElement>,
|
|
2340
2445
|
VariantProps<typeof tableVariants> {
|
|
2341
2446
|
/** Remove outer border from the table */
|
|
2342
|
-
withoutBorder?: boolean
|
|
2447
|
+
withoutBorder?: boolean;
|
|
2343
2448
|
}
|
|
2344
2449
|
|
|
2345
2450
|
const Table = React.forwardRef<HTMLTableElement, TableProps>(
|
|
2346
2451
|
({ className, size, withoutBorder, ...props }, ref) => (
|
|
2347
|
-
<div
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2452
|
+
<div
|
|
2453
|
+
className={cn(
|
|
2454
|
+
"relative w-full overflow-auto",
|
|
2455
|
+
!withoutBorder && "rounded-lg border border-[#E9EAEB]"
|
|
2456
|
+
)}
|
|
2457
|
+
>
|
|
2351
2458
|
<table
|
|
2352
2459
|
ref={ref}
|
|
2353
2460
|
className={cn(tableVariants({ size, className }))}
|
|
@@ -2355,8 +2462,8 @@ const Table = React.forwardRef<HTMLTableElement, TableProps>(
|
|
|
2355
2462
|
/>
|
|
2356
2463
|
</div>
|
|
2357
2464
|
)
|
|
2358
|
-
)
|
|
2359
|
-
Table.displayName = "Table"
|
|
2465
|
+
);
|
|
2466
|
+
Table.displayName = "Table";
|
|
2360
2467
|
|
|
2361
2468
|
const TableHeader = React.forwardRef<
|
|
2362
2469
|
HTMLTableSectionElement,
|
|
@@ -2367,8 +2474,8 @@ const TableHeader = React.forwardRef<
|
|
|
2367
2474
|
className={cn("bg-[#FAFAFA] [&_tr]:border-b", className)}
|
|
2368
2475
|
{...props}
|
|
2369
2476
|
/>
|
|
2370
|
-
))
|
|
2371
|
-
TableHeader.displayName = "TableHeader"
|
|
2477
|
+
));
|
|
2478
|
+
TableHeader.displayName = "TableHeader";
|
|
2372
2479
|
|
|
2373
2480
|
const TableBody = React.forwardRef<
|
|
2374
2481
|
HTMLTableSectionElement,
|
|
@@ -2379,8 +2486,8 @@ const TableBody = React.forwardRef<
|
|
|
2379
2486
|
className={cn("[&_tr:last-child]:border-0", className)}
|
|
2380
2487
|
{...props}
|
|
2381
2488
|
/>
|
|
2382
|
-
))
|
|
2383
|
-
TableBody.displayName = "TableBody"
|
|
2489
|
+
));
|
|
2490
|
+
TableBody.displayName = "TableBody";
|
|
2384
2491
|
|
|
2385
2492
|
const TableFooter = React.forwardRef<
|
|
2386
2493
|
HTMLTableSectionElement,
|
|
@@ -2394,12 +2501,12 @@ const TableFooter = React.forwardRef<
|
|
|
2394
2501
|
)}
|
|
2395
2502
|
{...props}
|
|
2396
2503
|
/>
|
|
2397
|
-
))
|
|
2398
|
-
TableFooter.displayName = "TableFooter"
|
|
2504
|
+
));
|
|
2505
|
+
TableFooter.displayName = "TableFooter";
|
|
2399
2506
|
|
|
2400
2507
|
export interface TableRowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
|
2401
2508
|
/** Highlight the row with a colored background */
|
|
2402
|
-
highlighted?: boolean
|
|
2509
|
+
highlighted?: boolean;
|
|
2403
2510
|
}
|
|
2404
2511
|
|
|
2405
2512
|
const TableRow = React.forwardRef<HTMLTableRowElement, TableRowProps>(
|
|
@@ -2416,20 +2523,23 @@ const TableRow = React.forwardRef<HTMLTableRowElement, TableRowProps>(
|
|
|
2416
2523
|
{...props}
|
|
2417
2524
|
/>
|
|
2418
2525
|
)
|
|
2419
|
-
)
|
|
2420
|
-
TableRow.displayName = "TableRow"
|
|
2526
|
+
);
|
|
2527
|
+
TableRow.displayName = "TableRow";
|
|
2421
2528
|
|
|
2422
2529
|
export interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
|
|
2423
2530
|
/** Make this column sticky on horizontal scroll */
|
|
2424
|
-
sticky?: boolean
|
|
2531
|
+
sticky?: boolean;
|
|
2425
2532
|
/** Sort direction indicator */
|
|
2426
|
-
sortDirection?:
|
|
2533
|
+
sortDirection?: "asc" | "desc" | null;
|
|
2427
2534
|
/** Show info icon with tooltip */
|
|
2428
|
-
infoTooltip?: string
|
|
2535
|
+
infoTooltip?: string;
|
|
2429
2536
|
}
|
|
2430
2537
|
|
|
2431
2538
|
const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
2432
|
-
(
|
|
2539
|
+
(
|
|
2540
|
+
{ className, sticky, sortDirection, infoTooltip, children, ...props },
|
|
2541
|
+
ref
|
|
2542
|
+
) => (
|
|
2433
2543
|
<th
|
|
2434
2544
|
ref={ref}
|
|
2435
2545
|
className={cn(
|
|
@@ -2444,7 +2554,7 @@ const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
|
2444
2554
|
{children}
|
|
2445
2555
|
{sortDirection && (
|
|
2446
2556
|
<span className="text-[#A4A7AE]">
|
|
2447
|
-
{sortDirection ===
|
|
2557
|
+
{sortDirection === "asc" ? "\u2191" : "\u2193"}
|
|
2448
2558
|
</span>
|
|
2449
2559
|
)}
|
|
2450
2560
|
{infoTooltip && (
|
|
@@ -2455,12 +2565,12 @@ const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
|
|
|
2455
2565
|
</div>
|
|
2456
2566
|
</th>
|
|
2457
2567
|
)
|
|
2458
|
-
)
|
|
2459
|
-
TableHead.displayName = "TableHead"
|
|
2568
|
+
);
|
|
2569
|
+
TableHead.displayName = "TableHead";
|
|
2460
2570
|
|
|
2461
2571
|
export interface TableCellProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
|
|
2462
2572
|
/** Make this cell sticky on horizontal scroll */
|
|
2463
|
-
sticky?: boolean
|
|
2573
|
+
sticky?: boolean;
|
|
2464
2574
|
}
|
|
2465
2575
|
|
|
2466
2576
|
const TableCell = React.forwardRef<HTMLTableCellElement, TableCellProps>(
|
|
@@ -2475,8 +2585,8 @@ const TableCell = React.forwardRef<HTMLTableCellElement, TableCellProps>(
|
|
|
2475
2585
|
{...props}
|
|
2476
2586
|
/>
|
|
2477
2587
|
)
|
|
2478
|
-
)
|
|
2479
|
-
TableCell.displayName = "TableCell"
|
|
2588
|
+
);
|
|
2589
|
+
TableCell.displayName = "TableCell";
|
|
2480
2590
|
|
|
2481
2591
|
const TableCaption = React.forwardRef<
|
|
2482
2592
|
HTMLTableCaptionElement,
|
|
@@ -2487,17 +2597,17 @@ const TableCaption = React.forwardRef<
|
|
|
2487
2597
|
className={cn("mt-4 text-sm text-[#717680]", className)}
|
|
2488
2598
|
{...props}
|
|
2489
2599
|
/>
|
|
2490
|
-
))
|
|
2491
|
-
TableCaption.displayName = "TableCaption"
|
|
2600
|
+
));
|
|
2601
|
+
TableCaption.displayName = "TableCaption";
|
|
2492
2602
|
|
|
2493
2603
|
/**
|
|
2494
2604
|
* TableSkeleton - Loading state for table rows
|
|
2495
2605
|
*/
|
|
2496
2606
|
export interface TableSkeletonProps {
|
|
2497
2607
|
/** Number of rows to show */
|
|
2498
|
-
rows?: number
|
|
2608
|
+
rows?: number;
|
|
2499
2609
|
/** Number of columns to show */
|
|
2500
|
-
columns?: number
|
|
2610
|
+
columns?: number;
|
|
2501
2611
|
}
|
|
2502
2612
|
|
|
2503
2613
|
const TableSkeleton = ({ rows = 5, columns = 5 }: TableSkeletonProps) => (
|
|
@@ -2506,24 +2616,28 @@ const TableSkeleton = ({ rows = 5, columns = 5 }: TableSkeletonProps) => (
|
|
|
2506
2616
|
<TableRow key={rowIndex}>
|
|
2507
2617
|
{Array.from({ length: columns }).map((_, colIndex) => (
|
|
2508
2618
|
<TableCell key={colIndex}>
|
|
2509
|
-
<div
|
|
2510
|
-
|
|
2619
|
+
<div
|
|
2620
|
+
className="h-4 bg-[#E9EAEB] rounded animate-pulse"
|
|
2621
|
+
style={{
|
|
2622
|
+
width: colIndex === 1 ? "80%" : colIndex === 2 ? "30%" : "60%",
|
|
2623
|
+
}}
|
|
2624
|
+
/>
|
|
2511
2625
|
</TableCell>
|
|
2512
2626
|
))}
|
|
2513
2627
|
</TableRow>
|
|
2514
2628
|
))}
|
|
2515
2629
|
</>
|
|
2516
|
-
)
|
|
2517
|
-
TableSkeleton.displayName = "TableSkeleton"
|
|
2630
|
+
);
|
|
2631
|
+
TableSkeleton.displayName = "TableSkeleton";
|
|
2518
2632
|
|
|
2519
2633
|
/**
|
|
2520
2634
|
* TableEmpty - Empty state message
|
|
2521
2635
|
*/
|
|
2522
2636
|
export interface TableEmptyProps {
|
|
2523
2637
|
/** Number of columns to span */
|
|
2524
|
-
colSpan: number
|
|
2638
|
+
colSpan: number;
|
|
2525
2639
|
/** Custom message or component */
|
|
2526
|
-
children?: React.ReactNode
|
|
2640
|
+
children?: React.ReactNode;
|
|
2527
2641
|
}
|
|
2528
2642
|
|
|
2529
2643
|
const TableEmpty = ({ colSpan, children }: TableEmptyProps) => (
|
|
@@ -2532,17 +2646,17 @@ const TableEmpty = ({ colSpan, children }: TableEmptyProps) => (
|
|
|
2532
2646
|
{children || "No data available"}
|
|
2533
2647
|
</TableCell>
|
|
2534
2648
|
</TableRow>
|
|
2535
|
-
)
|
|
2536
|
-
TableEmpty.displayName = "TableEmpty"
|
|
2649
|
+
);
|
|
2650
|
+
TableEmpty.displayName = "TableEmpty";
|
|
2537
2651
|
|
|
2538
2652
|
/**
|
|
2539
2653
|
* Avatar component for table cells
|
|
2540
2654
|
*/
|
|
2541
2655
|
export interface TableAvatarProps {
|
|
2542
2656
|
/** Initials to display */
|
|
2543
|
-
initials: string
|
|
2657
|
+
initials: string;
|
|
2544
2658
|
/** Background color */
|
|
2545
|
-
color?: string
|
|
2659
|
+
color?: string;
|
|
2546
2660
|
}
|
|
2547
2661
|
|
|
2548
2662
|
const TableAvatar = ({ initials, color = "#7C3AED" }: TableAvatarProps) => (
|
|
@@ -2552,23 +2666,23 @@ const TableAvatar = ({ initials, color = "#7C3AED" }: TableAvatarProps) => (
|
|
|
2552
2666
|
>
|
|
2553
2667
|
{initials}
|
|
2554
2668
|
</div>
|
|
2555
|
-
)
|
|
2556
|
-
TableAvatar.displayName = "TableAvatar"
|
|
2669
|
+
);
|
|
2670
|
+
TableAvatar.displayName = "TableAvatar";
|
|
2557
2671
|
|
|
2558
2672
|
/**
|
|
2559
2673
|
* Switch component optimized for table cells (previously TableToggle)
|
|
2560
2674
|
*/
|
|
2561
|
-
export interface TableToggleProps extends Omit<SwitchProps,
|
|
2675
|
+
export interface TableToggleProps extends Omit<SwitchProps, "size"> {
|
|
2562
2676
|
/** Size of the switch - defaults to 'sm' for tables */
|
|
2563
|
-
size?:
|
|
2677
|
+
size?: "sm" | "default";
|
|
2564
2678
|
}
|
|
2565
2679
|
|
|
2566
2680
|
const TableToggle = React.forwardRef<HTMLButtonElement, TableToggleProps>(
|
|
2567
|
-
({ size =
|
|
2681
|
+
({ size = "sm", ...props }, ref) => (
|
|
2568
2682
|
<Switch ref={ref} size={size} {...props} />
|
|
2569
2683
|
)
|
|
2570
|
-
)
|
|
2571
|
-
TableToggle.displayName = "TableToggle"
|
|
2684
|
+
);
|
|
2685
|
+
TableToggle.displayName = "TableToggle";
|
|
2572
2686
|
|
|
2573
2687
|
export {
|
|
2574
2688
|
Table,
|
|
@@ -2584,7 +2698,7 @@ export {
|
|
|
2584
2698
|
TableAvatar,
|
|
2585
2699
|
TableToggle,
|
|
2586
2700
|
tableVariants,
|
|
2587
|
-
}
|
|
2701
|
+
};
|
|
2588
2702
|
`, prefix)
|
|
2589
2703
|
}
|
|
2590
2704
|
]
|
|
@@ -2601,28 +2715,28 @@ export {
|
|
|
2601
2715
|
files: [
|
|
2602
2716
|
{
|
|
2603
2717
|
name: "dropdown-menu.tsx",
|
|
2604
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
2605
|
-
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
|
2606
|
-
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";
|
|
2607
2721
|
|
|
2608
|
-
import { cn } from "../../lib/utils"
|
|
2722
|
+
import { cn } from "../../lib/utils";
|
|
2609
2723
|
|
|
2610
|
-
const DropdownMenu = DropdownMenuPrimitive.Root
|
|
2724
|
+
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
2611
2725
|
|
|
2612
|
-
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
|
|
2726
|
+
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
2613
2727
|
|
|
2614
|
-
const DropdownMenuGroup = DropdownMenuPrimitive.Group
|
|
2728
|
+
const DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
2615
2729
|
|
|
2616
|
-
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
|
|
2730
|
+
const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
2617
2731
|
|
|
2618
|
-
const DropdownMenuSub = DropdownMenuPrimitive.Sub
|
|
2732
|
+
const DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
2619
2733
|
|
|
2620
|
-
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
|
|
2734
|
+
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
2621
2735
|
|
|
2622
2736
|
const DropdownMenuSubTrigger = React.forwardRef<
|
|
2623
2737
|
React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,
|
|
2624
2738
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {
|
|
2625
|
-
inset?: boolean
|
|
2739
|
+
inset?: boolean;
|
|
2626
2740
|
}
|
|
2627
2741
|
>(({ className, inset, children, ...props }, ref) => (
|
|
2628
2742
|
<DropdownMenuPrimitive.SubTrigger
|
|
@@ -2637,9 +2751,9 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
|
|
2637
2751
|
{children}
|
|
2638
2752
|
<ChevronRight className="ml-auto h-4 w-4" />
|
|
2639
2753
|
</DropdownMenuPrimitive.SubTrigger>
|
|
2640
|
-
))
|
|
2754
|
+
));
|
|
2641
2755
|
DropdownMenuSubTrigger.displayName =
|
|
2642
|
-
DropdownMenuPrimitive.SubTrigger.displayName
|
|
2756
|
+
DropdownMenuPrimitive.SubTrigger.displayName;
|
|
2643
2757
|
|
|
2644
2758
|
const DropdownMenuSubContent = React.forwardRef<
|
|
2645
2759
|
React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
|
|
@@ -2653,9 +2767,9 @@ const DropdownMenuSubContent = React.forwardRef<
|
|
|
2653
2767
|
)}
|
|
2654
2768
|
{...props}
|
|
2655
2769
|
/>
|
|
2656
|
-
))
|
|
2770
|
+
));
|
|
2657
2771
|
DropdownMenuSubContent.displayName =
|
|
2658
|
-
DropdownMenuPrimitive.SubContent.displayName
|
|
2772
|
+
DropdownMenuPrimitive.SubContent.displayName;
|
|
2659
2773
|
|
|
2660
2774
|
const DropdownMenuContent = React.forwardRef<
|
|
2661
2775
|
React.ElementRef<typeof DropdownMenuPrimitive.Content>,
|
|
@@ -2673,13 +2787,13 @@ const DropdownMenuContent = React.forwardRef<
|
|
|
2673
2787
|
{...props}
|
|
2674
2788
|
/>
|
|
2675
2789
|
</DropdownMenuPrimitive.Portal>
|
|
2676
|
-
))
|
|
2677
|
-
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
|
|
2790
|
+
));
|
|
2791
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
2678
2792
|
|
|
2679
2793
|
const DropdownMenuItem = React.forwardRef<
|
|
2680
2794
|
React.ElementRef<typeof DropdownMenuPrimitive.Item>,
|
|
2681
2795
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {
|
|
2682
|
-
inset?: boolean
|
|
2796
|
+
inset?: boolean;
|
|
2683
2797
|
}
|
|
2684
2798
|
>(({ className, inset, ...props }, ref) => (
|
|
2685
2799
|
<DropdownMenuPrimitive.Item
|
|
@@ -2691,8 +2805,8 @@ const DropdownMenuItem = React.forwardRef<
|
|
|
2691
2805
|
)}
|
|
2692
2806
|
{...props}
|
|
2693
2807
|
/>
|
|
2694
|
-
))
|
|
2695
|
-
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
|
|
2808
|
+
));
|
|
2809
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
2696
2810
|
|
|
2697
2811
|
const DropdownMenuCheckboxItem = React.forwardRef<
|
|
2698
2812
|
React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,
|
|
@@ -2714,9 +2828,9 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
|
|
2714
2828
|
</span>
|
|
2715
2829
|
{children}
|
|
2716
2830
|
</DropdownMenuPrimitive.CheckboxItem>
|
|
2717
|
-
))
|
|
2831
|
+
));
|
|
2718
2832
|
DropdownMenuCheckboxItem.displayName =
|
|
2719
|
-
DropdownMenuPrimitive.CheckboxItem.displayName
|
|
2833
|
+
DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
2720
2834
|
|
|
2721
2835
|
const DropdownMenuRadioItem = React.forwardRef<
|
|
2722
2836
|
React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,
|
|
@@ -2737,13 +2851,13 @@ const DropdownMenuRadioItem = React.forwardRef<
|
|
|
2737
2851
|
</span>
|
|
2738
2852
|
{children}
|
|
2739
2853
|
</DropdownMenuPrimitive.RadioItem>
|
|
2740
|
-
))
|
|
2741
|
-
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
|
|
2854
|
+
));
|
|
2855
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
2742
2856
|
|
|
2743
2857
|
const DropdownMenuLabel = React.forwardRef<
|
|
2744
2858
|
React.ElementRef<typeof DropdownMenuPrimitive.Label>,
|
|
2745
2859
|
React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {
|
|
2746
|
-
inset?: boolean
|
|
2860
|
+
inset?: boolean;
|
|
2747
2861
|
}
|
|
2748
2862
|
>(({ className, inset, ...props }, ref) => (
|
|
2749
2863
|
<DropdownMenuPrimitive.Label
|
|
@@ -2755,8 +2869,8 @@ const DropdownMenuLabel = React.forwardRef<
|
|
|
2755
2869
|
)}
|
|
2756
2870
|
{...props}
|
|
2757
2871
|
/>
|
|
2758
|
-
))
|
|
2759
|
-
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
|
|
2872
|
+
));
|
|
2873
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
2760
2874
|
|
|
2761
2875
|
const DropdownMenuSeparator = React.forwardRef<
|
|
2762
2876
|
React.ElementRef<typeof DropdownMenuPrimitive.Separator>,
|
|
@@ -2767,8 +2881,8 @@ const DropdownMenuSeparator = React.forwardRef<
|
|
|
2767
2881
|
className={cn("-mx-1 my-1 h-px bg-[#E9EAEB]", className)}
|
|
2768
2882
|
{...props}
|
|
2769
2883
|
/>
|
|
2770
|
-
))
|
|
2771
|
-
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
|
|
2884
|
+
));
|
|
2885
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
2772
2886
|
|
|
2773
2887
|
const DropdownMenuShortcut = ({
|
|
2774
2888
|
className,
|
|
@@ -2779,9 +2893,9 @@ const DropdownMenuShortcut = ({
|
|
|
2779
2893
|
className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
|
|
2780
2894
|
{...props}
|
|
2781
2895
|
/>
|
|
2782
|
-
)
|
|
2783
|
-
}
|
|
2784
|
-
DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
|
|
2896
|
+
);
|
|
2897
|
+
};
|
|
2898
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
2785
2899
|
|
|
2786
2900
|
export {
|
|
2787
2901
|
DropdownMenu,
|
|
@@ -2799,7 +2913,7 @@ export {
|
|
|
2799
2913
|
DropdownMenuSubContent,
|
|
2800
2914
|
DropdownMenuSubTrigger,
|
|
2801
2915
|
DropdownMenuRadioGroup,
|
|
2802
|
-
}
|
|
2916
|
+
};
|
|
2803
2917
|
`, prefix)
|
|
2804
2918
|
}
|
|
2805
2919
|
]
|
|
@@ -2815,16 +2929,16 @@ export {
|
|
|
2815
2929
|
files: [
|
|
2816
2930
|
{
|
|
2817
2931
|
name: "tooltip.tsx",
|
|
2818
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
2819
|
-
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";
|
|
2820
2934
|
|
|
2821
|
-
import { cn } from "../../lib/utils"
|
|
2935
|
+
import { cn } from "../../lib/utils";
|
|
2822
2936
|
|
|
2823
|
-
const TooltipProvider = TooltipPrimitive.Provider
|
|
2937
|
+
const TooltipProvider = TooltipPrimitive.Provider;
|
|
2824
2938
|
|
|
2825
|
-
const Tooltip = TooltipPrimitive.Root
|
|
2939
|
+
const Tooltip = TooltipPrimitive.Root;
|
|
2826
2940
|
|
|
2827
|
-
const TooltipTrigger = TooltipPrimitive.Trigger
|
|
2941
|
+
const TooltipTrigger = TooltipPrimitive.Trigger;
|
|
2828
2942
|
|
|
2829
2943
|
const TooltipContent = React.forwardRef<
|
|
2830
2944
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
@@ -2841,8 +2955,8 @@ const TooltipContent = React.forwardRef<
|
|
|
2841
2955
|
{...props}
|
|
2842
2956
|
/>
|
|
2843
2957
|
</TooltipPrimitive.Portal>
|
|
2844
|
-
))
|
|
2845
|
-
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
|
2958
|
+
));
|
|
2959
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
2846
2960
|
|
|
2847
2961
|
const TooltipArrow = React.forwardRef<
|
|
2848
2962
|
React.ElementRef<typeof TooltipPrimitive.Arrow>,
|
|
@@ -2853,8 +2967,8 @@ const TooltipArrow = React.forwardRef<
|
|
|
2853
2967
|
className={cn("fill-[#343E55]", className)}
|
|
2854
2968
|
{...props}
|
|
2855
2969
|
/>
|
|
2856
|
-
))
|
|
2857
|
-
TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName
|
|
2970
|
+
));
|
|
2971
|
+
TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName;
|
|
2858
2972
|
|
|
2859
2973
|
export {
|
|
2860
2974
|
Tooltip,
|
|
@@ -2862,7 +2976,7 @@ export {
|
|
|
2862
2976
|
TooltipContent,
|
|
2863
2977
|
TooltipArrow,
|
|
2864
2978
|
TooltipProvider,
|
|
2865
|
-
}
|
|
2979
|
+
};
|
|
2866
2980
|
`, prefix)
|
|
2867
2981
|
}
|
|
2868
2982
|
]
|
|
@@ -2878,41 +2992,38 @@ export {
|
|
|
2878
2992
|
files: [
|
|
2879
2993
|
{
|
|
2880
2994
|
name: "tag.tsx",
|
|
2881
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
2882
|
-
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";
|
|
2883
2997
|
|
|
2884
|
-
import { cn } from "../../lib/utils"
|
|
2998
|
+
import { cn } from "../../lib/utils";
|
|
2885
2999
|
|
|
2886
3000
|
/**
|
|
2887
3001
|
* Tag variants for event labels and categories.
|
|
2888
3002
|
* Rounded rectangle tags with optional bold labels.
|
|
2889
3003
|
*/
|
|
2890
|
-
const tagVariants = cva(
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
error: "bg-[#FEF3F2] text-[#F04438]",
|
|
2902
|
-
destructive: "bg-[#FEF3F2] text-[#F04438]",
|
|
2903
|
-
},
|
|
2904
|
-
size: {
|
|
2905
|
-
default: "px-2 py-1",
|
|
2906
|
-
sm: "px-1.5 py-0.5 text-xs",
|
|
2907
|
-
lg: "px-3 py-1.5",
|
|
2908
|
-
},
|
|
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]",
|
|
2909
3015
|
},
|
|
2910
|
-
|
|
2911
|
-
|
|
2912
|
-
|
|
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",
|
|
2913
3020
|
},
|
|
2914
|
-
}
|
|
2915
|
-
|
|
3021
|
+
},
|
|
3022
|
+
defaultVariants: {
|
|
3023
|
+
variant: "default",
|
|
3024
|
+
size: "default",
|
|
3025
|
+
},
|
|
3026
|
+
});
|
|
2916
3027
|
|
|
2917
3028
|
/**
|
|
2918
3029
|
* Tag component for displaying event labels and categories.
|
|
@@ -2924,10 +3035,11 @@ const tagVariants = cva(
|
|
|
2924
3035
|
* \`\`\`
|
|
2925
3036
|
*/
|
|
2926
3037
|
export interface TagProps
|
|
2927
|
-
extends
|
|
3038
|
+
extends
|
|
3039
|
+
React.HTMLAttributes<HTMLSpanElement>,
|
|
2928
3040
|
VariantProps<typeof tagVariants> {
|
|
2929
3041
|
/** Bold label prefix displayed before the content */
|
|
2930
|
-
label?: string
|
|
3042
|
+
label?: string;
|
|
2931
3043
|
}
|
|
2932
3044
|
|
|
2933
3045
|
const Tag = React.forwardRef<HTMLSpanElement, TagProps>(
|
|
@@ -2938,15 +3050,13 @@ const Tag = React.forwardRef<HTMLSpanElement, TagProps>(
|
|
|
2938
3050
|
ref={ref}
|
|
2939
3051
|
{...props}
|
|
2940
3052
|
>
|
|
2941
|
-
{label &&
|
|
2942
|
-
<span className="font-semibold mr-1">{label}</span>
|
|
2943
|
-
)}
|
|
3053
|
+
{label && <span className="font-semibold mr-1">{label}</span>}
|
|
2944
3054
|
<span className="font-normal">{children}</span>
|
|
2945
3055
|
</span>
|
|
2946
|
-
)
|
|
3056
|
+
);
|
|
2947
3057
|
}
|
|
2948
|
-
)
|
|
2949
|
-
Tag.displayName = "Tag"
|
|
3058
|
+
);
|
|
3059
|
+
Tag.displayName = "Tag";
|
|
2950
3060
|
|
|
2951
3061
|
/**
|
|
2952
3062
|
* TagGroup component for displaying multiple tags with overflow indicator.
|
|
@@ -2965,15 +3075,15 @@ Tag.displayName = "Tag"
|
|
|
2965
3075
|
*/
|
|
2966
3076
|
export interface TagGroupProps {
|
|
2967
3077
|
/** Array of tags to display */
|
|
2968
|
-
tags: Array<{ label?: string; value: string }
|
|
3078
|
+
tags: Array<{ label?: string; value: string }>;
|
|
2969
3079
|
/** Maximum number of tags to show before overflow (default: 2) */
|
|
2970
|
-
maxVisible?: number
|
|
3080
|
+
maxVisible?: number;
|
|
2971
3081
|
/** Tag variant */
|
|
2972
|
-
variant?: TagProps[
|
|
3082
|
+
variant?: TagProps["variant"];
|
|
2973
3083
|
/** Tag size */
|
|
2974
|
-
size?: TagProps[
|
|
3084
|
+
size?: TagProps["size"];
|
|
2975
3085
|
/** Additional className for the container */
|
|
2976
|
-
className?: string
|
|
3086
|
+
className?: string;
|
|
2977
3087
|
}
|
|
2978
3088
|
|
|
2979
3089
|
const TagGroup = ({
|
|
@@ -2983,13 +3093,14 @@ const TagGroup = ({
|
|
|
2983
3093
|
size,
|
|
2984
3094
|
className,
|
|
2985
3095
|
}: TagGroupProps) => {
|
|
2986
|
-
const visibleTags = tags.slice(0, maxVisible)
|
|
2987
|
-
const overflowCount = tags.length - maxVisible
|
|
3096
|
+
const visibleTags = tags.slice(0, maxVisible);
|
|
3097
|
+
const overflowCount = tags.length - maxVisible;
|
|
2988
3098
|
|
|
2989
3099
|
return (
|
|
2990
3100
|
<div className={cn("flex flex-col items-start gap-2", className)}>
|
|
2991
3101
|
{visibleTags.map((tag, index) => {
|
|
2992
|
-
const isLastVisible =
|
|
3102
|
+
const isLastVisible =
|
|
3103
|
+
index === visibleTags.length - 1 && overflowCount > 0;
|
|
2993
3104
|
|
|
2994
3105
|
if (isLastVisible) {
|
|
2995
3106
|
return (
|
|
@@ -3001,21 +3112,21 @@ const TagGroup = ({
|
|
|
3001
3112
|
+{overflowCount} more
|
|
3002
3113
|
</Tag>
|
|
3003
3114
|
</div>
|
|
3004
|
-
)
|
|
3115
|
+
);
|
|
3005
3116
|
}
|
|
3006
3117
|
|
|
3007
3118
|
return (
|
|
3008
3119
|
<Tag key={index} label={tag.label} variant={variant} size={size}>
|
|
3009
3120
|
{tag.value}
|
|
3010
3121
|
</Tag>
|
|
3011
|
-
)
|
|
3122
|
+
);
|
|
3012
3123
|
})}
|
|
3013
3124
|
</div>
|
|
3014
|
-
)
|
|
3015
|
-
}
|
|
3016
|
-
TagGroup.displayName = "TagGroup"
|
|
3125
|
+
);
|
|
3126
|
+
};
|
|
3127
|
+
TagGroup.displayName = "TagGroup";
|
|
3017
3128
|
|
|
3018
|
-
export { Tag, TagGroup, tagVariants }
|
|
3129
|
+
export { Tag, TagGroup, tagVariants };
|
|
3019
3130
|
`, prefix)
|
|
3020
3131
|
}
|
|
3021
3132
|
]
|
|
@@ -3032,11 +3143,11 @@ export { Tag, TagGroup, tagVariants }
|
|
|
3032
3143
|
files: [
|
|
3033
3144
|
{
|
|
3034
3145
|
name: "accordion.tsx",
|
|
3035
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
3036
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3037
|
-
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";
|
|
3038
3149
|
|
|
3039
|
-
import { cn } from "../../lib/utils"
|
|
3150
|
+
import { cn } from "../../lib/utils";
|
|
3040
3151
|
|
|
3041
3152
|
/**
|
|
3042
3153
|
* Accordion root variants
|
|
@@ -3051,7 +3162,7 @@ const accordionVariants = cva("w-full", {
|
|
|
3051
3162
|
defaultVariants: {
|
|
3052
3163
|
variant: "default",
|
|
3053
3164
|
},
|
|
3054
|
-
})
|
|
3165
|
+
});
|
|
3055
3166
|
|
|
3056
3167
|
/**
|
|
3057
3168
|
* Accordion item variants
|
|
@@ -3066,7 +3177,7 @@ const accordionItemVariants = cva("", {
|
|
|
3066
3177
|
defaultVariants: {
|
|
3067
3178
|
variant: "default",
|
|
3068
3179
|
},
|
|
3069
|
-
})
|
|
3180
|
+
});
|
|
3070
3181
|
|
|
3071
3182
|
/**
|
|
3072
3183
|
* Accordion trigger variants
|
|
@@ -3084,7 +3195,7 @@ const accordionTriggerVariants = cva(
|
|
|
3084
3195
|
variant: "default",
|
|
3085
3196
|
},
|
|
3086
3197
|
}
|
|
3087
|
-
)
|
|
3198
|
+
);
|
|
3088
3199
|
|
|
3089
3200
|
/**
|
|
3090
3201
|
* Accordion content variants
|
|
@@ -3102,58 +3213,64 @@ const accordionContentVariants = cva(
|
|
|
3102
3213
|
variant: "default",
|
|
3103
3214
|
},
|
|
3104
3215
|
}
|
|
3105
|
-
)
|
|
3216
|
+
);
|
|
3106
3217
|
|
|
3107
3218
|
// Types
|
|
3108
|
-
type AccordionType = "single" | "multiple"
|
|
3219
|
+
type AccordionType = "single" | "multiple";
|
|
3109
3220
|
|
|
3110
3221
|
interface AccordionContextValue {
|
|
3111
|
-
type: AccordionType
|
|
3112
|
-
value: string[]
|
|
3113
|
-
onValueChange: (value: string[]) => void
|
|
3114
|
-
variant: "default" | "bordered"
|
|
3222
|
+
type: AccordionType;
|
|
3223
|
+
value: string[];
|
|
3224
|
+
onValueChange: (value: string[]) => void;
|
|
3225
|
+
variant: "default" | "bordered";
|
|
3115
3226
|
}
|
|
3116
3227
|
|
|
3117
3228
|
interface AccordionItemContextValue {
|
|
3118
|
-
value: string
|
|
3119
|
-
isOpen: boolean
|
|
3120
|
-
disabled?: boolean
|
|
3229
|
+
value: string;
|
|
3230
|
+
isOpen: boolean;
|
|
3231
|
+
disabled?: boolean;
|
|
3121
3232
|
}
|
|
3122
3233
|
|
|
3123
3234
|
// Contexts
|
|
3124
|
-
const AccordionContext = React.createContext<AccordionContextValue | null>(
|
|
3125
|
-
|
|
3235
|
+
const AccordionContext = React.createContext<AccordionContextValue | null>(
|
|
3236
|
+
null
|
|
3237
|
+
);
|
|
3238
|
+
const AccordionItemContext =
|
|
3239
|
+
React.createContext<AccordionItemContextValue | null>(null);
|
|
3126
3240
|
|
|
3127
3241
|
function useAccordionContext() {
|
|
3128
|
-
const context = React.useContext(AccordionContext)
|
|
3242
|
+
const context = React.useContext(AccordionContext);
|
|
3129
3243
|
if (!context) {
|
|
3130
|
-
throw new Error("Accordion components must be used within an Accordion")
|
|
3244
|
+
throw new Error("Accordion components must be used within an Accordion");
|
|
3131
3245
|
}
|
|
3132
|
-
return context
|
|
3246
|
+
return context;
|
|
3133
3247
|
}
|
|
3134
3248
|
|
|
3135
3249
|
function useAccordionItemContext() {
|
|
3136
|
-
const context = React.useContext(AccordionItemContext)
|
|
3250
|
+
const context = React.useContext(AccordionItemContext);
|
|
3137
3251
|
if (!context) {
|
|
3138
|
-
throw new Error(
|
|
3252
|
+
throw new Error(
|
|
3253
|
+
"AccordionTrigger/AccordionContent must be used within an AccordionItem"
|
|
3254
|
+
);
|
|
3139
3255
|
}
|
|
3140
|
-
return context
|
|
3256
|
+
return context;
|
|
3141
3257
|
}
|
|
3142
3258
|
|
|
3143
3259
|
/**
|
|
3144
3260
|
* Root accordion component that manages state
|
|
3145
3261
|
*/
|
|
3146
3262
|
export interface AccordionProps
|
|
3147
|
-
extends
|
|
3263
|
+
extends
|
|
3264
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
3148
3265
|
VariantProps<typeof accordionVariants> {
|
|
3149
3266
|
/** Whether only one item can be open at a time ('single') or multiple ('multiple') */
|
|
3150
|
-
type?: AccordionType
|
|
3267
|
+
type?: AccordionType;
|
|
3151
3268
|
/** Controlled value - array of open item values */
|
|
3152
|
-
value?: string[]
|
|
3269
|
+
value?: string[];
|
|
3153
3270
|
/** Default open items for uncontrolled usage */
|
|
3154
|
-
defaultValue?: string[]
|
|
3271
|
+
defaultValue?: string[];
|
|
3155
3272
|
/** Callback when open items change */
|
|
3156
|
-
onValueChange?: (value: string[]) => void
|
|
3273
|
+
onValueChange?: (value: string[]) => void;
|
|
3157
3274
|
}
|
|
3158
3275
|
|
|
3159
3276
|
const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
@@ -3170,20 +3287,21 @@ const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
|
3170
3287
|
},
|
|
3171
3288
|
ref
|
|
3172
3289
|
) => {
|
|
3173
|
-
const [internalValue, setInternalValue] =
|
|
3290
|
+
const [internalValue, setInternalValue] =
|
|
3291
|
+
React.useState<string[]>(defaultValue);
|
|
3174
3292
|
|
|
3175
|
-
const isControlled = controlledValue !== undefined
|
|
3176
|
-
const currentValue = isControlled ? controlledValue : internalValue
|
|
3293
|
+
const isControlled = controlledValue !== undefined;
|
|
3294
|
+
const currentValue = isControlled ? controlledValue : internalValue;
|
|
3177
3295
|
|
|
3178
3296
|
const handleValueChange = React.useCallback(
|
|
3179
3297
|
(newValue: string[]) => {
|
|
3180
3298
|
if (!isControlled) {
|
|
3181
|
-
setInternalValue(newValue)
|
|
3299
|
+
setInternalValue(newValue);
|
|
3182
3300
|
}
|
|
3183
|
-
onValueChange?.(newValue)
|
|
3301
|
+
onValueChange?.(newValue);
|
|
3184
3302
|
},
|
|
3185
3303
|
[isControlled, onValueChange]
|
|
3186
|
-
)
|
|
3304
|
+
);
|
|
3187
3305
|
|
|
3188
3306
|
const contextValue = React.useMemo(
|
|
3189
3307
|
() => ({
|
|
@@ -3193,7 +3311,7 @@ const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
|
3193
3311
|
variant: variant || "default",
|
|
3194
3312
|
}),
|
|
3195
3313
|
[type, currentValue, handleValueChange, variant]
|
|
3196
|
-
)
|
|
3314
|
+
);
|
|
3197
3315
|
|
|
3198
3316
|
return (
|
|
3199
3317
|
<AccordionContext.Provider value={contextValue}>
|
|
@@ -3205,27 +3323,28 @@ const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
|
|
|
3205
3323
|
{children}
|
|
3206
3324
|
</div>
|
|
3207
3325
|
</AccordionContext.Provider>
|
|
3208
|
-
)
|
|
3326
|
+
);
|
|
3209
3327
|
}
|
|
3210
|
-
)
|
|
3211
|
-
Accordion.displayName = "Accordion"
|
|
3328
|
+
);
|
|
3329
|
+
Accordion.displayName = "Accordion";
|
|
3212
3330
|
|
|
3213
3331
|
/**
|
|
3214
3332
|
* Individual accordion item
|
|
3215
3333
|
*/
|
|
3216
3334
|
export interface AccordionItemProps
|
|
3217
|
-
extends
|
|
3335
|
+
extends
|
|
3336
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
3218
3337
|
VariantProps<typeof accordionItemVariants> {
|
|
3219
3338
|
/** Unique value for this item */
|
|
3220
|
-
value: string
|
|
3339
|
+
value: string;
|
|
3221
3340
|
/** Whether this item is disabled */
|
|
3222
|
-
disabled?: boolean
|
|
3341
|
+
disabled?: boolean;
|
|
3223
3342
|
}
|
|
3224
3343
|
|
|
3225
3344
|
const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
|
|
3226
3345
|
({ className, value, disabled, children, ...props }, ref) => {
|
|
3227
|
-
const { value: openValues, variant } = useAccordionContext()
|
|
3228
|
-
const isOpen = openValues.includes(value)
|
|
3346
|
+
const { value: openValues, variant } = useAccordionContext();
|
|
3347
|
+
const isOpen = openValues.includes(value);
|
|
3229
3348
|
|
|
3230
3349
|
const contextValue = React.useMemo(
|
|
3231
3350
|
() => ({
|
|
@@ -3234,7 +3353,7 @@ const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
|
|
|
3234
3353
|
disabled,
|
|
3235
3354
|
}),
|
|
3236
3355
|
[value, isOpen, disabled]
|
|
3237
|
-
)
|
|
3356
|
+
);
|
|
3238
3357
|
|
|
3239
3358
|
return (
|
|
3240
3359
|
<AccordionItemContext.Provider value={contextValue}>
|
|
@@ -3247,106 +3366,115 @@ const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
|
|
|
3247
3366
|
{children}
|
|
3248
3367
|
</div>
|
|
3249
3368
|
</AccordionItemContext.Provider>
|
|
3250
|
-
)
|
|
3369
|
+
);
|
|
3251
3370
|
}
|
|
3252
|
-
)
|
|
3253
|
-
AccordionItem.displayName = "AccordionItem"
|
|
3371
|
+
);
|
|
3372
|
+
AccordionItem.displayName = "AccordionItem";
|
|
3254
3373
|
|
|
3255
3374
|
/**
|
|
3256
3375
|
* Trigger button that toggles the accordion item
|
|
3257
3376
|
*/
|
|
3258
3377
|
export interface AccordionTriggerProps
|
|
3259
|
-
extends
|
|
3378
|
+
extends
|
|
3379
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
3260
3380
|
VariantProps<typeof accordionTriggerVariants> {
|
|
3261
3381
|
/** Whether to show the chevron icon */
|
|
3262
|
-
showChevron?: boolean
|
|
3382
|
+
showChevron?: boolean;
|
|
3263
3383
|
}
|
|
3264
3384
|
|
|
3265
|
-
const AccordionTrigger = React.forwardRef<
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
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();
|
|
3272
3396
|
|
|
3273
|
-
|
|
3397
|
+
const handleClick = () => {
|
|
3398
|
+
if (disabled) return;
|
|
3274
3399
|
|
|
3275
|
-
|
|
3276
|
-
// In single mode, toggle current item (close if open, open if closed)
|
|
3277
|
-
newValue = isOpen ? [] : [value]
|
|
3278
|
-
} else {
|
|
3279
|
-
// In multiple mode, toggle the item in the array
|
|
3280
|
-
newValue = isOpen
|
|
3281
|
-
? openValues.filter((v) => v !== value)
|
|
3282
|
-
: [...openValues, value]
|
|
3283
|
-
}
|
|
3400
|
+
let newValue: string[];
|
|
3284
3401
|
|
|
3285
|
-
|
|
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];
|
|
3286
3410
|
}
|
|
3287
3411
|
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
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";
|
|
3312
3438
|
|
|
3313
3439
|
/**
|
|
3314
3440
|
* Content that is shown/hidden when the item is toggled
|
|
3315
3441
|
*/
|
|
3316
3442
|
export interface AccordionContentProps
|
|
3317
|
-
extends
|
|
3443
|
+
extends
|
|
3444
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
3318
3445
|
VariantProps<typeof accordionContentVariants> {}
|
|
3319
3446
|
|
|
3320
|
-
const AccordionContent = React.forwardRef<
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
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]);
|
|
3333
3462
|
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
</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}
|
|
3345
3473
|
</div>
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
)
|
|
3349
|
-
AccordionContent.displayName = "AccordionContent"
|
|
3474
|
+
</div>
|
|
3475
|
+
);
|
|
3476
|
+
});
|
|
3477
|
+
AccordionContent.displayName = "AccordionContent";
|
|
3350
3478
|
|
|
3351
3479
|
export {
|
|
3352
3480
|
Accordion,
|
|
@@ -3357,7 +3485,7 @@ export {
|
|
|
3357
3485
|
accordionItemVariants,
|
|
3358
3486
|
accordionTriggerVariants,
|
|
3359
3487
|
accordionContentVariants,
|
|
3360
|
-
}
|
|
3488
|
+
};
|
|
3361
3489
|
`, prefix)
|
|
3362
3490
|
}
|
|
3363
3491
|
]
|
|
@@ -3374,12 +3502,12 @@ export {
|
|
|
3374
3502
|
files: [
|
|
3375
3503
|
{
|
|
3376
3504
|
name: "page-header.tsx",
|
|
3377
|
-
content: prefixTailwindClasses(`import * as React from "react"
|
|
3378
|
-
import { cva, type VariantProps } from "class-variance-authority"
|
|
3379
|
-
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";
|
|
3380
3508
|
|
|
3381
|
-
import { cn } from "../../lib/utils"
|
|
3382
|
-
import { Button } from "./button"
|
|
3509
|
+
import { cn } from "../../lib/utils";
|
|
3510
|
+
import { Button } from "./button";
|
|
3383
3511
|
|
|
3384
3512
|
/**
|
|
3385
3513
|
* PageHeader variants for layout styles.
|
|
@@ -3390,7 +3518,7 @@ const pageHeaderVariants = cva(
|
|
|
3390
3518
|
variants: {},
|
|
3391
3519
|
defaultVariants: {},
|
|
3392
3520
|
}
|
|
3393
|
-
)
|
|
3521
|
+
);
|
|
3394
3522
|
|
|
3395
3523
|
/**
|
|
3396
3524
|
* Page header component for displaying page titles with optional icons and actions.
|
|
@@ -3423,47 +3551,51 @@ const pageHeaderVariants = cva(
|
|
|
3423
3551
|
* \`\`\`
|
|
3424
3552
|
*/
|
|
3425
3553
|
export interface PageHeaderProps
|
|
3426
|
-
extends
|
|
3554
|
+
extends
|
|
3555
|
+
React.HTMLAttributes<HTMLDivElement>,
|
|
3427
3556
|
VariantProps<typeof pageHeaderVariants> {
|
|
3428
3557
|
/** Page title (required) */
|
|
3429
|
-
title: string
|
|
3558
|
+
title: string;
|
|
3430
3559
|
/** Optional description/subtitle displayed below the title */
|
|
3431
|
-
description?: string
|
|
3560
|
+
description?: string;
|
|
3432
3561
|
/** Icon displayed on the left side (hidden when showBackButton is true) */
|
|
3433
|
-
icon?: React.ReactNode
|
|
3562
|
+
icon?: React.ReactNode;
|
|
3434
3563
|
/** Shows back arrow button instead of icon */
|
|
3435
|
-
showBackButton?: boolean
|
|
3564
|
+
showBackButton?: boolean;
|
|
3436
3565
|
/** Callback when back button is clicked */
|
|
3437
|
-
onBackClick?: () => void
|
|
3566
|
+
onBackClick?: () => void;
|
|
3438
3567
|
/** Optional info icon displayed next to the title (e.g., tooltip trigger) */
|
|
3439
|
-
infoIcon?: React.ReactNode
|
|
3568
|
+
infoIcon?: React.ReactNode;
|
|
3440
3569
|
/** Action buttons/elements rendered on the right side */
|
|
3441
|
-
actions?: React.ReactNode
|
|
3570
|
+
actions?: React.ReactNode;
|
|
3442
3571
|
/** Show bottom border (default: true) */
|
|
3443
|
-
showBorder?: boolean
|
|
3572
|
+
showBorder?: boolean;
|
|
3444
3573
|
/** Layout mode: 'horizontal' (single row), 'vertical' (stacked), 'responsive' (auto based on screen size, default) */
|
|
3445
|
-
layout?:
|
|
3574
|
+
layout?: "horizontal" | "vertical" | "responsive";
|
|
3446
3575
|
/** Max actions to show on mobile before overflow (default: 2) */
|
|
3447
|
-
mobileOverflowLimit?: number
|
|
3576
|
+
mobileOverflowLimit?: number;
|
|
3448
3577
|
}
|
|
3449
3578
|
|
|
3450
3579
|
const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
3451
|
-
(
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
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
|
+
) => {
|
|
3465
3597
|
// State for overflow expansion (moved to top level)
|
|
3466
|
-
const [isOverflowExpanded, setIsOverflowExpanded] = React.useState(false)
|
|
3598
|
+
const [isOverflowExpanded, setIsOverflowExpanded] = React.useState(false);
|
|
3467
3599
|
|
|
3468
3600
|
// Determine what to show on the left: back button, icon, or nothing
|
|
3469
3601
|
const renderLeftElement = () => {
|
|
@@ -3477,66 +3609,68 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3477
3609
|
>
|
|
3478
3610
|
<ArrowLeft className="w-5 h-5" />
|
|
3479
3611
|
</button>
|
|
3480
|
-
)
|
|
3612
|
+
);
|
|
3481
3613
|
}
|
|
3482
3614
|
if (icon) {
|
|
3483
3615
|
return (
|
|
3484
3616
|
<div className="flex items-center justify-center w-10 h-10 [&_svg]:w-6 [&_svg]:h-6 text-[#717680]">
|
|
3485
3617
|
{icon}
|
|
3486
3618
|
</div>
|
|
3487
|
-
)
|
|
3619
|
+
);
|
|
3488
3620
|
}
|
|
3489
|
-
return null
|
|
3490
|
-
}
|
|
3621
|
+
return null;
|
|
3622
|
+
};
|
|
3491
3623
|
|
|
3492
|
-
const leftElement = renderLeftElement()
|
|
3624
|
+
const leftElement = renderLeftElement();
|
|
3493
3625
|
|
|
3494
3626
|
// Flatten children recursively to handle fragments
|
|
3495
3627
|
const flattenChildren = (children: React.ReactNode): React.ReactNode[] => {
|
|
3496
|
-
const result: React.ReactNode[] = []
|
|
3628
|
+
const result: React.ReactNode[] = [];
|
|
3497
3629
|
React.Children.forEach(children, (child) => {
|
|
3498
3630
|
if (React.isValidElement(child) && child.type === React.Fragment) {
|
|
3499
|
-
result.push(...flattenChildren(child.props.children))
|
|
3631
|
+
result.push(...flattenChildren(child.props.children));
|
|
3500
3632
|
} else if (child !== null && child !== undefined) {
|
|
3501
|
-
result.push(child)
|
|
3633
|
+
result.push(child);
|
|
3502
3634
|
}
|
|
3503
|
-
})
|
|
3504
|
-
return result
|
|
3505
|
-
}
|
|
3635
|
+
});
|
|
3636
|
+
return result;
|
|
3637
|
+
};
|
|
3506
3638
|
|
|
3507
3639
|
// Convert actions to array for overflow handling
|
|
3508
|
-
const actionsArray = flattenChildren(actions)
|
|
3509
|
-
const hasOverflow = actionsArray.length > mobileOverflowLimit
|
|
3510
|
-
const visibleActions = hasOverflow
|
|
3511
|
-
|
|
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
|
+
: [];
|
|
3512
3648
|
|
|
3513
3649
|
// Layout classes based on prop
|
|
3514
3650
|
const layoutClasses = {
|
|
3515
|
-
horizontal:
|
|
3516
|
-
vertical:
|
|
3517
|
-
responsive:
|
|
3518
|
-
}
|
|
3651
|
+
horizontal: "flex-row items-center",
|
|
3652
|
+
vertical: "flex-col",
|
|
3653
|
+
responsive: "flex-col sm:flex-row sm:items-center",
|
|
3654
|
+
};
|
|
3519
3655
|
|
|
3520
3656
|
const heightClasses = {
|
|
3521
|
-
horizontal:
|
|
3522
|
-
vertical:
|
|
3523
|
-
responsive:
|
|
3524
|
-
}
|
|
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
|
+
};
|
|
3525
3661
|
|
|
3526
3662
|
// Render actions for desktop (all inline)
|
|
3527
3663
|
const renderDesktopActions = () => (
|
|
3528
|
-
<div className="hidden sm:flex items-center gap-2">
|
|
3529
|
-
|
|
3530
|
-
</div>
|
|
3531
|
-
)
|
|
3664
|
+
<div className="hidden sm:flex items-center gap-2">{actionsArray}</div>
|
|
3665
|
+
);
|
|
3532
3666
|
|
|
3533
3667
|
// Render expandable actions (for mobile and vertical layout)
|
|
3534
3668
|
const renderExpandableActions = (additionalClasses?: string) => {
|
|
3535
3669
|
// Calculate grid columns: equal width for visible actions, smaller for overflow button
|
|
3536
|
-
const hasOverflowBtn = overflowActions.length > 0
|
|
3670
|
+
const hasOverflowBtn = overflowActions.length > 0;
|
|
3537
3671
|
const gridCols = hasOverflowBtn
|
|
3538
3672
|
? \`repeat(\${visibleActions.length}, 1fr) auto\`
|
|
3539
|
-
: \`repeat(\${visibleActions.length}, 1fr)
|
|
3673
|
+
: \`repeat(\${visibleActions.length}, 1fr)\`;
|
|
3540
3674
|
|
|
3541
3675
|
return (
|
|
3542
3676
|
<div className={cn("flex flex-col gap-2 w-full", additionalClasses)}>
|
|
@@ -3556,7 +3690,11 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3556
3690
|
aria-label={isOverflowExpanded ? "Show less" : "More actions"}
|
|
3557
3691
|
aria-expanded={isOverflowExpanded}
|
|
3558
3692
|
>
|
|
3559
|
-
{isOverflowExpanded ?
|
|
3693
|
+
{isOverflowExpanded ? (
|
|
3694
|
+
<X className="w-4 h-4" />
|
|
3695
|
+
) : (
|
|
3696
|
+
<MoreHorizontal className="w-4 h-4" />
|
|
3697
|
+
)}
|
|
3560
3698
|
</Button>
|
|
3561
3699
|
)}
|
|
3562
3700
|
</div>
|
|
@@ -3572,25 +3710,23 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3572
3710
|
</div>
|
|
3573
3711
|
)}
|
|
3574
3712
|
</div>
|
|
3575
|
-
)
|
|
3576
|
-
}
|
|
3713
|
+
);
|
|
3714
|
+
};
|
|
3577
3715
|
|
|
3578
3716
|
// For horizontal layout, always show all actions inline
|
|
3579
3717
|
const renderHorizontalActions = () => (
|
|
3580
|
-
<div className="flex items-center gap-2 ml-4">
|
|
3581
|
-
|
|
3582
|
-
</div>
|
|
3583
|
-
)
|
|
3718
|
+
<div className="flex items-center gap-2 ml-4">{actionsArray}</div>
|
|
3719
|
+
);
|
|
3584
3720
|
|
|
3585
3721
|
const renderActions = () => {
|
|
3586
|
-
if (!actions) return null
|
|
3722
|
+
if (!actions) return null;
|
|
3587
3723
|
|
|
3588
|
-
if (layout ===
|
|
3589
|
-
return renderHorizontalActions()
|
|
3724
|
+
if (layout === "horizontal") {
|
|
3725
|
+
return renderHorizontalActions();
|
|
3590
3726
|
}
|
|
3591
3727
|
|
|
3592
|
-
if (layout ===
|
|
3593
|
-
return renderExpandableActions("mt-3")
|
|
3728
|
+
if (layout === "vertical") {
|
|
3729
|
+
return renderExpandableActions("mt-3");
|
|
3594
3730
|
}
|
|
3595
3731
|
|
|
3596
3732
|
// Responsive: render both, CSS handles visibility
|
|
@@ -3601,8 +3737,8 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3601
3737
|
{renderExpandableActions()}
|
|
3602
3738
|
</div>
|
|
3603
3739
|
</>
|
|
3604
|
-
)
|
|
3605
|
-
}
|
|
3740
|
+
);
|
|
3741
|
+
};
|
|
3606
3742
|
|
|
3607
3743
|
return (
|
|
3608
3744
|
<div
|
|
@@ -3620,9 +3756,7 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3620
3756
|
<div className="flex items-center flex-1 min-w-0">
|
|
3621
3757
|
{/* Left Section: Icon or Back Button */}
|
|
3622
3758
|
{leftElement && (
|
|
3623
|
-
<div className="flex-shrink-0 mr-4">
|
|
3624
|
-
{leftElement}
|
|
3625
|
-
</div>
|
|
3759
|
+
<div className="flex-shrink-0 mr-4">{leftElement}</div>
|
|
3626
3760
|
)}
|
|
3627
3761
|
|
|
3628
3762
|
{/* Content Section: Title + Description */}
|
|
@@ -3648,12 +3782,12 @@ const PageHeader = React.forwardRef<HTMLDivElement, PageHeaderProps>(
|
|
|
3648
3782
|
{/* Actions Section */}
|
|
3649
3783
|
{renderActions()}
|
|
3650
3784
|
</div>
|
|
3651
|
-
)
|
|
3785
|
+
);
|
|
3652
3786
|
}
|
|
3653
|
-
)
|
|
3654
|
-
PageHeader.displayName = "PageHeader"
|
|
3787
|
+
);
|
|
3788
|
+
PageHeader.displayName = "PageHeader";
|
|
3655
3789
|
|
|
3656
|
-
export { PageHeader, pageHeaderVariants }
|
|
3790
|
+
export { PageHeader, pageHeaderVariants };
|
|
3657
3791
|
`, prefix)
|
|
3658
3792
|
}
|
|
3659
3793
|
]
|