drivn 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +924 -31
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import {Command}from'commander';import*as e from'@clack/prompts';import i from'picocolors';import {join,dirname}from'path';import {execSync}from'child_process';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';var M={next:"Next.js",react:"React"};function B(t){let n=join(t,"package.json");if(!existsSync(n))throw new Error("package.json not found");let c=JSON.parse(readFileSync(n,"utf-8")),v={...c.dependencies,...c.devDependencies},d="react";v.next&&(d="next");let m=existsSync(join(t,"src")),f=existsSync(join(t,"tsconfig.json"));return {framework:d,srcDir:m,typescript:f}}var H="drivn.config.json";function V(t){let n=join(t,H);return existsSync(n)?JSON.parse(readFileSync(n,"utf-8")):null}function z(t,n){let c=join(t,H);writeFileSync(c,JSON.stringify(n,null,2));}function he(t){existsSync(t)||mkdirSync(t,{recursive:true});}function p(t,n){he(dirname(t)),writeFileSync(t,n);}function F(t){return readFileSync(t,"utf-8")}function h(t){return existsSync(t)}function k(t){return existsSync(join(t,"pnpm-lock.yaml"))?"pnpm":"npm"}function y(t,n){let c=n.join(" ");return t==="pnpm"?`pnpm add ${c}`:`npm install ${c}`}function _(t){return t==="pnpm"?"pnpm dlx":"npx"}var T=`@import "tailwindcss";
2
+ import {Command}from'commander';import*as e from'@clack/prompts';import i from'picocolors';import {join,dirname}from'path';import {execSync}from'child_process';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';var A={next:"Next.js",react:"React"};function B(t){let n=join(t,"package.json");if(!existsSync(n))throw new Error("package.json not found");let a=JSON.parse(readFileSync(n,"utf-8")),v={...a.dependencies,...a.devDependencies},p="react";v.next&&(p="next");let g=existsSync(join(t,"src")),u=existsSync(join(t,"tsconfig.json"));return {framework:p,srcDir:g,typescript:u}}var $="drivn.config.json";function V(t){let n=join(t,$);return existsSync(n)?JSON.parse(readFileSync(n,"utf-8")):null}function _(t,n){let a=join(t,$);writeFileSync(a,JSON.stringify(n,null,2));}function Ne(t){existsSync(t)||mkdirSync(t,{recursive:true});}function d(t,n){Ne(dirname(t)),writeFileSync(t,n);}function O(t){return readFileSync(t,"utf-8")}function m(t){return existsSync(t)}function P(t){return existsSync(join(t,"pnpm-lock.yaml"))?"pnpm":"npm"}function y(t,n){let a=n.join(" ");return t==="pnpm"?`pnpm add ${a}`:`npm install ${a}`}function F(t){return t==="pnpm"?"pnpm dlx":"npx"}var D=`@import "tailwindcss";
3
3
 
4
4
  :root {
5
5
  /* Surfaces */
@@ -70,7 +70,7 @@ body {
70
70
  color: var(--color-foreground);
71
71
  font-family: system-ui, -apple-system, sans-serif;
72
72
  }
73
- `,L=`
73
+ `,M=`
74
74
  /* Drivn Dark/Light Theme */
75
75
  :root,
76
76
  [data-theme="dark"] {
@@ -136,13 +136,13 @@ body {
136
136
  /* Special Surfaces */
137
137
  --overlay: hsl(0 0% 0% / 0.18);
138
138
  }
139
- `;var ye=`import { type ClassValue, clsx } from 'clsx'
139
+ `;var ke=`import { type ClassValue, clsx } from 'clsx'
140
140
  import { twMerge } from 'tailwind-merge'
141
141
 
142
142
  export function cn(...inputs: ClassValue[]) {
143
143
  return twMerge(clsx(inputs))
144
144
  }
145
- `,Ne=["src/app/globals.css","src/styles/globals.css","src/styles/globals.scss","app/globals.css"];function we(t){for(let n of Ne)if(h(join(t,n)))return n;return null}async function U(){let t=process.cwd();console.log(""),console.log(i.bgCyan(i.bold(i.black(" Drivn "))));let n;try{n=B(t),e.log.success(`Detected ${i.cyan(M[n.framework])}`);}catch{e.log.error("No package.json found. Run this command in a project directory."),e.outro("Setup cancelled"),process.exit(1);}if(h(join(t,"drivn.config.json"))){let s=await e.confirm({message:"Config already exists. Overwrite?",initialValue:false});(e.isCancel(s)||!s)&&(e.cancel("Setup cancelled"),process.exit(0));}let c=n.srcDir?"src/components/ui":"components/ui",v=n.srcDir?"src/utils":"utils",d=await e.group({components:()=>e.text({message:"Where should components be installed?",placeholder:c,defaultValue:c}),utils:()=>e.text({message:"Where should utilities be placed?",placeholder:v,defaultValue:v})},{onCancel:()=>{e.cancel("Setup cancelled"),process.exit(0);}}),m={framework:n.framework,typescript:n.typescript,paths:{components:d.components,utils:d.utils},installed:[]},f=n.typescript?"ts":"js",b=join(t,d.utils,`cn.${f}`);h(b)||p(b,ye);let g=we(t);if(g){let s=await e.confirm({message:`Found ${i.cyan(g)}. Add Drivn color tokens?`,initialValue:true});!e.isCancel(s)&&s&&(p(join(t,g),T),m.paths.globals=g,e.log.success(`Color tokens written to ${i.cyan(g)}`));}else {let s=n.srcDir?"src/styles/globals.css":"styles/globals.css",a=await e.text({message:"Where should the globals CSS file be created?",placeholder:s,defaultValue:s});e.isCancel(a)||(p(join(t,a),T),m.paths.globals=a,e.log.success(`Color tokens written to ${i.cyan(a)}`));}z(t,m);let x=k(t),l=_(x),N=["clsx","tailwind-merge","lucide-react"],o=e.spinner();o.start("Installing dependencies");try{execSync(y(x,N),{cwd:t,stdio:"ignore"}),o.stop("Dependencies installed");}catch{o.stop("Failed to install dependencies"),e.log.warn(`Run manually: ${y(x,N)}`);}e.log.info(`Add components with: ${i.cyan(`${l} drivn add button`)}`),e.log.info(`Add dark/light theme: ${i.cyan(`${l} drivn add theme`)}`),e.outro("Drivn initialized");}var G=`'use client'
145
+ `,Pe=["src/app/globals.css","src/styles/globals.css","src/styles/globals.scss","app/globals.css"];function De(t){for(let n of Pe)if(m(join(t,n)))return n;return null}async function K(){let t=process.cwd();console.log(""),console.log(i.bgCyan(i.bold(i.black(" Drivn "))));let n;try{n=B(t),e.log.success(`Detected ${i.cyan(A[n.framework])}`);}catch{e.log.error("No package.json found. Run this command in a project directory."),e.outro("Setup cancelled"),process.exit(1);}if(m(join(t,"drivn.config.json"))){let r=await e.confirm({message:"Config already exists. Overwrite?",initialValue:false});(e.isCancel(r)||!r)&&(e.cancel("Setup cancelled"),process.exit(0));}let a=n.srcDir?"src/components/ui":"components/ui",v=n.srcDir?"src/utils":"utils",p=await e.group({components:()=>e.text({message:"Where should components be installed?",placeholder:a,defaultValue:a}),utils:()=>e.text({message:"Where should utilities be placed?",placeholder:v,defaultValue:v})},{onCancel:()=>{e.cancel("Setup cancelled"),process.exit(0);}}),g={framework:n.framework,typescript:n.typescript,paths:{components:p.components,utils:p.utils},installed:[]},u=n.typescript?"ts":"js",b=join(t,p.utils,`cn.${u}`);m(b)||d(b,ke);let h=De(t);if(h){let r=await e.confirm({message:`Found ${i.cyan(h)}. Add Drivn color tokens?`,initialValue:true});!e.isCancel(r)&&r&&(d(join(t,h),D),g.paths.globals=h,e.log.success(`Color tokens written to ${i.cyan(h)}`));}else {let r=n.srcDir?"src/styles/globals.css":"styles/globals.css",c=await e.text({message:"Where should the globals CSS file be created?",placeholder:r,defaultValue:r});e.isCancel(c)||(d(join(t,c),D),g.paths.globals=c,e.log.success(`Color tokens written to ${i.cyan(c)}`));}_(t,g);let x=P(t),l=F(x),N=["clsx","tailwind-merge","lucide-react"],o=e.spinner();o.start("Installing dependencies");try{execSync(y(x,N),{cwd:t,stdio:"ignore"}),o.stop("Dependencies installed");}catch{o.stop("Failed to install dependencies"),e.log.warn(`Run manually: ${y(x,N)}`);}e.log.info(`Add components with: ${i.cyan(`${l} drivn add button`)}`),e.log.info(`Add dark/light theme: ${i.cyan(`${l} drivn add theme`)}`),e.outro("Drivn initialized");}var Y=`'use client'
146
146
 
147
147
  import { ThemeProvider as NextThemesProvider } from 'next-themes'
148
148
 
@@ -161,7 +161,7 @@ export function ThemeProvider({
161
161
  </NextThemesProvider>
162
162
  )
163
163
  }
164
- `;var W=`'use client'
164
+ `;var U=`'use client'
165
165
 
166
166
  import * as React from 'react'
167
167
  import { ChevronDown } from 'lucide-react'
@@ -289,9 +289,11 @@ function useCtx() {
289
289
  }
290
290
 
291
291
  export const Accordion = Object.assign(AccordionRoot, {
292
- Item, Trigger, Content,
292
+ Item,
293
+ Trigger,
294
+ Content
293
295
  })
294
- `;var X=`import * as React from 'react'
296
+ `;var G=`import * as React from 'react'
295
297
  import { cn } from '@/utils/cn'
296
298
 
297
299
  const styles = {
@@ -339,7 +341,7 @@ export function Alert({
339
341
  </div>
340
342
  )
341
343
  }
342
- `;var q=`import * as React from 'react'
344
+ `;var W=`import * as React from 'react'
343
345
  import { cn } from '@/utils/cn'
344
346
 
345
347
  const styles = {
@@ -383,7 +385,7 @@ export function Avatar({
383
385
  </div>
384
386
  )
385
387
  }
386
- `;var J=`import * as React from 'react'
388
+ `;var X=`import * as React from 'react'
387
389
  import { cn } from '@/utils/cn'
388
390
 
389
391
  const styles = {
@@ -413,7 +415,7 @@ export function Badge({ variant = 'default', className, children }: BadgeProps)
413
415
  </span>
414
416
  )
415
417
  }
416
- `;var K=`import * as React from 'react'
418
+ `;var q=`import * as React from 'react'
417
419
  import { Loader2 } from 'lucide-react'
418
420
  import { cn } from '@/utils/cn'
419
421
 
@@ -493,7 +495,887 @@ export const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(({
493
495
  )
494
496
 
495
497
  Button.displayName = 'Button'
496
- `;var Q=`import * as React from 'react'
498
+ `;var J=`'use client'
499
+
500
+ import * as React from 'react'
501
+ import {
502
+ DayPicker,
503
+ type DateRange,
504
+ type Locale,
505
+ type PropsBase,
506
+ type PropsSingle,
507
+ type PropsRange,
508
+ } from 'react-day-picker'
509
+ import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react'
510
+ import { cn } from '@/utils/cn'
511
+
512
+ const styles = {
513
+ root: 'p-3 bg-card border border-border rounded-[10px] text-sm',
514
+ classNames: {
515
+ months: 'relative flex gap-4',
516
+ month: 'flex flex-col gap-4',
517
+ month_caption: 'flex items-center justify-center h-7',
518
+ caption_label: 'text-sm font-medium text-foreground',
519
+ nav: 'absolute inset-x-0 top-0 flex items-center justify-between h-7',
520
+ button_previous: cn(
521
+ 'inline-flex items-center justify-center',
522
+ 'w-7 h-7 rounded-md text-muted-foreground',
523
+ 'hover:text-foreground hover:bg-accent',
524
+ 'transition-colors cursor-pointer'
525
+ ),
526
+ button_next: cn(
527
+ 'inline-flex items-center justify-center',
528
+ 'w-7 h-7 rounded-md text-muted-foreground',
529
+ 'hover:text-foreground hover:bg-accent',
530
+ 'transition-colors cursor-pointer'
531
+ ),
532
+ month_grid: 'border-collapse border-spacing-0',
533
+ weekdays: '',
534
+ weekday: cn(
535
+ 'w-8 pb-2 text-[0.8rem] font-medium',
536
+ 'text-muted-foreground text-center'
537
+ ),
538
+ week: '',
539
+ day: 'p-0 text-center',
540
+ day_button: cn(
541
+ 'inline-flex items-center justify-center',
542
+ 'w-8 h-8 rounded-md text-sm cursor-pointer',
543
+ 'hover:bg-accent hover:text-accent-foreground',
544
+ 'focus-visible:outline-none'
545
+ ),
546
+ today: 'font-semibold text-primary',
547
+ selected: cn(
548
+ '[&>button]:bg-primary',
549
+ '[&>button]:text-primary-foreground',
550
+ '[&>button]:hover:bg-primary',
551
+ '[&>button]:hover:text-primary-foreground'
552
+ ),
553
+ outside: 'text-muted-foreground/40',
554
+ disabled: 'text-muted-foreground/30 cursor-not-allowed',
555
+ range_middle: cn(
556
+ 'bg-accent text-accent-foreground',
557
+ 'hover:bg-accent hover:text-accent-foreground',
558
+ 'rounded-none [&>button]:rounded-none'
559
+ ),
560
+ chevron: 'w-4 h-4',
561
+ dropdown: cn(
562
+ 'appearance-none bg-transparent text-sm',
563
+ 'font-medium text-foreground cursor-pointer',
564
+ 'border-none outline-none'
565
+ ),
566
+ week_number: cn(
567
+ 'w-8 text-[0.75rem]',
568
+ 'text-muted-foreground/50 text-center font-normal'
569
+ ),
570
+ hidden: 'invisible',
571
+ },
572
+ dropdownOverrides: {
573
+ dropdowns: 'flex items-center gap-2 justify-center',
574
+ dropdown_root: 'relative inline-flex items-center',
575
+ dropdown: 'absolute inset-0 opacity-0 cursor-pointer w-full z-10',
576
+ caption_label: cn(
577
+ 'inline-flex items-center text-sm',
578
+ 'font-medium text-foreground',
579
+ 'pointer-events-none whitespace-nowrap'
580
+ ),
581
+ chevron: 'w-3 h-3 ml-0.5 text-muted-foreground',
582
+ },
583
+ rangeActive: {
584
+ range_start: cn(
585
+ 'bg-primary text-primary-foreground',
586
+ 'rounded-l-md [&>button]:rounded-r-none'
587
+ ),
588
+ range_end: cn(
589
+ 'bg-primary text-primary-foreground',
590
+ 'rounded-r-md [&>button]:rounded-l-none'
591
+ ),
592
+ },
593
+ }
594
+
595
+ const variantConfig = {
596
+ default: {},
597
+ weekNumbers: { showWeekNumber: true },
598
+ dropdown: { captionLayout: 'dropdown' as const },
599
+ }
600
+
601
+ export type CalendarVariant = keyof typeof variantConfig
602
+
603
+ type CalendarProps = Omit<PropsBase, 'mode'> & Omit<PropsSingle, 'mode'> & { variant?: CalendarVariant; fromYear?: number; toYear?: number }
604
+
605
+ type RangeProps = Omit<PropsBase, 'mode'> & Omit<PropsRange, 'mode'>
606
+
607
+ function Chevron(props: { orientation?: 'left' | 'right' | 'up' | 'down'; size?: number; disabled?: boolean; className?: string }) {
608
+ const icons = { left: ChevronLeft, right: ChevronRight, down: ChevronDown, up: ChevronRight }
609
+ const Icon = icons[props.orientation ?? 'right']
610
+ return <Icon className={props.className} />
611
+ }
612
+
613
+ const currentYear = new Date().getFullYear()
614
+
615
+ function CalendarRoot({
616
+ variant = 'default',
617
+ fromYear = currentYear - 20,
618
+ toYear = currentYear + 20,
619
+ className,
620
+ classNames: userClassNames,
621
+ ...props
622
+ }: CalendarProps) {
623
+ const dropdownRange = variant === 'dropdown' ? {
624
+ startMonth: new Date(fromYear, 0),
625
+ endMonth: new Date(toYear, 11),
626
+ } : {}
627
+
628
+ return (
629
+ <DayPicker
630
+ mode="single"
631
+ {...variantConfig[variant]}
632
+ {...dropdownRange}
633
+ classNames={{
634
+ ...styles.classNames,
635
+ ...(variant === 'dropdown' && styles.dropdownOverrides),
636
+ ...userClassNames,
637
+ }}
638
+ components={{ Chevron }}
639
+ className={cn(styles.root, className)}
640
+ {...props}
641
+ />
642
+ )
643
+ }
644
+
645
+ function Range({
646
+ className,
647
+ classNames: userClassNames,
648
+ selected,
649
+ ...props
650
+ }: RangeProps) {
651
+ const hasRange = selected?.from && selected?.to && selected.from.getTime() !== selected.to.getTime()
652
+
653
+ return (
654
+ <DayPicker
655
+ mode="range"
656
+ selected={selected}
657
+ classNames={{
658
+ ...styles.classNames,
659
+ ...(hasRange && styles.rangeActive),
660
+ ...userClassNames,
661
+ }}
662
+ components={{ Chevron }}
663
+ className={cn(styles.root, className)}
664
+ {...props}
665
+ />
666
+ )
667
+ }
668
+
669
+ export { type DateRange, type Locale }
670
+
671
+ export const Calendar = Object.assign(CalendarRoot, {
672
+ Range
673
+ })
674
+ `;var Q=`'use client'
675
+
676
+ import * as React from 'react'
677
+ import { CalendarDays } from 'lucide-react'
678
+ import { cn } from '@/utils/cn'
679
+ import {
680
+ Calendar,
681
+ type CalendarVariant,
682
+ type DateRange,
683
+ type Locale,
684
+ } from '@/components/ui/calendar'
685
+
686
+ const styles = {
687
+ base: 'relative',
688
+ trigger: cn(
689
+ 'flex items-center gap-2 w-full h-10 px-3',
690
+ 'border border-input rounded-[10px] text-sm',
691
+ 'text-foreground transition-colors cursor-pointer',
692
+ 'focus:outline-none',
693
+ 'disabled:opacity-50 disabled:cursor-default'
694
+ ),
695
+ placeholder: 'text-muted-foreground',
696
+ icon: 'w-4 h-4 text-muted-foreground shrink-0',
697
+ text: 'flex-1 truncate text-left',
698
+ content: cn(
699
+ 'absolute top-full left-0 mt-1 z-50',
700
+ 'transition-[opacity,scale] duration-150 ease-out'
701
+ ),
702
+ }
703
+
704
+ type FormatDateFn = (date: Date) => string
705
+
706
+ interface DatePickerBaseProps {
707
+ placeholder?: string
708
+ formatDate?: FormatDateFn
709
+ disabled?: boolean
710
+ locale?: Partial<Locale>
711
+ defaultMonth?: Date
712
+ className?: string
713
+ }
714
+
715
+ interface DatePickerProps extends DatePickerBaseProps {
716
+ selected?: Date
717
+ onSelect?: (date: Date | undefined) => void
718
+ variant?: CalendarVariant
719
+ fromYear?: number
720
+ toYear?: number
721
+ }
722
+
723
+ interface DatePickerRangeProps extends DatePickerBaseProps {
724
+ selected?: DateRange
725
+ onSelect?: (range: DateRange | undefined) => void
726
+ }
727
+
728
+ function formatRangeLabel(
729
+ range: DateRange,
730
+ fmt: FormatDateFn
731
+ ): string {
732
+ if (!range.from) return ''
733
+ if (!range.to) return fmt(range.from)
734
+ return \\\`\\\${fmt(range.from)} \u2013 \\\${fmt(range.to)}\\\`
735
+ }
736
+
737
+ function DatePickerRoot({
738
+ selected,
739
+ onSelect,
740
+ placeholder = 'Pick a date',
741
+ formatDate,
742
+ disabled = false,
743
+ locale,
744
+ variant = 'default',
745
+ fromYear,
746
+ toYear,
747
+ defaultMonth,
748
+ className,
749
+ }: DatePickerProps) {
750
+ const fmt = formatDate ?? ((d: Date) =>
751
+ d.toLocaleDateString(locale?.code ?? 'en-US', { month: 'short', day: 'numeric', year: 'numeric' }))
752
+ const [open, setOpen] = React.useState(false)
753
+ const ref = React.useRef<HTMLDivElement>(null)
754
+
755
+ React.useEffect(() => {
756
+ if (!open) return
757
+ const onMouseDown = (e: MouseEvent) => {
758
+ if (!ref.current?.contains(e.target as Node)) {
759
+ setOpen(false)
760
+ }
761
+ }
762
+ document.addEventListener('mousedown', onMouseDown)
763
+ return () => {
764
+ document.removeEventListener('mousedown', onMouseDown)
765
+ }
766
+ }, [open])
767
+
768
+ React.useEffect(() => {
769
+ if (!open) return
770
+ const onKeyDown = (e: KeyboardEvent) => {
771
+ if (e.key === 'Escape') setOpen(false)
772
+ }
773
+ document.addEventListener('keydown', onKeyDown)
774
+ return () => {
775
+ document.removeEventListener('keydown', onKeyDown)
776
+ }
777
+ }, [open])
778
+
779
+ const handleSelect = React.useCallback(
780
+ (date: Date | undefined) => {
781
+ onSelect?.(date)
782
+ setOpen(false)
783
+ },
784
+ [onSelect]
785
+ )
786
+
787
+ return (
788
+ <div ref={ref} className={cn(styles.base, className)}>
789
+ <button
790
+ type="button"
791
+ className={styles.trigger}
792
+ disabled={disabled}
793
+ onClick={() => setOpen((v) => !v)}
794
+ >
795
+ <CalendarDays className={styles.icon} />
796
+ <span className={cn(styles.text, !selected && styles.placeholder)}>
797
+ {selected ? fmt(selected) : placeholder}
798
+ </span>
799
+ </button>
800
+ <div className={cn(styles.content, open ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none')}>
801
+ <Calendar
802
+ variant={variant}
803
+ locale={locale}
804
+ selected={selected}
805
+ onSelect={handleSelect}
806
+ defaultMonth={defaultMonth ?? selected}
807
+ {...(fromYear !== undefined && { fromYear })}
808
+ {...(toYear !== undefined && { toYear })}
809
+ />
810
+ </div>
811
+ </div>
812
+ )
813
+ }
814
+
815
+ function Range({
816
+ selected,
817
+ onSelect,
818
+ placeholder = 'Pick a date range',
819
+ formatDate,
820
+ disabled = false,
821
+ locale,
822
+ defaultMonth,
823
+ className,
824
+ }: DatePickerRangeProps) {
825
+ const fmt = formatDate ?? ((d: Date) =>
826
+ d.toLocaleDateString(locale?.code ?? 'en-US', { month: 'short', day: 'numeric', year: 'numeric' }))
827
+ const [open, setOpen] = React.useState(false)
828
+ const ref = React.useRef<HTMLDivElement>(null)
829
+
830
+ React.useEffect(() => {
831
+ if (!open) return
832
+ const onMouseDown = (e: MouseEvent) => {
833
+ if (!ref.current?.contains(e.target as Node)) {
834
+ setOpen(false)
835
+ }
836
+ }
837
+ document.addEventListener('mousedown', onMouseDown)
838
+ return () => {
839
+ document.removeEventListener('mousedown', onMouseDown)
840
+ }
841
+ }, [open])
842
+
843
+ React.useEffect(() => {
844
+ if (!open) return
845
+ const onKeyDown = (e: KeyboardEvent) => {
846
+ if (e.key === 'Escape') setOpen(false)
847
+ }
848
+ document.addEventListener('keydown', onKeyDown)
849
+ return () => {
850
+ document.removeEventListener('keydown', onKeyDown)
851
+ }
852
+ }, [open])
853
+
854
+ const handleSelect = React.useCallback(
855
+ (range: DateRange | undefined) => {
856
+ onSelect?.(range)
857
+ const complete = range?.from && range?.to
858
+ && range.from.getTime() !== range.to.getTime()
859
+ if (complete) setOpen(false)
860
+ },
861
+ [onSelect]
862
+ )
863
+
864
+ const label = selected
865
+ ? formatRangeLabel(selected, fmt)
866
+ : undefined
867
+
868
+ return (
869
+ <div ref={ref} className={cn(styles.base, className)}>
870
+ <button
871
+ type="button"
872
+ className={styles.trigger}
873
+ disabled={disabled}
874
+ onClick={() => setOpen((v) => !v)}
875
+ >
876
+ <CalendarDays className={styles.icon} />
877
+ <span className={cn(styles.text, !label && styles.placeholder)}>
878
+ {label ?? placeholder}
879
+ </span>
880
+ </button>
881
+ <div className={cn(styles.content, open ? 'opacity-100 scale-100' : 'opacity-0 scale-95 pointer-events-none')}>
882
+ <Calendar.Range
883
+ locale={locale}
884
+ selected={selected}
885
+ onSelect={handleSelect}
886
+ defaultMonth={defaultMonth ?? selected?.from}
887
+ />
888
+ </div>
889
+ </div>
890
+ )
891
+ }
892
+
893
+ export { type DateRange, type Locale }
894
+
895
+ export const DatePicker = Object.assign(DatePickerRoot, {
896
+ Range,
897
+ })
898
+ `;var Z=`'use client'
899
+
900
+ import * as React from 'react'
901
+ import { Command as CommandPrimitive } from 'cmdk'
902
+ import { Search } from 'lucide-react'
903
+ import { cn } from '@/utils/cn'
904
+ import { Dialog } from '@/components/ui/dialog'
905
+
906
+ const styles = {
907
+ root: cn(
908
+ 'flex flex-col overflow-hidden',
909
+ 'bg-card border border-border rounded-[10px]'
910
+ ),
911
+ input: {
912
+ wrapper: cn(
913
+ 'flex items-center gap-2 px-3',
914
+ 'border-b border-border'
915
+ ),
916
+ icon: 'w-4 h-4 shrink-0 text-muted-foreground',
917
+ field: cn(
918
+ 'flex h-10 w-full bg-transparent py-2',
919
+ 'text-sm text-foreground outline-none',
920
+ 'placeholder:text-muted-foreground',
921
+ 'disabled:cursor-not-allowed disabled:opacity-50'
922
+ ),
923
+ },
924
+ list: cn(
925
+ 'h-[300px] overflow-y-auto overflow-x-hidden p-1',
926
+ '[&_[cmdk-list-sizer]]:space-y-1'
927
+ ),
928
+ empty: cn(
929
+ 'py-6 text-center text-sm',
930
+ 'text-muted-foreground'
931
+ ),
932
+ loading: cn(
933
+ 'py-6 text-center text-sm',
934
+ 'text-muted-foreground'
935
+ ),
936
+ group: cn(
937
+ 'overflow-hidden',
938
+ '[&_[cmdk-group-heading]]:px-2',
939
+ '[&_[cmdk-group-heading]]:py-1.5',
940
+ '[&_[cmdk-group-heading]]:text-xs',
941
+ '[&_[cmdk-group-heading]]:font-medium',
942
+ '[&_[cmdk-group-heading]]:text-muted-foreground'
943
+ ),
944
+ item: cn(
945
+ 'relative flex items-center gap-2 px-2 py-1.5',
946
+ 'text-sm rounded-lg cursor-default select-none',
947
+ 'data-[selected=true]:bg-accent',
948
+ 'data-[selected=true]:text-accent-foreground',
949
+ 'data-[disabled=true]:pointer-events-none',
950
+ 'data-[disabled=true]:opacity-50'
951
+ ),
952
+ separator: '-mx-1 h-px bg-border',
953
+ shortcut: cn(
954
+ 'ml-auto text-xs tracking-widest',
955
+ 'text-muted-foreground'
956
+ ),
957
+ }
958
+
959
+ function CommandRoot({
960
+ className,
961
+ ...props
962
+ }: React.ComponentProps<typeof CommandPrimitive>) {
963
+ return (
964
+ <CommandPrimitive
965
+ className={cn(styles.root, className)}
966
+ {...props}
967
+ />
968
+ )
969
+ }
970
+
971
+ function Input({
972
+ className,
973
+ ...props
974
+ }: React.ComponentProps<typeof CommandPrimitive.Input>) {
975
+ return (
976
+ <div className={styles.input.wrapper}>
977
+ <Search className={styles.input.icon} />
978
+ <CommandPrimitive.Input
979
+ className={cn(styles.input.field, className)}
980
+ {...props}
981
+ />
982
+ </div>
983
+ )
984
+ }
985
+
986
+ function List({
987
+ className,
988
+ ...props
989
+ }: React.ComponentProps<typeof CommandPrimitive.List>) {
990
+ return (
991
+ <CommandPrimitive.List
992
+ className={cn(styles.list, className)}
993
+ {...props}
994
+ />
995
+ )
996
+ }
997
+
998
+ function Empty({
999
+ className,
1000
+ children,
1001
+ ...props
1002
+ }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
1003
+ return (
1004
+ <CommandPrimitive.Empty
1005
+ className={cn(styles.empty, className)}
1006
+ {...props}
1007
+ >
1008
+ {children ?? 'No results found.'}
1009
+ </CommandPrimitive.Empty>
1010
+ )
1011
+ }
1012
+
1013
+ function Group({
1014
+ className,
1015
+ ...props
1016
+ }: React.ComponentProps<typeof CommandPrimitive.Group>) {
1017
+ return (
1018
+ <CommandPrimitive.Group
1019
+ className={cn(styles.group, className)}
1020
+ {...props}
1021
+ />
1022
+ )
1023
+ }
1024
+
1025
+ function Item({
1026
+ className,
1027
+ ...props
1028
+ }: React.ComponentProps<typeof CommandPrimitive.Item>) {
1029
+ return (
1030
+ <CommandPrimitive.Item
1031
+ className={cn(styles.item, className)}
1032
+ {...props}
1033
+ />
1034
+ )
1035
+ }
1036
+
1037
+ function Separator({
1038
+ className,
1039
+ ...props
1040
+ }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
1041
+ return (
1042
+ <CommandPrimitive.Separator
1043
+ className={cn(styles.separator, className)}
1044
+ {...props}
1045
+ />
1046
+ )
1047
+ }
1048
+
1049
+ function Loading({
1050
+ className,
1051
+ ...props
1052
+ }: React.ComponentProps<typeof CommandPrimitive.Loading>) {
1053
+ return (
1054
+ <CommandPrimitive.Loading
1055
+ className={cn(styles.loading, className)}
1056
+ {...props}
1057
+ />
1058
+ )
1059
+ }
1060
+
1061
+ function Shortcut({
1062
+ className,
1063
+ ...props
1064
+ }: React.HTMLAttributes<HTMLElement>) {
1065
+ return (
1066
+ <kbd
1067
+ className={cn(styles.shortcut, className)}
1068
+ {...props}
1069
+ />
1070
+ )
1071
+ }
1072
+
1073
+ function CommandDialog({
1074
+ open,
1075
+ onOpenChange,
1076
+ label,
1077
+ className,
1078
+ children,
1079
+ }: {
1080
+ open: boolean
1081
+ onOpenChange: (open: boolean) => void
1082
+ label?: string
1083
+ className?: string
1084
+ children: React.ReactNode
1085
+ }) {
1086
+ React.useEffect(() => {
1087
+ const onKeyDown = (e: KeyboardEvent) => {
1088
+ if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
1089
+ e.preventDefault()
1090
+ onOpenChange(!open)
1091
+ }
1092
+ }
1093
+ document.addEventListener('keydown', onKeyDown)
1094
+ return () => {
1095
+ document.removeEventListener('keydown', onKeyDown)
1096
+ }
1097
+ }, [open, onOpenChange])
1098
+
1099
+ return (
1100
+ <Dialog open={open} onOpenChange={onOpenChange}>
1101
+ <Dialog.Content
1102
+ className={cn(
1103
+ 'p-0 max-w-lg rounded-[10px]',
1104
+ className
1105
+ )}
1106
+ >
1107
+ <CommandPrimitive
1108
+ label={label}
1109
+ className="flex flex-col overflow-hidden"
1110
+ >
1111
+ {children}
1112
+ </CommandPrimitive>
1113
+ </Dialog.Content>
1114
+ </Dialog>
1115
+ )
1116
+ }
1117
+
1118
+ export const Command = Object.assign(CommandRoot, {
1119
+ Input,
1120
+ List,
1121
+ Empty,
1122
+ Group,
1123
+ Item,
1124
+ Separator,
1125
+ Loading,
1126
+ Shortcut,
1127
+ Dialog: CommandDialog,
1128
+ })
1129
+ `;var ee=`'use client'
1130
+
1131
+ import * as React from 'react'
1132
+ import useEmblaCarousel, { type UseEmblaCarouselType } from 'embla-carousel-react'
1133
+ import { ChevronLeft, ChevronRight } from 'lucide-react'
1134
+ import { cn } from '@/utils/cn'
1135
+ import { Button } from '@/components/ui/button'
1136
+
1137
+ type CarouselApi = UseEmblaCarouselType[1]
1138
+ type CarouselOptions = Parameters<typeof useEmblaCarousel>[0]
1139
+ type CarouselPlugins = Parameters<typeof useEmblaCarousel>[1]
1140
+
1141
+ const styles = {
1142
+ root: 'relative focus-visible:outline-none',
1143
+ content: {
1144
+ viewport: 'overflow-hidden',
1145
+ container: 'flex',
1146
+ horizontal: '-ml-4',
1147
+ vertical: '-mt-4 flex-col h-full',
1148
+ },
1149
+ item: {
1150
+ base: 'min-w-0 shrink-0 grow-0 basis-full',
1151
+ horizontal: 'pl-4',
1152
+ vertical: 'pt-4',
1153
+ },
1154
+ arrow: cn(
1155
+ 'absolute top-1/2 -translate-y-1/2',
1156
+ 'w-8 px-0 bg-card/80 backdrop-blur-sm'
1157
+ ),
1158
+ dots: {
1159
+ wrapper: 'flex justify-center gap-1.5 mt-3',
1160
+ dot: cn(
1161
+ 'w-2 h-2 rounded-full transition-colors',
1162
+ 'bg-border cursor-pointer'
1163
+ ),
1164
+ active: 'bg-foreground',
1165
+ },
1166
+ }
1167
+
1168
+ interface CarouselCtx {
1169
+ emblaRef: UseEmblaCarouselType[0]
1170
+ api: CarouselApi
1171
+ canScrollPrev: boolean
1172
+ canScrollNext: boolean
1173
+ selectedIndex: number
1174
+ scrollSnaps: number[]
1175
+ orientation: 'horizontal' | 'vertical'
1176
+ }
1177
+
1178
+ interface CarouselRootProps
1179
+ extends React.HTMLAttributes<HTMLDivElement> {
1180
+ opts?: CarouselOptions
1181
+ plugins?: CarouselPlugins
1182
+ orientation?: 'horizontal' | 'vertical'
1183
+ setApi?: (api: CarouselApi) => void
1184
+ }
1185
+
1186
+ function CarouselRoot({
1187
+ opts,
1188
+ plugins,
1189
+ orientation = 'horizontal',
1190
+ setApi,
1191
+ className,
1192
+ children,
1193
+ ...props
1194
+ }: CarouselRootProps) {
1195
+ const [emblaRef, api] = useEmblaCarousel({ ...opts, axis: orientation === 'vertical' ? 'y' : 'x' }, plugins)
1196
+ const [canScrollPrev, setCanScrollPrev] = React.useState(false)
1197
+ const [canScrollNext, setCanScrollNext] = React.useState(false)
1198
+ const [selectedIndex, setSelectedIndex] = React.useState(0)
1199
+ const [scrollSnaps, setScrollSnaps] = React.useState<number[]>([])
1200
+
1201
+ const onSelect = React.useCallback((emblaApi: NonNullable<CarouselApi>) => {
1202
+ setCanScrollPrev(emblaApi.canScrollPrev())
1203
+ setCanScrollNext(emblaApi.canScrollNext())
1204
+ setSelectedIndex(emblaApi.selectedScrollSnap())
1205
+ }, [])
1206
+
1207
+ React.useEffect(() => {
1208
+ if (!api) return
1209
+ setScrollSnaps(api.scrollSnapList())
1210
+ onSelect(api)
1211
+ api.on('reInit', onSelect)
1212
+ api.on('select', onSelect)
1213
+ return () => {
1214
+ api.off('reInit', onSelect)
1215
+ api.off('select', onSelect)
1216
+ }
1217
+ }, [api, onSelect])
1218
+
1219
+ React.useEffect(() => {
1220
+ if (setApi && api) setApi(api)
1221
+ }, [api, setApi])
1222
+
1223
+ const handleKeyDown = React.useCallback((e: React.KeyboardEvent) => {
1224
+ if (!api) return
1225
+ if (e.key === 'ArrowLeft') {
1226
+ e.preventDefault()
1227
+ api.scrollPrev()
1228
+ } else if (e.key === 'ArrowRight') {
1229
+ e.preventDefault()
1230
+ api.scrollNext()
1231
+ }
1232
+ }, [api])
1233
+
1234
+ return (
1235
+ <Ctx.Provider
1236
+ value={{
1237
+ emblaRef,
1238
+ api,
1239
+ canScrollPrev,
1240
+ canScrollNext,
1241
+ selectedIndex,
1242
+ scrollSnaps,
1243
+ orientation,
1244
+ }}
1245
+ >
1246
+ <div
1247
+ role="region"
1248
+ aria-roledescription="carousel"
1249
+ tabIndex={0}
1250
+ onKeyDown={handleKeyDown}
1251
+ className={cn(styles.root, className)}
1252
+ {...props}
1253
+ >
1254
+ {children}
1255
+ </div>
1256
+ </Ctx.Provider>
1257
+ )
1258
+ }
1259
+
1260
+ function Content({
1261
+ className,
1262
+ children,
1263
+ }: {
1264
+ className?: string
1265
+ children: React.ReactNode
1266
+ }) {
1267
+ const { emblaRef, orientation } = useCarousel()
1268
+ return (
1269
+ <div className={cn(styles.content.viewport, className)} ref={emblaRef}>
1270
+ <div className={cn(styles.content.container, orientation === 'vertical' ? styles.content.vertical : styles.content.horizontal)}>
1271
+ {children}
1272
+ </div>
1273
+ </div>
1274
+ )
1275
+ }
1276
+
1277
+ function Item({
1278
+ className,
1279
+ children,
1280
+ }: {
1281
+ className?: string
1282
+ children: React.ReactNode
1283
+ }) {
1284
+ const { orientation } = useCarousel()
1285
+ return (
1286
+ <div
1287
+ role="group"
1288
+ aria-roledescription="slide"
1289
+ className={cn(styles.item.base, orientation === 'vertical' ? styles.item.vertical : styles.item.horizontal, className)}
1290
+ >
1291
+ {children}
1292
+ </div>
1293
+ )
1294
+ }
1295
+
1296
+ function Previous({
1297
+ className,
1298
+ variant = 'secondary',
1299
+ size = 'sm',
1300
+ ...props
1301
+ }: React.ComponentProps<typeof Button>) {
1302
+ const { api, canScrollPrev } = useCarousel()
1303
+ return (
1304
+ <Button
1305
+ variant={variant}
1306
+ size={size}
1307
+ aria-label="Previous slide"
1308
+ className={cn(styles.arrow, '-left-12', className)}
1309
+ disabled={!canScrollPrev}
1310
+ onClick={() => api?.scrollPrev()}
1311
+ {...props}
1312
+ >
1313
+ <ChevronLeft className="w-4 h-4" />
1314
+ </Button>
1315
+ )
1316
+ }
1317
+
1318
+ function Next({
1319
+ className,
1320
+ variant = 'secondary',
1321
+ size = 'sm',
1322
+ ...props
1323
+ }: React.ComponentProps<typeof Button>) {
1324
+ const { api, canScrollNext } = useCarousel()
1325
+ return (
1326
+ <Button
1327
+ variant={variant}
1328
+ size={size}
1329
+ aria-label="Next slide"
1330
+ className={cn(styles.arrow, '-right-12', className)}
1331
+ disabled={!canScrollNext}
1332
+ onClick={() => api?.scrollNext()}
1333
+ {...props}
1334
+ >
1335
+ <ChevronRight className="w-4 h-4" />
1336
+ </Button>
1337
+ )
1338
+ }
1339
+
1340
+ function Dots({
1341
+ className,
1342
+ }: {
1343
+ className?: string
1344
+ }) {
1345
+ const { api, scrollSnaps, selectedIndex } = useCarousel()
1346
+ return (
1347
+ <div className={cn(styles.dots.wrapper, className)}>
1348
+ {scrollSnaps.map((_, i) => (
1349
+ <button
1350
+ key={i}
1351
+ type="button"
1352
+ aria-label={\`Go to slide \${i + 1}\`}
1353
+ className={cn(styles.dots.dot, i === selectedIndex && styles.dots.active)}
1354
+ onClick={() => api?.scrollTo(i)}
1355
+ />
1356
+ ))}
1357
+ </div>
1358
+ )
1359
+ }
1360
+
1361
+ const Ctx = React.createContext<CarouselCtx | null>(null)
1362
+
1363
+ function useCarousel() {
1364
+ const ctx = React.useContext(Ctx)
1365
+ if (!ctx) throw new Error('Carousel.* used outside <Carousel>')
1366
+ return ctx
1367
+ }
1368
+
1369
+ export type { CarouselApi }
1370
+
1371
+ export const Carousel = Object.assign(CarouselRoot, {
1372
+ Content,
1373
+ Item,
1374
+ Previous,
1375
+ Next,
1376
+ Dots
1377
+ })
1378
+ `;var te=`import * as React from 'react'
497
1379
  import { cn } from '@/utils/cn'
498
1380
 
499
1381
  const styles = {
@@ -558,9 +1440,9 @@ function Info({
558
1440
 
559
1441
  export const Card = Object.assign(CardRoot, {
560
1442
  Preview,
561
- Info,
1443
+ Info
562
1444
  })
563
- `;var Y=`'use client'
1445
+ `;var oe=`'use client'
564
1446
 
565
1447
  import * as React from 'react'
566
1448
  import { Check } from 'lucide-react'
@@ -623,7 +1505,7 @@ export const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(({
623
1505
  )
624
1506
 
625
1507
  Checkbox.displayName = 'Checkbox'
626
- `;var Z=`'use client'
1508
+ `;var ne=`'use client'
627
1509
 
628
1510
  import * as React from 'react'
629
1511
  import { X } from 'lucide-react'
@@ -780,9 +1662,9 @@ function useDialog() {
780
1662
  export const Dialog = Object.assign(DialogRoot, {
781
1663
  Trigger,
782
1664
  Content,
783
- Close,
1665
+ Close
784
1666
  })
785
- `;var ee=`'use client'
1667
+ `;var re=`'use client'
786
1668
 
787
1669
  import * as React from 'react'
788
1670
  import { cn } from '@/utils/cn'
@@ -978,9 +1860,9 @@ export const Dropdown = Object.assign(DropdownRoot, {
978
1860
  Item,
979
1861
  Group,
980
1862
  Label,
981
- Separator: DropdownSeparator,
1863
+ Separator: DropdownSeparator
982
1864
  })
983
- `;var te=`import * as React from 'react'
1865
+ `;var se=`import * as React from 'react'
984
1866
  import { cn } from '@/utils/cn'
985
1867
 
986
1868
  const styles = {
@@ -1003,7 +1885,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(({
1003
1885
  )
1004
1886
 
1005
1887
  Input.displayName = 'Input'
1006
- `;var oe=`'use client'
1888
+ `;var ae=`'use client'
1007
1889
 
1008
1890
  import * as React from 'react'
1009
1891
  import { cn } from '@/utils/cn'
@@ -1106,9 +1988,9 @@ function usePopover() {
1106
1988
 
1107
1989
  export const Popover = Object.assign(PopoverRoot, {
1108
1990
  Trigger,
1109
- Content,
1991
+ Content
1110
1992
  })
1111
- `;var ne=`import * as React from 'react'
1993
+ `;var ce=`import * as React from 'react'
1112
1994
  import { cn } from '@/utils/cn'
1113
1995
 
1114
1996
  const styles = {
@@ -1146,7 +2028,7 @@ export function Progress({
1146
2028
  </div>
1147
2029
  )
1148
2030
  }
1149
- `;var re=`'use client'
2031
+ `;var ie=`'use client'
1150
2032
 
1151
2033
  import * as React from 'react'
1152
2034
  import { ChevronDown } from 'lucide-react'
@@ -1298,9 +2180,9 @@ function useSelect() {
1298
2180
  export const Select = Object.assign(SelectRoot, {
1299
2181
  Trigger,
1300
2182
  Menu,
1301
- Option,
2183
+ Option
1302
2184
  })
1303
- `;var se=`import * as React from 'react'
2185
+ `;var le=`import * as React from 'react'
1304
2186
  import { cn } from '@/utils/cn'
1305
2187
 
1306
2188
  const styles = {
@@ -1324,7 +2206,7 @@ export function Separator({
1324
2206
  />
1325
2207
  )
1326
2208
  }
1327
- `;var ae=`'use client'
2209
+ `;var de=`'use client'
1328
2210
 
1329
2211
  import * as React from 'react'
1330
2212
  import { cn } from '@/utils/cn'
@@ -1365,7 +2247,7 @@ export function Switch({
1365
2247
  </button>
1366
2248
  )
1367
2249
  }
1368
- `;var ce=`'use client'
2250
+ `;var pe=`'use client'
1369
2251
 
1370
2252
  import * as React from 'react'
1371
2253
  import { cn } from '@/utils/cn'
@@ -1477,9 +2359,9 @@ function useTabs() {
1477
2359
  export const Tabs = Object.assign(TabsRoot, {
1478
2360
  List,
1479
2361
  Tab,
1480
- Panel,
2362
+ Panel
1481
2363
  })
1482
- `;var ie=`import * as React from 'react'
2364
+ `;var ue=`import * as React from 'react'
1483
2365
  import { cn } from '@/utils/cn'
1484
2366
 
1485
2367
  const styles = {
@@ -1506,7 +2388,7 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(({
1506
2388
  ))
1507
2389
 
1508
2390
  Textarea.displayName = 'Textarea'
1509
- `;var le=`'use client'
2391
+ `;var me=`'use client'
1510
2392
 
1511
2393
  import * as React from 'react'
1512
2394
  import { Toaster as Sonner, toast } from 'sonner'
@@ -1549,7 +2431,7 @@ function Toaster() {
1549
2431
  }
1550
2432
 
1551
2433
  export { Toaster, toast }
1552
- `;var de=`import * as React from 'react'
2434
+ `;var fe=`import * as React from 'react'
1553
2435
  import { cn } from '@/utils/cn'
1554
2436
 
1555
2437
  const styles = {
@@ -1594,4 +2476,15 @@ export function Tooltip({
1594
2476
  </span>
1595
2477
  )
1596
2478
  }
1597
- `;var R=[{name:"accordion",description:"Collapsible content sections with dot notation and smooth animation",dependencies:[],npmDependencies:[]},{name:"alert",description:"Contextual feedback messages with variants and icons",dependencies:[],npmDependencies:[]},{name:"avatar",description:"User avatar with image support and fallback initials",dependencies:[],npmDependencies:[]},{name:"badge",description:"Small status indicator with color variants",dependencies:[],npmDependencies:[]},{name:"button",description:"Button with variants, sizes, loading state, and icon slots",dependencies:[],npmDependencies:[]},{name:"card",description:"Container with dot notation preview and info sub-components",dependencies:[],npmDependencies:[]},{name:"checkbox",description:"Checkbox input with label and CSS-only checkmark",dependencies:[],npmDependencies:[]},{name:"dialog",description:"Modal dialog with dot notation, overlay, and escape key",dependencies:["button"],npmDependencies:[]},{name:"dropdown",description:"Dropdown menu with dot notation, groups, separators, and click-outside",dependencies:["button"],npmDependencies:[]},{name:"input",description:"Text input with focus ring and disabled state",dependencies:[],npmDependencies:[]},{name:"popover",description:"Floating content panel with dot notation and click-outside",dependencies:["button"],npmDependencies:[]},{name:"progress",description:"Progress bar with animated fill and ARIA attributes",dependencies:[],npmDependencies:[]},{name:"select",description:"Custom select with dot notation and composable options",dependencies:[],npmDependencies:[]},{name:"separator",description:"Visual divider with horizontal and vertical orientation",dependencies:[],npmDependencies:[]},{name:"switch",description:"Toggle switch with smooth transition",dependencies:[],npmDependencies:[]},{name:"tabs",description:"Tab navigation with dot notation and panel content",dependencies:[],npmDependencies:[]},{name:"textarea",description:"Multi-line text input with consistent styling",dependencies:[],npmDependencies:[]},{name:"theme",description:"Dark/light theme support with next-themes and ThemeProvider",dependencies:[],npmDependencies:["next-themes"]},{name:"toast",description:"Toast notifications powered by Sonner",dependencies:[],npmDependencies:["sonner"]},{name:"tooltip",description:"Pure CSS tooltip with 4 position options",dependencies:[],npmDependencies:[]}],P={accordion:W,alert:X,avatar:q,badge:J,button:K,card:Q,checkbox:Y,dialog:Z,dropdown:ee,input:te,popover:oe,progress:ne,select:re,separator:se,switch:ae,tabs:ce,textarea:ie,theme:G,toast:le,tooltip:de};async function pe(t){let n=process.cwd();e.intro("drivn add");let c=V(n);if(c||(e.log.error("Drivn is not initialized. Run npx drivn@latest create"),e.outro("Cancelled"),process.exit(1)),!t||!t.length){let o=await e.multiselect({message:"Select components to add",options:R.map(s=>({label:s.name,hint:s.description,value:s.name})),required:true});e.isCancel(o)&&(e.cancel("Cancelled"),process.exit(0)),t=o;}let v=t.filter(o=>!R.find(s=>s.name===o));v.length&&(e.log.error(`Unknown components: ${v.join(", ")}`),e.log.info("Available: "+R.map(o=>o.name).join(", ")),e.outro("Cancelled"),process.exit(1));let d=t.includes("theme"),m=t.filter(o=>o!=="theme"),f=new Set,b=new Set,g=o=>{if(f.has(o))return;let s=R.find(a=>a.name===o);s&&(s.dependencies.forEach(a=>g(a)),s.npmDependencies?.forEach(a=>b.add(a)),f.add(o));};m.forEach(g),d&&b.add("next-themes");let x=[...f].filter(o=>!m.includes(o));x.length&&e.log.info(`Required dependency: ${x.join(", ")}`);let l=c.typescript?"tsx":"jsx",N=join(n,c.paths.components);for(let o of f){let s=join(N,`${o}.${l}`);if(h(s)){let w=await e.confirm({message:`${o}.${l} exists. Overwrite?`,initialValue:false});if(e.isCancel(w)||!w){e.log.warn(`Skipped ${o}`);continue}}let a=P[o];a=a.replace(/@\/utils/g,`@/${c.paths.utils.replace(/^src\//,"")}`),p(s,a),e.log.success(`${o} \u2192 ${c.paths.components}/${o}.${l}`);}if(d){let o=join(N,`theme-provider.${l}`);if(h(o)){let a=await e.confirm({message:`theme-provider.${l} exists. Overwrite?`,initialValue:false});!e.isCancel(a)&&a?(p(o,P.theme),e.log.success(`theme-provider \u2192 ${c.paths.components}/theme-provider.${l}`)):e.log.warn("Skipped theme-provider");}else p(o,P.theme),e.log.success(`theme-provider \u2192 ${c.paths.components}/theme-provider.${l}`);if(c.paths.globals){let a=join(n,c.paths.globals);if(h(a)){let w=F(a);w.includes('[data-theme="dark"]')?e.log.warn("Theme tokens already exist in globals \u2014 skipped"):(p(a,w+L),e.log.success(`Theme tokens appended to ${i.cyan(c.paths.globals)}`));}else e.log.warn(`Globals file not found at ${c.paths.globals}`);}else e.log.warn('No globals path in drivn.config.json. Add "globals" to paths');let s=c.paths.components.replace(/^src\//,"@/");e.log.message(""),e.log.info(i.bold("Complete the setup:")),e.log.message(""),e.log.message(i.bold(`${i.cyan("1.")} Import ThemeProvider in your root layout:`)),e.log.message(i.cyan(` import { ThemeProvider } from "${s}/theme-provider"`)),e.log.message(""),e.log.message(i.bold(`${i.cyan("2.")} Add suppressHydrationWarning to <html>:`)),e.log.message(i.cyan(" <html suppressHydrationWarning>")),e.log.message(""),e.log.message(i.bold(`${i.cyan("3.")} Wrap your app with ThemeProvider:`)),e.log.message(i.cyan(" <ThemeProvider>")),e.log.message(i.cyan(" {children}")),e.log.message(i.cyan(" </ThemeProvider>")),e.log.message("");}if(b.size){let o=k(n),s=[...b],a=e.spinner();a.start("Installing packages");try{execSync(y(o,s),{cwd:n,stdio:"ignore"}),a.stop("Packages installed");}catch{a.stop("Failed to install packages"),e.log.warn(`Run manually: ${y(o,s)}`);}}e.outro("Done.");}var ue={version:"1.1.0"};var D=new Command;D.name("drivn").description("Drivn \u2014 Modern UI components").version(ue.version);D.command("create").description("Initialize Drivn in your project").action(U);D.command("add [components...]").description("Add components to your project").action(pe);D.parse();
2479
+ `;var ge=`
2480
+ /* react-day-picker theme integration */
2481
+ .rdp-root {
2482
+ --rdp-accent-color: var(--primary);
2483
+ --rdp-accent-background-color: var(--accent);
2484
+ --rdp-day-height: 36px;
2485
+ --rdp-day-width: 36px;
2486
+ --rdp-selected-font: inherit;
2487
+ --rdp-selected-border: none;
2488
+ --rdp-day_button-border: none;
2489
+ }
2490
+ `,R=[{name:"accordion",description:"Collapsible content sections with dot notation and smooth animation",dependencies:[],npmDependencies:[]},{name:"alert",description:"Contextual feedback messages with variants and icons",dependencies:[],npmDependencies:[]},{name:"avatar",description:"User avatar with image support and fallback initials",dependencies:[],npmDependencies:[]},{name:"badge",description:"Small status indicator with color variants",dependencies:[],npmDependencies:[]},{name:"button",description:"Button with variants, sizes, loading state, and icon slots",dependencies:[],npmDependencies:[]},{name:"calendar",description:"Date picker powered by react-day-picker with single, range, and dropdown modes",dependencies:[],npmDependencies:["react-day-picker"]},{name:"date-picker",description:"Date picker input with Calendar dropdown for single date and range selection",dependencies:["calendar"],npmDependencies:[]},{name:"command",description:"Searchable command menu with filtering, keyboard navigation, and dialog mode",dependencies:["dialog"],npmDependencies:["cmdk"]},{name:"carousel",description:"Carousel with touch/swipe, navigation arrows, dot indicators, and loop mode",dependencies:["button"],npmDependencies:["embla-carousel-react"]},{name:"card",description:"Container with dot notation preview and info sub-components",dependencies:[],npmDependencies:[]},{name:"checkbox",description:"Checkbox input with label and CSS-only checkmark",dependencies:[],npmDependencies:[]},{name:"dialog",description:"Modal dialog with dot notation, overlay, and escape key",dependencies:["button"],npmDependencies:[]},{name:"dropdown",description:"Dropdown menu with dot notation, groups, separators, and click-outside",dependencies:["button"],npmDependencies:[]},{name:"input",description:"Text input with focus ring and disabled state",dependencies:[],npmDependencies:[]},{name:"popover",description:"Floating content panel with dot notation and click-outside",dependencies:["button"],npmDependencies:[]},{name:"progress",description:"Progress bar with animated fill and ARIA attributes",dependencies:[],npmDependencies:[]},{name:"select",description:"Custom select with dot notation and composable options",dependencies:[],npmDependencies:[]},{name:"separator",description:"Visual divider with horizontal and vertical orientation",dependencies:[],npmDependencies:[]},{name:"switch",description:"Toggle switch with smooth transition",dependencies:[],npmDependencies:[]},{name:"tabs",description:"Tab navigation with dot notation and panel content",dependencies:[],npmDependencies:[]},{name:"textarea",description:"Multi-line text input with consistent styling",dependencies:[],npmDependencies:[]},{name:"theme",description:"Dark/light theme support with next-themes and ThemeProvider",dependencies:[],npmDependencies:["next-themes"]},{name:"toast",description:"Toast notifications powered by Sonner",dependencies:[],npmDependencies:["sonner"]},{name:"tooltip",description:"Pure CSS tooltip with 4 position options",dependencies:[],npmDependencies:[]}],S={accordion:U,alert:G,avatar:W,badge:X,button:q,calendar:J,"date-picker":Q,command:Z,carousel:ee,card:te,checkbox:oe,dialog:ne,dropdown:re,input:se,popover:ae,progress:ce,select:ie,separator:le,switch:de,tabs:pe,textarea:ue,theme:Y,toast:me,tooltip:fe};async function he(t){let n=process.cwd();e.intro("drivn add");let a=V(n);if(a||(e.log.error("Drivn is not initialized. Run npx drivn@latest create"),e.outro("Cancelled"),process.exit(1)),!t||!t.length){let o=await e.multiselect({message:"Select components to add",options:R.map(r=>({label:r.name,hint:r.description,value:r.name})),required:true});e.isCancel(o)&&(e.cancel("Cancelled"),process.exit(0)),t=o;}let v=t.filter(o=>!R.find(r=>r.name===o));v.length&&(e.log.error(`Unknown components: ${v.join(", ")}`),e.log.info("Available: "+R.map(o=>o.name).join(", ")),e.outro("Cancelled"),process.exit(1));let p=t.includes("theme"),g=t.filter(o=>o!=="theme"),u=new Set,b=new Set,h=o=>{if(u.has(o))return;let r=R.find(c=>c.name===o);r&&(r.dependencies.forEach(c=>h(c)),r.npmDependencies?.forEach(c=>b.add(c)),u.add(o));};g.forEach(h),p&&b.add("next-themes");let x=[...u].filter(o=>!g.includes(o));x.length&&e.log.info(`Required dependency: ${x.join(", ")}`);let l=a.typescript?"tsx":"jsx",N=join(n,a.paths.components);for(let o of u){let r=join(N,`${o}.${l}`);if(m(r)){let C=await e.confirm({message:`${o}.${l} exists. Overwrite?`,initialValue:false});if(e.isCancel(C)||!C){e.log.warn(`Skipped ${o}`);continue}}let c=S[o];c=c.replace(/@\/utils/g,`@/${a.paths.utils.replace(/^src\//,"")}`),d(r,c),e.log.success(`${o} \u2192 ${a.paths.components}/${o}.${l}`);}if(p){let o=join(N,`theme-provider.${l}`);if(m(o)){let c=await e.confirm({message:`theme-provider.${l} exists. Overwrite?`,initialValue:false});!e.isCancel(c)&&c?(d(o,S.theme),e.log.success(`theme-provider \u2192 ${a.paths.components}/theme-provider.${l}`)):e.log.warn("Skipped theme-provider");}else d(o,S.theme),e.log.success(`theme-provider \u2192 ${a.paths.components}/theme-provider.${l}`);if(a.paths.globals){let c=join(n,a.paths.globals);if(m(c)){let C=O(c);C.includes('[data-theme="dark"]')?e.log.warn("Theme tokens already exist in globals \u2014 skipped"):(d(c,C+M),e.log.success(`Theme tokens appended to ${i.cyan(a.paths.globals)}`));}else e.log.warn(`Globals file not found at ${a.paths.globals}`);}else e.log.warn('No globals path in drivn.config.json. Add "globals" to paths');let r=a.paths.components.replace(/^src\//,"@/");e.log.message(""),e.log.info(i.bold("Complete the setup:")),e.log.message(""),e.log.message(i.bold(`${i.cyan("1.")} Import ThemeProvider in your root layout:`)),e.log.message(i.cyan(` import { ThemeProvider } from "${r}/theme-provider"`)),e.log.message(""),e.log.message(i.bold(`${i.cyan("2.")} Add suppressHydrationWarning to <html>:`)),e.log.message(i.cyan(" <html suppressHydrationWarning>")),e.log.message(""),e.log.message(i.bold(`${i.cyan("3.")} Wrap your app with ThemeProvider:`)),e.log.message(i.cyan(" <ThemeProvider>")),e.log.message(i.cyan(" {children}")),e.log.message(i.cyan(" </ThemeProvider>")),e.log.message("");}if(u.has("calendar")&&a.paths.globals){let o=join(n,a.paths.globals);if(m(o)){let r=O(o);r.includes(".rdp-root")?e.log.warn("Calendar tokens already exist in globals \u2014 skipped"):(d(o,r+ge),e.log.success(`Calendar tokens appended to ${i.cyan(a.paths.globals)}`));}}if(b.size){let o=P(n),r=[...b],c=e.spinner();c.start("Installing packages");try{execSync(y(o,r),{cwd:n,stdio:"ignore"}),c.stop("Packages installed");}catch{c.stop("Failed to install packages"),e.log.warn(`Run manually: ${y(o,r)}`);}}e.outro("Done.");}var ve={version:"1.2.0"};var T=new Command;T.name("drivn").description("Drivn \u2014 Modern UI components").version(ve.version);T.command("create").description("Initialize Drivn in your project").action(K);T.command("add [components...]").description("Add components to your project").action(he);T.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drivn",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Modern UI components for React — add beautiful, accessible components to your project",
5
5
  "license": "MIT",
6
6
  "type": "module",