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.
- package/dist/index.js +924 -31
- 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
|
|
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
|
-
`,
|
|
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
|
|
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
|
-
`,
|
|
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
|
|
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,
|
|
292
|
+
Item,
|
|
293
|
+
Trigger,
|
|
294
|
+
Content
|
|
293
295
|
})
|
|
294
|
-
`;var
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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();
|