periplo-ui 3.13.3 → 3.15.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/components/Accordion/Accordion.js.map +1 -1
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/Checkbox/Checkbox.js.map +1 -1
- package/dist/components/Command/Command.js.map +1 -1
- package/dist/components/DataTable/DataTable.d.ts +14 -5
- package/dist/components/DataTable/DataTable.js +41 -31
- package/dist/components/DataTable/DataTable.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.d.ts +19 -11
- package/dist/components/DatePicker/DatePicker.js +63 -41
- package/dist/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/Dialog/Dialog.d.ts +3 -1
- package/dist/components/Dialog/Dialog.js +33 -21
- package/dist/components/Dialog/Dialog.js.map +1 -1
- package/dist/components/DropdownMenu/DropdownMenu.js.map +1 -1
- package/dist/components/Form/Form.js.map +1 -1
- package/dist/components/Label/Label.js.map +1 -1
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Progress/Progress.js.map +1 -1
- package/dist/components/RadioGroup/RadioGroup.js.map +1 -1
- package/dist/components/Select/Select.d.ts +5 -4
- package/dist/components/Select/Select.js +32 -30
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/index.js +1 -1
- package/dist/components/Separator/Separator.js.map +1 -1
- package/dist/components/Sheet/Sheet.js.map +1 -1
- package/dist/components/Sidebar/Sidebar.js.map +1 -1
- package/dist/components/Tabs/Tabs.js.map +1 -1
- package/dist/components/Toggle/Toggle.js.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroup.js.map +1 -1
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Accordion.js","sources":["../../../src/components/Accordion/Accordion.tsx"],"sourcesContent":["import * as React from 'react'\nimport * as AccordionPrimitive from '@radix-ui/react-accordion'\nimport { CaretDown } from '@phosphor-icons/react'\n\nimport { cn } from '@/lib/utils'\n\nconst Accordion = AccordionPrimitive.Root\n\nconst AccordionItem = React.forwardRef<\n React.
|
|
1
|
+
{"version":3,"file":"Accordion.js","sources":["../../../src/components/Accordion/Accordion.tsx"],"sourcesContent":["import * as React from 'react'\nimport * as AccordionPrimitive from '@radix-ui/react-accordion'\nimport { CaretDown } from '@phosphor-icons/react'\n\nimport { cn } from '@/lib/utils'\n\nconst Accordion = AccordionPrimitive.Root\n\nconst AccordionItem = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>\n>(({ className, ...props }, ref) => (\n <AccordionPrimitive.Item ref={ref} className={cn('border-b', className)} {...props} />\n))\nAccordionItem.displayName = 'AccordionItem'\n\nconst AccordionTrigger = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n <AccordionPrimitive.Header className=\"flex\">\n <AccordionPrimitive.Trigger\n ref={ref}\n className={cn(\n 'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline md:py-6 [&[data-state=open]>svg]:rotate-180',\n className,\n )}\n {...props}\n >\n {children}\n <CaretDown className=\"h-6 w-6 shrink-0 text-foreground transition-transform duration-200\" />\n </AccordionPrimitive.Trigger>\n </AccordionPrimitive.Header>\n))\nAccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName\n\nconst AccordionContent = React.forwardRef<\n React.ComponentRef<typeof AccordionPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <AccordionPrimitive.Content\n ref={ref}\n className=\"overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down\"\n {...props}\n >\n <div className={cn('pb-6 pt-0', className)}>{children}</div>\n </AccordionPrimitive.Content>\n))\n\nAccordionContent.displayName = AccordionPrimitive.Content.displayName\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent }\n"],"names":[],"mappings":";;;;;;AAMA,MAAM,YAAY,kBAAmB,CAAA;AAE/B,MAAA,aAAA,GAAgB,MAAM,UAG1B,CAAA,CAAC,EAAE,SAAW,EAAA,GAAG,KAAM,EAAA,EAAG,GAC1B,qBAAA,GAAA,CAAC,mBAAmB,IAAnB,EAAA,EAAwB,KAAU,SAAW,EAAA,EAAA,CAAG,YAAY,SAAS,CAAA,EAAI,GAAG,KAAA,EAAO,CACrF;AACD,aAAA,CAAc,WAAc,GAAA,eAAA;AAE5B,MAAM,mBAAmB,KAAM,CAAA,UAAA,CAG7B,CAAC,EAAE,WAAW,QAAU,EAAA,GAAG,KAAM,EAAA,EAAG,wBACnC,GAAA,CAAA,kBAAA,CAAmB,MAAnB,EAAA,EAA0B,WAAU,MACnC,EAAA,QAAA,kBAAA,IAAA;AAAA,EAAC,kBAAmB,CAAA,OAAA;AAAA,EAAnB;AAAA,IACC,GAAA;AAAA,IACA,SAAW,EAAA,EAAA;AAAA,MACT,sIAAA;AAAA,MACA;AAAA,KACF;AAAA,IACC,GAAG,KAAA;AAAA,IAEH,QAAA,EAAA;AAAA,MAAA,QAAA;AAAA,sBACD,GAAA,CAAC,SAAU,EAAA,EAAA,SAAA,EAAU,oEAAqE,EAAA;AAAA;AAAA;AAC5F,CAAA,EACF,CACD;AACD,gBAAiB,CAAA,WAAA,GAAc,mBAAmB,OAAQ,CAAA,WAAA;AAEpD,MAAA,gBAAA,GAAmB,KAAM,CAAA,UAAA,CAG7B,CAAC,EAAE,WAAW,QAAU,EAAA,GAAG,KAAM,EAAA,EAAG,GACpC,qBAAA,GAAA;AAAA,EAAC,kBAAmB,CAAA,OAAA;AAAA,EAAnB;AAAA,IACC,GAAA;AAAA,IACA,SAAU,EAAA,0HAAA;AAAA,IACT,GAAG,KAAA;AAAA,IAEJ,8BAAC,KAAI,EAAA,EAAA,SAAA,EAAW,GAAG,WAAa,EAAA,SAAS,GAAI,QAAS,EAAA;AAAA;AACxD,CACD;AAED,gBAAiB,CAAA,WAAA,GAAc,mBAAmB,OAAQ,CAAA,WAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Avatar.js","sources":["../../../src/components/Avatar/Avatar.tsx"],"sourcesContent":["import * as AvatarPrimitive from '@radix-ui/react-avatar'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport React from 'react'\n\nimport { cn } from '../../lib/utils'\n\nconst avatarVariants = cva('h-10 w-10 relative flex shrink-0 rounded-full', {\n variants: {\n /**\n * Controls the size of the avatar\n * @default \"md\"\n */\n size: {\n /** Small size - 32x32px */\n sm: 'h-8 w-8',\n /** Medium size - 40x40px */\n md: 'h-10 w-10',\n /** Large size - 48x48px */\n lg: 'h-12 w-12',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n})\nexport interface AvatarProps extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof avatarVariants> {}\n\n/**\n * Root avatar component that serves as a container for AvatarImage and AvatarFallback\n * @param props.size - Controls the size of the avatar (\"sm\" | \"md\" | \"lg\")\n * @param props.className - Additional CSS classes to apply\n */\nconst Avatar = React.forwardRef<React.
|
|
1
|
+
{"version":3,"file":"Avatar.js","sources":["../../../src/components/Avatar/Avatar.tsx"],"sourcesContent":["import * as AvatarPrimitive from '@radix-ui/react-avatar'\nimport { cva, type VariantProps } from 'class-variance-authority'\nimport React from 'react'\n\nimport { cn } from '../../lib/utils'\n\nconst avatarVariants = cva('h-10 w-10 relative flex shrink-0 rounded-full', {\n variants: {\n /**\n * Controls the size of the avatar\n * @default \"md\"\n */\n size: {\n /** Small size - 32x32px */\n sm: 'h-8 w-8',\n /** Medium size - 40x40px */\n md: 'h-10 w-10',\n /** Large size - 48x48px */\n lg: 'h-12 w-12',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n})\nexport interface AvatarProps extends React.HTMLAttributes<HTMLElement>, VariantProps<typeof avatarVariants> {}\n\n/**\n * Root avatar component that serves as a container for AvatarImage and AvatarFallback\n * @param props.size - Controls the size of the avatar (\"sm\" | \"md\" | \"lg\")\n * @param props.className - Additional CSS classes to apply\n */\nconst Avatar = React.forwardRef<React.ComponentRef<typeof AvatarPrimitive.Root>, AvatarProps>(\n ({ className, size, ...props }, ref) => (\n <AvatarPrimitive.Root ref={ref} className={cn(avatarVariants({ size, className }))} {...props} />\n ),\n)\nAvatar.displayName = 'Avatar'\n\n/**\n * Component for displaying the actual avatar image\n * @param props.src - The source URL of the image\n * @param props.alt - Alt text for the image\n * @param props.className - Additional CSS classes to apply\n */\nconst AvatarImage = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Image>,\n React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>\n>(({ className, ...props }, ref) => (\n <AvatarPrimitive.Image\n ref={ref}\n className={cn('aspect-square h-full w-full shrink-0 overflow-hidden rounded-full', className)}\n {...props}\n />\n))\nAvatarImage.displayName = 'AvatarImage'\n\n/**\n * Fallback component displayed when the image fails to load or isn't provided\n * @param props.className - Additional CSS classes to apply\n * @param props.children - Content to display as fallback (typically initials or an icon)\n */\nconst AvatarFallback = React.forwardRef<\n React.ComponentRef<typeof AvatarPrimitive.Fallback>,\n React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>\n>(({ className, ...props }, ref) => (\n <AvatarPrimitive.Fallback\n ref={ref}\n className={cn('bg-muted flex h-full w-full items-center justify-center rounded-full', className)}\n {...props}\n />\n))\nAvatarFallback.displayName = 'AvatarFallback'\n\ninterface AvatarGroupProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Optional custom className */\n className?: string\n /** Children should be Avatar components */\n children: React.ReactNode\n}\n\n/**\n * Container component for grouping multiple avatars with an overlapping effect\n * @param props.className - Additional CSS classes to apply\n * @param props.children - Avatar components to be grouped\n */\nconst AvatarGroup = React.forwardRef<HTMLDivElement, AvatarGroupProps>(({ className, children, ...props }, ref) => {\n const modifiedChildren = React.Children.map(children, (child) => {\n if (React.isValidElement<AvatarProps>(child) && child.type === Avatar) {\n return React.cloneElement(child, {\n className: cn('ring-2 ring-white dark:ring-gray-800', child.props.className),\n })\n }\n return child\n })\n\n return (\n <div ref={ref} className={cn('flex -space-x-4', className)} {...props}>\n {modifiedChildren}\n </div>\n )\n})\n\nAvatarGroup.displayName = 'AvatarGroup'\n\nexport { Avatar, AvatarImage, AvatarFallback, AvatarGroup }\n"],"names":["React"],"mappings":";;;;;;AAMA,MAAM,cAAA,GAAiB,IAAI,+CAAiD,EAAA;AAAA,EAC1E,QAAU,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKR,IAAM,EAAA;AAAA;AAAA,MAEJ,EAAI,EAAA,SAAA;AAAA;AAAA,MAEJ,EAAI,EAAA,WAAA;AAAA;AAAA,MAEJ,EAAI,EAAA;AAAA;AACN,GACF;AAAA,EACA,eAAiB,EAAA;AAAA,IACf,IAAM,EAAA;AAAA;AAEV,CAAC,CAAA;AAQD,MAAM,SAASA,cAAM,CAAA,UAAA;AAAA,EACnB,CAAC,EAAE,SAAW,EAAA,IAAA,EAAM,GAAG,KAAM,EAAA,EAAG,GAC9B,qBAAA,GAAA,CAAC,eAAgB,CAAA,IAAA,EAAhB,EAAqB,GAAU,EAAA,SAAA,EAAW,EAAG,CAAA,cAAA,CAAe,EAAE,IAAA,EAAM,WAAW,CAAC,CAAI,EAAA,GAAG,KAAO,EAAA;AAEnG;AACA,MAAA,CAAO,WAAc,GAAA,QAAA;AAQf,MAAA,WAAA,GAAcA,eAAM,UAGxB,CAAA,CAAC,EAAE,SAAW,EAAA,GAAG,KAAM,EAAA,EAAG,GAC1B,qBAAA,GAAA;AAAA,EAAC,eAAgB,CAAA,KAAA;AAAA,EAAhB;AAAA,IACC,GAAA;AAAA,IACA,SAAA,EAAW,EAAG,CAAA,mEAAA,EAAqE,SAAS,CAAA;AAAA,IAC3F,GAAG;AAAA;AACN,CACD;AACD,WAAA,CAAY,WAAc,GAAA,aAAA;AAOpB,MAAA,cAAA,GAAiBA,eAAM,UAG3B,CAAA,CAAC,EAAE,SAAW,EAAA,GAAG,KAAM,EAAA,EAAG,GAC1B,qBAAA,GAAA;AAAA,EAAC,eAAgB,CAAA,QAAA;AAAA,EAAhB;AAAA,IACC,GAAA;AAAA,IACA,SAAA,EAAW,EAAG,CAAA,sEAAA,EAAwE,SAAS,CAAA;AAAA,IAC9F,GAAG;AAAA;AACN,CACD;AACD,cAAA,CAAe,WAAc,GAAA,gBAAA;AAcvB,MAAA,WAAA,GAAcA,cAAM,CAAA,UAAA,CAA6C,CAAC,EAAE,WAAW,QAAU,EAAA,GAAG,KAAM,EAAA,EAAG,GAAQ,KAAA;AACjH,EAAA,MAAM,mBAAmBA,cAAM,CAAA,QAAA,CAAS,GAAI,CAAA,QAAA,EAAU,CAAC,KAAU,KAAA;AAC/D,IAAA,IAAIA,eAAM,cAA4B,CAAA,KAAK,CAAK,IAAA,KAAA,CAAM,SAAS,MAAQ,EAAA;AACrE,MAAO,OAAAA,cAAA,CAAM,aAAa,KAAO,EAAA;AAAA,QAC/B,SAAW,EAAA,EAAA,CAAG,sCAAwC,EAAA,KAAA,CAAM,MAAM,SAAS;AAAA,OAC5E,CAAA;AAAA;AAEH,IAAO,OAAA,KAAA;AAAA,GACR,CAAA;AAED,EACE,uBAAA,GAAA,CAAC,KAAI,EAAA,EAAA,GAAA,EAAU,SAAW,EAAA,EAAA,CAAG,mBAAmB,SAAS,CAAA,EAAI,GAAG,KAAA,EAC7D,QACH,EAAA,gBAAA,EAAA,CAAA;AAEJ,CAAC;AAED,WAAA,CAAY,WAAc,GAAA,aAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","sources":["../../../src/components/Checkbox/Checkbox.tsx"],"sourcesContent":["import * as React from 'react'\nimport * as CheckboxPrimitive from '@radix-ui/react-checkbox'\nimport { Check } from '@phosphor-icons/react'\nimport { cn } from '@/lib/utils'\n\ninterface CheckboxProps extends React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> {\n /** Additional CSS classes to be applied to the checkbox */\n className?: string\n /** The controlled checked state of the checkbox */\n checked?: boolean\n /** The default checked state when initially rendered */\n defaultChecked?: boolean\n /** Whether the checkbox is disabled */\n disabled?: boolean\n /** Whether the checkbox is required in a form */\n required?: boolean\n /** The name of the checkbox when used in a form */\n name?: string\n /** The value of the checkbox when used in a form */\n value?: string\n /** Handler called when the checked state changes */\n onCheckedChange?: (checked: boolean) => void\n}\n\n/**\n * A controlled checkbox component built on top of Radix UI Checkbox.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <Checkbox />\n *\n * // With controlled state\n * <Checkbox checked={checked} onCheckedChange={setChecked} />\n *\n * // With form integration\n * <Checkbox name=\"terms\" required />\n *\n * // With label\n * <div className=\"flex items-center gap-2\">\n * <Checkbox id=\"terms\" />\n * <label htmlFor=\"terms\">Accept terms</label>\n * </div>\n * ```\n */\nconst Checkbox = React.forwardRef<React.
|
|
1
|
+
{"version":3,"file":"Checkbox.js","sources":["../../../src/components/Checkbox/Checkbox.tsx"],"sourcesContent":["import * as React from 'react'\nimport * as CheckboxPrimitive from '@radix-ui/react-checkbox'\nimport { Check } from '@phosphor-icons/react'\nimport { cn } from '@/lib/utils'\n\ninterface CheckboxProps extends React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root> {\n /** Additional CSS classes to be applied to the checkbox */\n className?: string\n /** The controlled checked state of the checkbox */\n checked?: boolean\n /** The default checked state when initially rendered */\n defaultChecked?: boolean\n /** Whether the checkbox is disabled */\n disabled?: boolean\n /** Whether the checkbox is required in a form */\n required?: boolean\n /** The name of the checkbox when used in a form */\n name?: string\n /** The value of the checkbox when used in a form */\n value?: string\n /** Handler called when the checked state changes */\n onCheckedChange?: (checked: boolean) => void\n}\n\n/**\n * A controlled checkbox component built on top of Radix UI Checkbox.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <Checkbox />\n *\n * // With controlled state\n * <Checkbox checked={checked} onCheckedChange={setChecked} />\n *\n * // With form integration\n * <Checkbox name=\"terms\" required />\n *\n * // With label\n * <div className=\"flex items-center gap-2\">\n * <Checkbox id=\"terms\" />\n * <label htmlFor=\"terms\">Accept terms</label>\n * </div>\n * ```\n */\nconst Checkbox = React.forwardRef<React.ComponentRef<typeof CheckboxPrimitive.Root>, CheckboxProps>(\n ({ className, ...props }, ref) => (\n <CheckboxPrimitive.Root\n ref={ref}\n className={cn(\n 'peer relative h-5 w-5 shrink-0 rounded-[5px] border border-neutral-300 transition-colors',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-500 focus-visible:ring-offset-2',\n 'disabled:cursor-not-allowed disabled:bg-neutral-100 disabled:opacity-50',\n 'data-[state=checked]:border-neutral-500 data-[state=checked]:bg-neutral-500',\n 'hover:border-neutral-500 disabled:hover:border-neutral-300 data-[state=checked]:hover:border-neutral-500',\n className,\n )}\n {...props}\n >\n <CheckboxPrimitive.Indicator\n className={cn(\n 'absolute inset-0 flex items-center justify-center',\n 'data-[state=checked]:animate-in data-[state=unchecked]:animate-out',\n 'data-[state=checked]:fade-in-0 data-[state=unchecked]:fade-out-0',\n )}\n >\n <Check className=\"h-3.5 w-3.5 text-white\" />\n </CheckboxPrimitive.Indicator>\n </CheckboxPrimitive.Root>\n ),\n)\n\nCheckbox.displayName = 'Checkbox'\n\nexport type { CheckboxProps }\nexport { Checkbox }\n"],"names":[],"mappings":";;;;;;AA6CA,MAAM,WAAW,KAAM,CAAA,UAAA;AAAA,EACrB,CAAC,EAAE,SAAA,EAAW,GAAG,KAAA,IAAS,GACxB,qBAAA,GAAA;AAAA,IAAC,iBAAkB,CAAA,IAAA;AAAA,IAAlB;AAAA,MACC,GAAA;AAAA,MACA,SAAW,EAAA,EAAA;AAAA,QACT,0FAAA;AAAA,QACA,4GAAA;AAAA,QACA,yEAAA;AAAA,QACA,6EAAA;AAAA,QACA,0GAAA;AAAA,QACA;AAAA,OACF;AAAA,MACC,GAAG,KAAA;AAAA,MAEJ,QAAA,kBAAA,GAAA;AAAA,QAAC,iBAAkB,CAAA,SAAA;AAAA,QAAlB;AAAA,UACC,SAAW,EAAA,EAAA;AAAA,YACT,mDAAA;AAAA,YACA,oEAAA;AAAA,YACA;AAAA,WACF;AAAA,UAEA,QAAA,kBAAA,GAAA,CAAC,KAAM,EAAA,EAAA,SAAA,EAAU,wBAAyB,EAAA;AAAA;AAAA;AAC5C;AAAA;AAGN;AAEA,QAAA,CAAS,WAAc,GAAA,UAAA;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Command.js","sources":["../../../src/components/Command/Command.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { type DialogProps } from '@radix-ui/react-dialog'\nimport { Command as CommandPrimitive } from 'cmdk'\n\nimport { cn } from '@/lib/utils'\nimport { Dialog, DialogContent } from '../Dialog'\nimport { MagnifyingGlass } from '@phosphor-icons/react'\n\nconst Command = React.forwardRef<\n React.
|
|
1
|
+
{"version":3,"file":"Command.js","sources":["../../../src/components/Command/Command.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { type DialogProps } from '@radix-ui/react-dialog'\nimport { Command as CommandPrimitive } from 'cmdk'\n\nimport { cn } from '@/lib/utils'\nimport { Dialog, DialogContent } from '../Dialog'\nimport { MagnifyingGlass } from '@phosphor-icons/react'\n\nconst Command = React.forwardRef<\n React.ComponentRef<typeof CommandPrimitive>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive\n ref={ref}\n className={cn('flex h-full w-full flex-col overflow-hidden rounded-md bg-white text-popover-foreground', className)}\n {...props}\n />\n))\nCommand.displayName = CommandPrimitive.displayName\n\nconst CommandDialog = ({ children, ...props }: DialogProps) => {\n return (\n <Dialog {...props}>\n <DialogContent className=\"overflow-hidden p-0\">\n <Command className=\"[&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5\">\n {children}\n </Command>\n </DialogContent>\n </Dialog>\n )\n}\n\nconst CommandInput = React.forwardRef<\n React.ComponentRef<typeof CommandPrimitive.Input>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>\n>(({ className, children, ...props }, ref) => (\n <div className=\"flex items-center border-b px-3\" cmdk-input-wrapper=\"\">\n <MagnifyingGlass className=\"mr-2 h-4 w-4 shrink-0 opacity-50\" />\n <CommandPrimitive.Input\n ref={ref}\n className={cn(\n 'placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none disabled:cursor-not-allowed disabled:opacity-50',\n className,\n )}\n {...props}\n />\n {children && <div className=\"ml-2\">{children}</div>}\n </div>\n))\n\nCommandInput.displayName = CommandPrimitive.Input.displayName\n\nconst CommandList = React.forwardRef<\n React.ComponentRef<typeof CommandPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.List\n ref={ref}\n className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}\n {...props}\n />\n))\n\nCommandList.displayName = CommandPrimitive.List.displayName\n\nconst CommandEmpty = React.forwardRef<\n React.ComponentRef<typeof CommandPrimitive.Empty>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>\n>((props, ref) => <CommandPrimitive.Empty ref={ref} className=\"py-6 text-center text-sm\" {...props} />)\n\nCommandEmpty.displayName = CommandPrimitive.Empty.displayName\n\nconst CommandGroup = React.forwardRef<\n React.ComponentRef<typeof CommandPrimitive.Group>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Group\n ref={ref}\n className={cn(\n '[&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium',\n className,\n )}\n {...props}\n />\n))\n\nCommandGroup.displayName = CommandPrimitive.Group.displayName\n\nconst CommandSeparator = React.forwardRef<\n React.ComponentRef<typeof CommandPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Separator ref={ref} className={cn('-mx-1 h-px bg-border', className)} {...props} />\n))\nCommandSeparator.displayName = CommandPrimitive.Separator.displayName\n\nconst CommandItem = React.forwardRef<\n React.ComponentRef<typeof CommandPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>\n>(({ className, ...props }, ref) => (\n <CommandPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-neutral-50 data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',\n className,\n )}\n {...props}\n />\n))\n\nCommandItem.displayName = CommandPrimitive.Item.displayName\n\nconst CommandShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {\n return <span className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)} {...props} />\n}\nCommandShortcut.displayName = 'CommandShortcut'\n\nexport {\n Command,\n CommandDialog,\n CommandInput,\n CommandList,\n CommandEmpty,\n CommandGroup,\n CommandItem,\n CommandShortcut,\n CommandSeparator,\n}\n"],"names":[],"mappings":";;;;;;;;AAUM;AAIJ;AAAC;AAAA;AACC;AACkH;AAC9G;AACN;AAEF;AAEA;AACE;AASF;AAEA;AAKI;AAA8D;AAC9D;AAAkB;AAAjB;AACC;AACW;AACT;AACA;AACF;AACI;AAAA;AACN;AAC6C;AAIjD;AAEM;AAIJ;AAAkB;AAAjB;AACC;AAC0E;AACtE;AACN;AAGF;AAEA;AAKA;AAEM;AAIJ;AAAkB;AAAjB;AACC;AACW;AACT;AACA;AACF;AACI;AACN;AAGF;AAEM;AAMN;AAEM;AAIJ;AAAkB;AAAjB;AACC;AACW;AACT;AACA;AACF;AACI;AACN;AAGF;AAEA;AACE;AACF;AACA;;"}
|
|
@@ -46,14 +46,23 @@ export type DataTableProps<TData> = {
|
|
|
46
46
|
readonly data: Array<TData>;
|
|
47
47
|
/** Whether to show the column visibility toggle menu */
|
|
48
48
|
readonly showColumnVisibilityControls?: boolean;
|
|
49
|
-
/** Custom text for the column visibility button */
|
|
50
|
-
readonly columnVisibilityButtonLabel?: string;
|
|
51
49
|
/** Whether the table is in a loading state */
|
|
52
50
|
readonly isLoading?: boolean;
|
|
53
51
|
/** Pagination configuration. If not provided, pagination is disabled */
|
|
54
52
|
readonly pagination?: BasePaginationProps | BackendPaginationProps;
|
|
55
|
-
/**
|
|
56
|
-
readonly
|
|
53
|
+
/** Primary filters that appear directly above the table */
|
|
54
|
+
readonly primaryFilters?: React.ReactNode;
|
|
55
|
+
/** Secondary filters that appear in the filters dropdown */
|
|
56
|
+
readonly secondaryFilters?: React.ReactNode;
|
|
57
|
+
/** Text customization for filters */
|
|
58
|
+
readonly customLabels?: {
|
|
59
|
+
/** Text for the column visibility button (default: "Hide columns") */
|
|
60
|
+
columnVisibilityButton?: string;
|
|
61
|
+
/** Text for the filters button when only secondary filters are present (default: "Filters") */
|
|
62
|
+
filters?: string;
|
|
63
|
+
/** Text for the more filters button when both primary and secondary filters are present (default: "More filters") */
|
|
64
|
+
moreFilters?: string;
|
|
65
|
+
};
|
|
57
66
|
/** Callback when all rows are selected */
|
|
58
67
|
readonly onSelectAll?: (selected: boolean) => void;
|
|
59
68
|
/** Callback when a row is selected */
|
|
@@ -106,5 +115,5 @@ type RowIdentifierFn<T> = (row: T) => string;
|
|
|
106
115
|
* <DataTable columns={columns} data={data} />
|
|
107
116
|
* ```
|
|
108
117
|
*/
|
|
109
|
-
export declare function DataTable<TData extends object = any>({ columns: userColumns, data, getRowId, showColumnVisibilityControls,
|
|
118
|
+
export declare function DataTable<TData extends object = any>({ columns: userColumns, data, getRowId, showColumnVisibilityControls, isLoading, pagination, primaryFilters, secondaryFilters, customLabels, onSelectAll, onSelect, className, tableClassName, }: DataTableProps<TData>): import("react/jsx-runtime").JSX.Element;
|
|
110
119
|
export {};
|
|
@@ -19,12 +19,17 @@ function DataTable({
|
|
|
19
19
|
data,
|
|
20
20
|
getRowId = (row) => row?.id,
|
|
21
21
|
showColumnVisibilityControls = true,
|
|
22
|
-
columnVisibilityButtonLabel = "Hide columns",
|
|
23
22
|
isLoading = false,
|
|
24
23
|
pagination,
|
|
24
|
+
primaryFilters,
|
|
25
|
+
secondaryFilters,
|
|
26
|
+
customLabels = {
|
|
27
|
+
columnVisibilityButton: "Hide columns",
|
|
28
|
+
filters: "Filters",
|
|
29
|
+
moreFilters: "More filters"
|
|
30
|
+
},
|
|
25
31
|
onSelectAll,
|
|
26
32
|
onSelect,
|
|
27
|
-
Filters,
|
|
28
33
|
className,
|
|
29
34
|
tableClassName
|
|
30
35
|
}) {
|
|
@@ -149,40 +154,45 @@ function DataTable({
|
|
|
149
154
|
});
|
|
150
155
|
};
|
|
151
156
|
return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 w-full flex-1 flex-col gap-2 overflow-hidden", className), children: [
|
|
152
|
-
(showColumnVisibilityControls ||
|
|
153
|
-
!isMobile && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children:
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
onSelect: (event) => event.preventDefault(),
|
|
167
|
-
onCheckedChange: (value) => table.getAllColumns().forEach((column) => column.toggleVisibility(!!value)),
|
|
168
|
-
children: "Select all"
|
|
169
|
-
},
|
|
170
|
-
"all-columns"
|
|
171
|
-
),
|
|
172
|
-
/* @__PURE__ */ jsx(DropdownMenuSeparator, { className: "bg-neutral-100" }),
|
|
173
|
-
table.getAllColumns().filter((column) => column.getCanHide()).map((column) => {
|
|
174
|
-
return /* @__PURE__ */ jsx(
|
|
157
|
+
(showColumnVisibilityControls || primaryFilters || secondaryFilters) && /* @__PURE__ */ jsxs("div", { className: "flex flex-shrink-0 items-end justify-between p-1", children: [
|
|
158
|
+
!isMobile && (primaryFilters ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: primaryFilters }) : /* @__PURE__ */ jsx("div", {})),
|
|
159
|
+
/* @__PURE__ */ jsxs("div", { className: cn("flex items-center gap-2", isMobile ? "w-full justify-end" : ""), children: [
|
|
160
|
+
(isMobile ? primaryFilters || secondaryFilters : secondaryFilters) && /* @__PURE__ */ jsxs(PopoverRoot, { children: [
|
|
161
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", StartIcon: FunnelSimple, children: !primaryFilters || isMobile ? customLabels.filters : customLabels.moreFilters }) }),
|
|
162
|
+
/* @__PURE__ */ jsx(PopoverContent, { align: "center", className: "w-fit p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
163
|
+
isMobile && primaryFilters && primaryFilters,
|
|
164
|
+
secondaryFilters
|
|
165
|
+
] }) })
|
|
166
|
+
] }),
|
|
167
|
+
showColumnVisibilityControls && /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
168
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "text", size: "sm", className: cn("whitespace-nowrap"), StartIcon: TextColumns, children: customLabels.columnVisibilityButton ?? "Hide columns" }) }),
|
|
169
|
+
/* @__PURE__ */ jsxs(DropdownMenuContent, { children: [
|
|
170
|
+
/* @__PURE__ */ jsx(
|
|
175
171
|
DropdownMenuCheckboxItem,
|
|
176
172
|
{
|
|
177
173
|
className: "capitalize",
|
|
178
|
-
checked: column.getIsVisible(),
|
|
174
|
+
checked: table.getAllColumns().every((column) => column.getIsVisible()),
|
|
179
175
|
onSelect: (event) => event.preventDefault(),
|
|
180
|
-
onCheckedChange: (value) => column.toggleVisibility(!!value),
|
|
181
|
-
children:
|
|
176
|
+
onCheckedChange: (value) => table.getAllColumns().forEach((column) => column.toggleVisibility(!!value)),
|
|
177
|
+
children: "Select all"
|
|
182
178
|
},
|
|
183
|
-
|
|
184
|
-
)
|
|
185
|
-
|
|
179
|
+
"all-columns"
|
|
180
|
+
),
|
|
181
|
+
/* @__PURE__ */ jsx(DropdownMenuSeparator, { className: "bg-neutral-100" }),
|
|
182
|
+
table.getAllColumns().filter((column) => column.getCanHide()).map((column) => {
|
|
183
|
+
return /* @__PURE__ */ jsx(
|
|
184
|
+
DropdownMenuCheckboxItem,
|
|
185
|
+
{
|
|
186
|
+
className: "capitalize",
|
|
187
|
+
checked: column.getIsVisible(),
|
|
188
|
+
onSelect: (event) => event.preventDefault(),
|
|
189
|
+
onCheckedChange: (value) => column.toggleVisibility(!!value),
|
|
190
|
+
children: column.columnDef.header?.toString()
|
|
191
|
+
},
|
|
192
|
+
column.id
|
|
193
|
+
);
|
|
194
|
+
})
|
|
195
|
+
] })
|
|
186
196
|
] })
|
|
187
197
|
] })
|
|
188
198
|
] }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["'use client'\n\nimport {\n ColumnDef,\n flexRender,\n getCoreRowModel,\n getPaginationRowModel,\n useReactTable,\n VisibilityState,\n} from '@tanstack/react-table'\nimport * as React from 'react'\n\nimport { Button } from '../Button'\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../DropdownMenu'\nimport { Skeleton } from '../Skeleton'\nimport { Table as TableComponent, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../Table'\nimport { DataTablePagination } from './DataTablePagination'\nimport { Checkbox } from '../Checkbox'\nimport { TextColumns, FunnelSimple } from '@phosphor-icons/react'\nimport { cn } from '@/lib/utils'\nimport { Typography } from '../Typography'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { useIsMobile } from '@/lib/useMobile'\n\ntype BasePaginationProps = {\n /** Number of rows per page */\n readonly pageSize: number\n /** Whether the pagination is in a loading state */\n readonly isLoading?: boolean\n /** Text customization for pagination */\n readonly labels?: {\n /** Text shown before the page size number (default: \"Showing\") */\n showing?: string\n /** Text shown before the total number (default: \"of\") */\n of?: string\n /** Text shown after the total number (default: \"results\") */\n results?: string\n /** Aria label for previous page button (default: \"Previous page\") */\n previousPage?: string\n /** Aria label for next page button (default: \"Next page\") */\n nextPage?: string\n /** Aria label for page number (default: \"Page {number}\") */\n pageLabel?: string\n }\n}\n\ntype BackendPaginationProps = BasePaginationProps & {\n /** Current page */\n readonly currentPage: number\n /** Total number of items */\n readonly total: number\n /** Callback when page changes */\n readonly onPageChange: (page: number) => void\n}\n\n/**\n * Type helper to check if a type has an 'id' property\n */\ntype HasId<T> = T extends { id: string | number } ? true : false\n\n/**\n * Props for the DataTable component\n * @template TData The type of data being displayed in the table\n */\nexport type DataTableProps<TData> = {\n /** Array of column definitions that describe the table structure */\n readonly columns: Array<ColumnDef<TData>>\n /** Array of data items to be displayed in the table */\n readonly data: Array<TData>\n /** Whether to show the column visibility toggle menu */\n readonly showColumnVisibilityControls?: boolean\n /** Custom text for the column visibility button */\n readonly columnVisibilityButtonLabel?: string\n /** Whether the table is in a loading state */\n readonly isLoading?: boolean\n /** Pagination configuration. If not provided, pagination is disabled */\n readonly pagination?: BasePaginationProps | BackendPaginationProps\n /** Optional component to render filters */\n readonly Filters?: React.ReactNode\n /** Callback when all rows are selected */\n readonly onSelectAll?: (selected: boolean) => void\n /** Callback when a row is selected */\n readonly onSelect?: (selected: boolean, row: TData) => void\n /** Optional className for the table container */\n readonly className?: string\n /** Optional className for the table */\n readonly tableClassName?: string\n} & (HasId<TData> extends true\n ? {\n /** Function to get unique identifier from a row. Not needed when data has 'id' property */\n readonly getRowId?: never\n }\n : {\n /** Function to get unique identifier from a row. Required when data doesn't have 'id' property */\n readonly getRowId: RowIdentifierFn<TData>\n })\n\n/**\n * Function to get a unique identifier from a row\n */\ntype RowIdentifierFn<T> = (row: T) => string\n\n/**\n * A feature-rich data table component built on top of TanStack Table.\n * Provides sorting, filtering, pagination, and column visibility controls.\n *\n * @template TData The type of data being displayed in the table\n *\n * @example\n * ```tsx\n * type User = {\n * id: string;\n * name: string;\n * email: string;\n * };\n *\n * const columns: ColumnDef<User>[] = [\n * {\n * accessorKey: 'name',\n * header: 'Name',\n * },\n * {\n * accessorKey: 'email',\n * header: 'Email',\n * },\n * ];\n *\n * const data: User[] = [\n * { id: '1', name: 'John', email: 'john@example.com' },\n * { id: '2', name: 'Jane', email: 'jane@example.com' },\n * ];\n *\n * <DataTable columns={columns} data={data} />\n * ```\n */\nexport function DataTable<TData extends object = any>({\n columns: userColumns,\n data,\n getRowId = (row: TData) => (row as { id: string })?.id,\n showColumnVisibilityControls = true,\n columnVisibilityButtonLabel = 'Hide columns',\n isLoading = false,\n pagination,\n onSelectAll,\n onSelect,\n Filters,\n className,\n tableClassName,\n}: DataTableProps<TData>) {\n const isMobile = useIsMobile()\n const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})\n const [isAllRowsSelected, setIsAllRowsSelected] = React.useState(false)\n const [deselectedRows, setDeselectedRows] = React.useState<Record<string, boolean>>({})\n const [selectedRows, setSelectedRows] = React.useState<Record<string, boolean>>({})\n const [pageIndex, setPageIndex] = React.useState(0)\n\n const isBackendPagination = pagination && 'onPageChange' in pagination\n const total = isBackendPagination ? pagination.total : data.length\n const pageSize = pagination?.pageSize ?? data.length\n const totalPages = Math.ceil(total / pageSize)\n\n const isSelectable = typeof onSelectAll === 'function' || typeof onSelect === 'function'\n\n const handleSelectAll = (checked: boolean) => {\n setIsAllRowsSelected(checked)\n setDeselectedRows({})\n setSelectedRows({})\n onSelectAll?.(checked)\n }\n\n const handleRowSelect = (checked: boolean, rowData: TData) => {\n const rowId = (getRowId as (row: TData) => string)(rowData)\n\n if (isAllRowsSelected) {\n setDeselectedRows((prev) => {\n const newDeselections = { ...prev }\n if (!checked) {\n newDeselections[rowId] = true\n } else {\n delete newDeselections[rowId]\n }\n return newDeselections\n })\n } else {\n setSelectedRows((prev) => {\n const newSelection = { ...prev }\n if (checked) {\n newSelection[rowId] = true\n } else {\n delete newSelection[rowId]\n }\n return newSelection\n })\n }\n\n onSelect?.(checked, rowData)\n }\n\n const rowSelection = React.useMemo(() => {\n if (isAllRowsSelected) {\n return Object.fromEntries(\n data.map((row) => [\n (getRowId as (row: TData) => string)(row),\n !deselectedRows[(getRowId as (row: TData) => string)(row)],\n ]),\n )\n }\n return selectedRows\n }, [data, isAllRowsSelected, deselectedRows, selectedRows, getRowId])\n\n const table = useReactTable({\n data,\n columns: userColumns,\n getCoreRowModel: getCoreRowModel(),\n getPaginationRowModel: pagination && !isBackendPagination ? getPaginationRowModel() : undefined,\n onColumnVisibilityChange: setColumnVisibility,\n enableRowSelection: isSelectable,\n getRowId: getRowId,\n state: {\n columnVisibility,\n rowSelection,\n pagination: pagination\n ? {\n pageIndex: isBackendPagination ? pagination.currentPage - 1 : pageIndex,\n pageSize,\n }\n : undefined,\n },\n manualPagination: isBackendPagination,\n onPaginationChange: isBackendPagination\n ? undefined\n : (updater) => {\n if (typeof updater === 'function') {\n const newState = updater({ pageIndex, pageSize })\n setPageIndex(newState.pageIndex)\n }\n },\n })\n\n const renderTableBody = () => {\n if (isLoading) {\n return Array.from({ length: pageSize ?? 10 }).map((_, rowIndex) => (\n <TableRow key={`skeleton-row-${rowIndex.toString()}`}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox checked={false} disabled />\n </TableCell>\n )}\n {table\n .getAllColumns()\n .filter((column) => column.getIsVisible())\n .map((column) => (\n <TableCell\n key={`skeleton-cell-${rowIndex.toString()}-${column.id}`}\n style={{ width: column.columnDef.size }}\n >\n <div className={cn('flex items-center justify-center', column.id === 'actions' && 'justify-end')}>\n <Skeleton className={cn(column.id === 'actions' ? 'h-8 w-8' : 'h-[20px] w-full')} />\n </div>\n </TableCell>\n ))}\n </TableRow>\n ))\n }\n\n if (data.length === 0) {\n return (\n <TableRow>\n <TableCell\n colSpan={isSelectable ? table.getAllColumns().length + 1 : table.getAllColumns().length}\n className=\"h-[200px] text-center\"\n >\n <Typography color=\"neutral\">No data available</Typography>\n </TableCell>\n </TableRow>\n )\n }\n\n return table.getRowModel().rows.map((row) => {\n const rowId = (getRowId as (row: TData) => string)(row.original)\n const isSelected = rowSelection[rowId] ?? false\n\n return (\n <TableRow key={rowId} data-selected={isSelected}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox\n checked={isSelected}\n onCheckedChange={(checked) => {\n const isChecked = checked === true\n handleRowSelect(isChecked, row.original)\n }}\n aria-label={`Select row ${rowId}`}\n />\n </TableCell>\n )}\n {row.getVisibleCells().map((cell) => (\n <TableCell key={cell.id} style={{ width: cell.column.columnDef.size }}>\n <Typography>{flexRender(cell.column.columnDef.cell, cell.getContext())}</Typography>\n </TableCell>\n ))}\n </TableRow>\n )\n })\n }\n\n return (\n <div className={cn('flex h-full min-h-0 w-full flex-1 flex-col gap-2 overflow-hidden', className)}>\n {(showColumnVisibilityControls || Filters) && (\n <div className=\"flex flex-shrink-0 items-end justify-between p-1\">\n {!isMobile && <div className=\"flex items-center gap-2\">{Filters}</div>}\n {isMobile && (\n <div className=\"flex w-full justify-end\">\n {Filters && (\n <PopoverRoot>\n <PopoverTrigger asChild>\n <Button variant=\"text\" size=\"sm\" StartIcon={FunnelSimple}>\n Filters\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"center\" className=\"w-fit p-4\">\n {Filters}\n </PopoverContent>\n </PopoverRoot>\n )}\n </div>\n )}\n {showColumnVisibilityControls && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"text\" size=\"sm\" className={cn('whitespace-nowrap')} StartIcon={TextColumns}>\n {columnVisibilityButtonLabel}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuCheckboxItem\n key={'all-columns'}\n className=\"capitalize\"\n checked={table.getAllColumns().every((column) => column.getIsVisible())}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) =>\n table.getAllColumns().forEach((column) => column.toggleVisibility(!!value))\n }\n >\n Select all\n </DropdownMenuCheckboxItem>\n <DropdownMenuSeparator className=\"bg-neutral-100\" />\n {table\n .getAllColumns()\n .filter((column) => column.getCanHide())\n .map((column) => {\n return (\n <DropdownMenuCheckboxItem\n key={column.id}\n className=\"capitalize\"\n checked={column.getIsVisible()}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) => column.toggleVisibility(!!value)}\n >\n {column.columnDef.header?.toString()}\n </DropdownMenuCheckboxItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n )}\n\n <div className=\"flex min-h-0 flex-1 flex-col rounded-md border bg-white\">\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <div className=\"h-full overflow-auto\">\n <TableComponent className=\"w-full\" tableClassName={cn('table-fixed', tableClassName)}>\n <TableHeader className=\"sticky top-0 z-10 bg-neutral-50\">\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {isSelectable && (\n <TableHead className=\"w-[50px]\">\n <Checkbox\n checked={isAllRowsSelected}\n onCheckedChange={handleSelectAll}\n disabled={isLoading || !data.length}\n aria-label=\"Select all rows\"\n />\n </TableHead>\n )}\n {headerGroup.headers.map((header) => (\n <TableHead\n key={header.id}\n className=\"whitespace-normal\"\n style={{ width: header.column.columnDef.size }}\n >\n <Typography weight=\"medium\">\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n </Typography>\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>{renderTableBody()}</TableBody>\n </TableComponent>\n </div>\n </div>\n {!!pagination && (\n <div className=\"border-t px-4 py-2\">\n <DataTablePagination\n table={table}\n total={total}\n pageSize={pageSize}\n currentPage={\n isBackendPagination ? pagination?.currentPage : table.getState().pagination?.pageIndex + 1 || 1\n }\n totalPages={totalPages}\n onPageChange={isBackendPagination ? pagination?.onPageChange : undefined}\n isLoading={pagination?.isLoading}\n labels={pagination?.labels}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AA6IO;AAA+C;AAC3C;AACT;AACoD;AACrB;AACD;AAClB;AACZ;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACE;AACA;AACA;AACA;AAAqB;AAGvB;AACE;AAEA;AACE;AACE;AACA;AACE;AAAyB;AAEzB;AAA4B;AAE9B;AAAO;AACR;AAED;AACE;AACA;AACE;AAAsB;AAEtB;AAAyB;AAE3B;AAAO;AACR;AAGH;AAA2B;AAG7B;AACE;AACE;AAAc;AACM;AACwB;AACiB;AAC1D;AACH;AAEF;AAAO;AAGT;AAA4B;AAC1B;AACS;AACwB;AACqD;AAC5D;AACN;AACpB;AACO;AACL;AACA;AAEI;AACgE;AAC9D;AAEF;AACN;AACkB;AAIZ;AACE;AACA;AAA+B;AACjC;AACF;AAGN;AACE;AACE;AAEK;AAGC;AAME;AAAC;AAAA;AAEuC;AAItC;AAAA;AALsD;AAOzD;AAEN;AAGH;AACE;AAEI;AAAC;AAAA;AACkF;AACvE;AAEmC;AAAA;AAEjD;AAIJ;AACE;AACA;AAEA;AAEK;AAEG;AAAC;AAAA;AACU;AAEP;AACA;AAAuC;AACzC;AAC+B;AAAA;AAEnC;AAMD;AACH;AAEH;AAGH;AAEM;AAEG;AAA+D;AAKxD;AAIA;AAGA;AAGN;AAIE;AAIA;AAEE;AAAA;AAAC;AAAA;AAEW;AAC4D;AAC5B;AAEkC;AAE7E;AAAA;AAPM;AASP;AACkD;AAK9C;AACE;AAAC;AAAA;AAEW;AACmB;AACa;AACiB;AAExB;AAAA;AANvB;AAOd;AAEH;AACL;AACF;AAEJ;AAIA;AAGM;AAGO;AAEG;AAAC;AAAA;AACU;AACQ;AACY;AAClB;AAAA;AAEf;AAGA;AAAC;AAAA;AAEW;AACmC;AAM7C;AAAA;AARY;AAUf;AAGP;AAC8B;AAGpC;AAGI;AAAC;AAAA;AACC;AACA;AACA;AAEgG;AAEhG;AAC+D;AACxC;AACH;AAAA;AAExB;AAEJ;AAGN;;"}
|
|
1
|
+
{"version":3,"file":"DataTable.js","sources":["../../../src/components/DataTable/DataTable.tsx"],"sourcesContent":["'use client'\n\nimport {\n ColumnDef,\n flexRender,\n getCoreRowModel,\n getPaginationRowModel,\n useReactTable,\n VisibilityState,\n} from '@tanstack/react-table'\nimport * as React from 'react'\n\nimport { Button } from '../Button'\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuSeparator,\n DropdownMenuTrigger,\n} from '../DropdownMenu'\nimport { Skeleton } from '../Skeleton'\nimport { Table as TableComponent, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../Table'\nimport { DataTablePagination } from './DataTablePagination'\nimport { Checkbox } from '../Checkbox'\nimport { TextColumns, FunnelSimple } from '@phosphor-icons/react'\nimport { cn } from '@/lib/utils'\nimport { Typography } from '../Typography'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { useIsMobile } from '@/lib/useMobile'\n\ntype BasePaginationProps = {\n /** Number of rows per page */\n readonly pageSize: number\n /** Whether the pagination is in a loading state */\n readonly isLoading?: boolean\n /** Text customization for pagination */\n readonly labels?: {\n /** Text shown before the page size number (default: \"Showing\") */\n showing?: string\n /** Text shown before the total number (default: \"of\") */\n of?: string\n /** Text shown after the total number (default: \"results\") */\n results?: string\n /** Aria label for previous page button (default: \"Previous page\") */\n previousPage?: string\n /** Aria label for next page button (default: \"Next page\") */\n nextPage?: string\n /** Aria label for page number (default: \"Page {number}\") */\n pageLabel?: string\n }\n}\n\ntype BackendPaginationProps = BasePaginationProps & {\n /** Current page */\n readonly currentPage: number\n /** Total number of items */\n readonly total: number\n /** Callback when page changes */\n readonly onPageChange: (page: number) => void\n}\n\n/**\n * Type helper to check if a type has an 'id' property\n */\ntype HasId<T> = T extends { id: string | number } ? true : false\n\n/**\n * Props for the DataTable component\n * @template TData The type of data being displayed in the table\n */\nexport type DataTableProps<TData> = {\n /** Array of column definitions that describe the table structure */\n readonly columns: Array<ColumnDef<TData>>\n /** Array of data items to be displayed in the table */\n readonly data: Array<TData>\n /** Whether to show the column visibility toggle menu */\n readonly showColumnVisibilityControls?: boolean\n /** Whether the table is in a loading state */\n readonly isLoading?: boolean\n /** Pagination configuration. If not provided, pagination is disabled */\n readonly pagination?: BasePaginationProps | BackendPaginationProps\n /** Primary filters that appear directly above the table */\n readonly primaryFilters?: React.ReactNode\n /** Secondary filters that appear in the filters dropdown */\n readonly secondaryFilters?: React.ReactNode\n /** Text customization for filters */\n readonly customLabels?: {\n /** Text for the column visibility button (default: \"Hide columns\") */\n columnVisibilityButton?: string\n /** Text for the filters button when only secondary filters are present (default: \"Filters\") */\n filters?: string\n /** Text for the more filters button when both primary and secondary filters are present (default: \"More filters\") */\n moreFilters?: string\n }\n /** Callback when all rows are selected */\n readonly onSelectAll?: (selected: boolean) => void\n /** Callback when a row is selected */\n readonly onSelect?: (selected: boolean, row: TData) => void\n /** Optional className for the table container */\n readonly className?: string\n /** Optional className for the table */\n readonly tableClassName?: string\n} & (HasId<TData> extends true\n ? {\n /** Function to get unique identifier from a row. Not needed when data has 'id' property */\n readonly getRowId?: never\n }\n : {\n /** Function to get unique identifier from a row. Required when data doesn't have 'id' property */\n readonly getRowId: RowIdentifierFn<TData>\n })\n\n/**\n * Function to get a unique identifier from a row\n */\ntype RowIdentifierFn<T> = (row: T) => string\n\n/**\n * A feature-rich data table component built on top of TanStack Table.\n * Provides sorting, filtering, pagination, and column visibility controls.\n *\n * @template TData The type of data being displayed in the table\n *\n * @example\n * ```tsx\n * type User = {\n * id: string;\n * name: string;\n * email: string;\n * };\n *\n * const columns: ColumnDef<User>[] = [\n * {\n * accessorKey: 'name',\n * header: 'Name',\n * },\n * {\n * accessorKey: 'email',\n * header: 'Email',\n * },\n * ];\n *\n * const data: User[] = [\n * { id: '1', name: 'John', email: 'john@example.com' },\n * { id: '2', name: 'Jane', email: 'jane@example.com' },\n * ];\n *\n * <DataTable columns={columns} data={data} />\n * ```\n */\nexport function DataTable<TData extends object = any>({\n columns: userColumns,\n data,\n getRowId = (row: TData) => (row as { id: string })?.id,\n showColumnVisibilityControls = true,\n isLoading = false,\n pagination,\n primaryFilters,\n secondaryFilters,\n customLabels = {\n columnVisibilityButton: 'Hide columns',\n filters: 'Filters',\n moreFilters: 'More filters',\n },\n onSelectAll,\n onSelect,\n className,\n tableClassName,\n}: DataTableProps<TData>) {\n const isMobile = useIsMobile()\n const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({})\n const [isAllRowsSelected, setIsAllRowsSelected] = React.useState(false)\n const [deselectedRows, setDeselectedRows] = React.useState<Record<string, boolean>>({})\n const [selectedRows, setSelectedRows] = React.useState<Record<string, boolean>>({})\n const [pageIndex, setPageIndex] = React.useState(0)\n\n const isBackendPagination = pagination && 'onPageChange' in pagination\n const total = isBackendPagination ? pagination.total : data.length\n const pageSize = pagination?.pageSize ?? data.length\n const totalPages = Math.ceil(total / pageSize)\n\n const isSelectable = typeof onSelectAll === 'function' || typeof onSelect === 'function'\n\n const handleSelectAll = (checked: boolean) => {\n setIsAllRowsSelected(checked)\n setDeselectedRows({})\n setSelectedRows({})\n onSelectAll?.(checked)\n }\n\n const handleRowSelect = (checked: boolean, rowData: TData) => {\n const rowId = (getRowId as (row: TData) => string)(rowData)\n\n if (isAllRowsSelected) {\n setDeselectedRows((prev) => {\n const newDeselections = { ...prev }\n if (!checked) {\n newDeselections[rowId] = true\n } else {\n delete newDeselections[rowId]\n }\n return newDeselections\n })\n } else {\n setSelectedRows((prev) => {\n const newSelection = { ...prev }\n if (checked) {\n newSelection[rowId] = true\n } else {\n delete newSelection[rowId]\n }\n return newSelection\n })\n }\n\n onSelect?.(checked, rowData)\n }\n\n const rowSelection = React.useMemo(() => {\n if (isAllRowsSelected) {\n return Object.fromEntries(\n data.map((row) => [\n (getRowId as (row: TData) => string)(row),\n !deselectedRows[(getRowId as (row: TData) => string)(row)],\n ]),\n )\n }\n return selectedRows\n }, [data, isAllRowsSelected, deselectedRows, selectedRows, getRowId])\n\n const table = useReactTable({\n data,\n columns: userColumns,\n getCoreRowModel: getCoreRowModel(),\n getPaginationRowModel: pagination && !isBackendPagination ? getPaginationRowModel() : undefined,\n onColumnVisibilityChange: setColumnVisibility,\n enableRowSelection: isSelectable,\n getRowId: getRowId,\n state: {\n columnVisibility,\n rowSelection,\n pagination: pagination\n ? {\n pageIndex: isBackendPagination ? pagination.currentPage - 1 : pageIndex,\n pageSize,\n }\n : undefined,\n },\n manualPagination: isBackendPagination,\n onPaginationChange: isBackendPagination\n ? undefined\n : (updater) => {\n if (typeof updater === 'function') {\n const newState = updater({ pageIndex, pageSize })\n setPageIndex(newState.pageIndex)\n }\n },\n })\n\n const renderTableBody = () => {\n if (isLoading) {\n return Array.from({ length: pageSize ?? 10 }).map((_, rowIndex) => (\n <TableRow key={`skeleton-row-${rowIndex.toString()}`}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox checked={false} disabled />\n </TableCell>\n )}\n {table\n .getAllColumns()\n .filter((column) => column.getIsVisible())\n .map((column) => (\n <TableCell\n key={`skeleton-cell-${rowIndex.toString()}-${column.id}`}\n style={{ width: column.columnDef.size }}\n >\n <div className={cn('flex items-center justify-center', column.id === 'actions' && 'justify-end')}>\n <Skeleton className={cn(column.id === 'actions' ? 'h-8 w-8' : 'h-[20px] w-full')} />\n </div>\n </TableCell>\n ))}\n </TableRow>\n ))\n }\n\n if (data.length === 0) {\n return (\n <TableRow>\n <TableCell\n colSpan={isSelectable ? table.getAllColumns().length + 1 : table.getAllColumns().length}\n className=\"h-[200px] text-center\"\n >\n <Typography color=\"neutral\">No data available</Typography>\n </TableCell>\n </TableRow>\n )\n }\n\n return table.getRowModel().rows.map((row) => {\n const rowId = (getRowId as (row: TData) => string)(row.original)\n const isSelected = rowSelection[rowId] ?? false\n\n return (\n <TableRow key={rowId} data-selected={isSelected}>\n {isSelectable && (\n <TableCell className=\"w-[50px]\">\n <Checkbox\n checked={isSelected}\n onCheckedChange={(checked) => {\n const isChecked = checked === true\n handleRowSelect(isChecked, row.original)\n }}\n aria-label={`Select row ${rowId}`}\n />\n </TableCell>\n )}\n {row.getVisibleCells().map((cell) => (\n <TableCell key={cell.id} style={{ width: cell.column.columnDef.size }}>\n <Typography>{flexRender(cell.column.columnDef.cell, cell.getContext())}</Typography>\n </TableCell>\n ))}\n </TableRow>\n )\n })\n }\n\n return (\n <div className={cn('flex h-full min-h-0 w-full flex-1 flex-col gap-2 overflow-hidden', className)}>\n {(showColumnVisibilityControls || primaryFilters || secondaryFilters) && (\n <div className=\"flex flex-shrink-0 items-end justify-between p-1\">\n {!isMobile && (primaryFilters ? <div className=\"flex items-center gap-2\">{primaryFilters}</div> : <div />)}\n <div className={cn('flex items-center gap-2', isMobile ? 'w-full justify-end' : '')}>\n {(isMobile ? primaryFilters || secondaryFilters : secondaryFilters) && (\n <PopoverRoot>\n <PopoverTrigger asChild>\n <Button variant=\"text\" size=\"sm\" StartIcon={FunnelSimple}>\n {!primaryFilters || isMobile ? customLabels.filters : customLabels.moreFilters}\n </Button>\n </PopoverTrigger>\n <PopoverContent align=\"center\" className=\"w-fit p-4\">\n <div className=\"flex flex-col gap-4\">\n {isMobile && primaryFilters && primaryFilters}\n {secondaryFilters}\n </div>\n </PopoverContent>\n </PopoverRoot>\n )}\n {showColumnVisibilityControls && (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"text\" size=\"sm\" className={cn('whitespace-nowrap')} StartIcon={TextColumns}>\n {customLabels.columnVisibilityButton ?? 'Hide columns'}\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n <DropdownMenuCheckboxItem\n key={'all-columns'}\n className=\"capitalize\"\n checked={table.getAllColumns().every((column) => column.getIsVisible())}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) =>\n table.getAllColumns().forEach((column) => column.toggleVisibility(!!value))\n }\n >\n Select all\n </DropdownMenuCheckboxItem>\n <DropdownMenuSeparator className=\"bg-neutral-100\" />\n {table\n .getAllColumns()\n .filter((column) => column.getCanHide())\n .map((column) => {\n return (\n <DropdownMenuCheckboxItem\n key={column.id}\n className=\"capitalize\"\n checked={column.getIsVisible()}\n onSelect={(event) => event.preventDefault()}\n onCheckedChange={(value) => column.toggleVisibility(!!value)}\n >\n {column.columnDef.header?.toString()}\n </DropdownMenuCheckboxItem>\n )\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n )}\n </div>\n </div>\n )}\n\n <div className=\"flex min-h-0 flex-1 flex-col rounded-md border bg-white\">\n <div className=\"min-h-0 flex-1 overflow-auto\">\n <div className=\"h-full overflow-auto\">\n <TableComponent className=\"w-full\" tableClassName={cn('table-fixed', tableClassName)}>\n <TableHeader className=\"sticky top-0 z-10 bg-neutral-50\">\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {isSelectable && (\n <TableHead className=\"w-[50px]\">\n <Checkbox\n checked={isAllRowsSelected}\n onCheckedChange={handleSelectAll}\n disabled={isLoading || !data.length}\n aria-label=\"Select all rows\"\n />\n </TableHead>\n )}\n {headerGroup.headers.map((header) => (\n <TableHead\n key={header.id}\n className=\"whitespace-normal\"\n style={{ width: header.column.columnDef.size }}\n >\n <Typography weight=\"medium\">\n {header.isPlaceholder\n ? null\n : flexRender(header.column.columnDef.header, header.getContext())}\n </Typography>\n </TableHead>\n ))}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>{renderTableBody()}</TableBody>\n </TableComponent>\n </div>\n </div>\n {!!pagination && (\n <div className=\"border-t px-4 py-2\">\n <DataTablePagination\n table={table}\n total={total}\n pageSize={pageSize}\n currentPage={\n isBackendPagination ? pagination?.currentPage : table.getState().pagination?.pageIndex + 1 || 1\n }\n totalPages={totalPages}\n onPageChange={isBackendPagination ? pagination?.onPageChange : undefined}\n isLoading={pagination?.isLoading}\n labels={pagination?.labels}\n />\n </div>\n )}\n </div>\n </div>\n )\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAsJO;AAA+C;AAC3C;AACT;AACoD;AACrB;AACnB;AACZ;AACA;AACA;AACe;AACW;AACf;AACI;AACf;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AACE;AACA;AACA;AACA;AAAqB;AAGvB;AACE;AAEA;AACE;AACE;AACA;AACE;AAAyB;AAEzB;AAA4B;AAE9B;AAAO;AACR;AAED;AACE;AACA;AACE;AAAsB;AAEtB;AAAyB;AAE3B;AAAO;AACR;AAGH;AAA2B;AAG7B;AACE;AACE;AAAc;AACM;AACwB;AACiB;AAC1D;AACH;AAEF;AAAO;AAGT;AAA4B;AAC1B;AACS;AACwB;AACqD;AAC5D;AACN;AACpB;AACO;AACL;AACA;AAEI;AACgE;AAC9D;AAEF;AACN;AACkB;AAIZ;AACE;AACA;AAA+B;AACjC;AACF;AAGN;AACE;AACE;AAEK;AAGC;AAME;AAAC;AAAA;AAEuC;AAItC;AAAA;AALsD;AAOzD;AAEN;AAGH;AACE;AAEI;AAAC;AAAA;AACkF;AACvE;AAEmC;AAAA;AAEjD;AAIJ;AACE;AACA;AAEA;AAEK;AAEG;AAAC;AAAA;AACU;AAEP;AACA;AAAuC;AACzC;AAC+B;AAAA;AAEnC;AAMD;AACH;AAEH;AAGH;AAEM;AAEG;AAAsG;AAEnG;AAEE;AAIA;AAGK;AAA8B;AAC9B;AAEL;AACF;AAIE;AAIA;AAEE;AAAA;AAAC;AAAA;AAEW;AAC4D;AAC5B;AAEkC;AAE7E;AAAA;AAPM;AASP;AACkD;AAK9C;AACE;AAAC;AAAA;AAEW;AACmB;AACa;AACiB;AAExB;AAAA;AANvB;AAOd;AAEH;AACL;AACF;AAEJ;AACF;AAIA;AAGM;AAGO;AAEG;AAAC;AAAA;AACU;AACQ;AACY;AAClB;AAAA;AAEf;AAGA;AAAC;AAAA;AAEW;AACmC;AAM7C;AAAA;AARY;AAUf;AAGP;AAC8B;AAGpC;AAGI;AAAC;AAAA;AACC;AACA;AACA;AAEgG;AAEhG;AAC+D;AACxC;AACH;AAAA;AAExB;AAEJ;AAGN;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DateRange,
|
|
1
|
+
import { DateRange, PropsBase } from 'react-day-picker';
|
|
2
2
|
type BaseDatePickerProps = {
|
|
3
3
|
/**
|
|
4
4
|
* Placeholder text displayed when no date is selected
|
|
@@ -10,21 +10,29 @@ type BaseDatePickerProps = {
|
|
|
10
10
|
* @default 'MMM d, yyyy'
|
|
11
11
|
*/
|
|
12
12
|
format?: string;
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* ClassName for the button
|
|
15
|
+
*/
|
|
16
|
+
buttonClassName?: string;
|
|
17
|
+
} & Omit<PropsBase, 'mode' | 'selected' | 'onSelect'>;
|
|
18
|
+
type SingleDatePickerProps = {
|
|
15
19
|
variant?: 'single';
|
|
20
|
+
value?: Date;
|
|
16
21
|
initialValue?: Date;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
onChange?: (date: Date | undefined) => void;
|
|
23
|
+
closeOnSelect?: boolean;
|
|
24
|
+
showYearSwitcher?: boolean;
|
|
25
|
+
} & BaseDatePickerProps;
|
|
26
|
+
type RangeDatePickerProps = {
|
|
21
27
|
variant: 'range';
|
|
28
|
+
value?: DateRange;
|
|
22
29
|
initialValue?: DateRange;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
30
|
+
onChange?: (date: DateRange | undefined) => void;
|
|
31
|
+
closeOnSelect?: never;
|
|
32
|
+
showYearSwitcher?: never;
|
|
33
|
+
} & BaseDatePickerProps;
|
|
26
34
|
export type DatePickerProps = SingleDatePickerProps | RangeDatePickerProps;
|
|
27
|
-
declare function DatePicker(
|
|
35
|
+
declare function DatePicker(props: DatePickerProps): import("react/jsx-runtime").JSX.Element;
|
|
28
36
|
declare namespace DatePicker {
|
|
29
37
|
var displayName: string;
|
|
30
38
|
}
|
|
@@ -4,43 +4,58 @@ import * as React from 'react';
|
|
|
4
4
|
import { format } from 'date-fns';
|
|
5
5
|
import { cn } from '../../lib/utils.js';
|
|
6
6
|
import { PopoverRoot, PopoverTrigger, PopoverContent } from '../Popover/Popover.js';
|
|
7
|
-
import { Button } from '../Button/Button.js';
|
|
7
|
+
import { Button, buttonVariants } from '../Button/Button.js';
|
|
8
8
|
import { Calendar } from '../Calendar/Calendar.js';
|
|
9
9
|
import { CalendarBlank } from '@phosphor-icons/react';
|
|
10
10
|
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
function isDateRange(value) {
|
|
12
|
+
return typeof value === "object" && value !== null && "from" in value;
|
|
13
|
+
}
|
|
14
|
+
function DatePicker(props) {
|
|
15
|
+
const {
|
|
16
|
+
variant = "single",
|
|
17
|
+
placeholder = variant === "single" ? "Pick a date" : "Pick a date range",
|
|
18
|
+
initialValue,
|
|
19
|
+
value,
|
|
20
|
+
onChange,
|
|
21
|
+
buttonClassName,
|
|
22
|
+
closeOnSelect = variant === "single",
|
|
23
|
+
showYearSwitcher = variant === "single",
|
|
24
|
+
...rest
|
|
25
|
+
} = props;
|
|
26
|
+
const [isOpen, setIsOpen] = React.useState(false);
|
|
27
|
+
const [internalSingleDate, setInternalSingleDate] = React.useState(
|
|
28
|
+
variant === "single" && initialValue instanceof Date ? initialValue : undefined
|
|
21
29
|
);
|
|
22
|
-
const [
|
|
23
|
-
variant === "range" ? initialValue : undefined
|
|
30
|
+
const [internalDateRange, setInternalDateRange] = React.useState(
|
|
31
|
+
variant === "range" && isDateRange(initialValue) ? initialValue : undefined
|
|
24
32
|
);
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
return singleDate ? format(singleDate, rest.format ?? "MMM d, yyyy") : placeholder;
|
|
28
|
-
} else {
|
|
29
|
-
return dateRange ? `${dateRange.from ? format(dateRange.from, rest.format ?? "MMM d, yyyy") : ""} - ${dateRange.to ? format(dateRange.to, rest.format ?? "MMM d, yyyy") : ""}` : placeholder;
|
|
30
|
-
}
|
|
31
|
-
};
|
|
33
|
+
const singleDate = variant === "single" && value instanceof Date ? value : internalSingleDate;
|
|
34
|
+
const dateRange = variant === "range" && isDateRange(value) ? value : internalDateRange;
|
|
32
35
|
const handleSelect = React.useCallback(
|
|
33
|
-
(
|
|
36
|
+
(selectedDate) => {
|
|
34
37
|
if (variant === "single") {
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
const date = selectedDate;
|
|
39
|
+
setInternalSingleDate(date);
|
|
40
|
+
if (onChange && typeof onChange === "function") onChange(date);
|
|
41
|
+
if (closeOnSelect) {
|
|
42
|
+
setIsOpen(false);
|
|
43
|
+
}
|
|
37
44
|
} else {
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
const range = selectedDate;
|
|
46
|
+
setInternalDateRange(range);
|
|
47
|
+
if (onChange && typeof onChange === "function") onChange(range);
|
|
40
48
|
}
|
|
41
49
|
},
|
|
42
|
-
[variant,
|
|
50
|
+
[variant, onChange, closeOnSelect]
|
|
43
51
|
);
|
|
52
|
+
const formatDate = () => {
|
|
53
|
+
if (variant === "single") {
|
|
54
|
+
return singleDate ? format(singleDate, rest.format ?? "MMM d, yyyy") : placeholder;
|
|
55
|
+
}
|
|
56
|
+
if (!dateRange) return placeholder;
|
|
57
|
+
return `${dateRange.from ? format(dateRange.from, rest.format ?? "MMM d, yyyy") : ""} - ${dateRange.to ? format(dateRange.to, rest.format ?? "MMM d, yyyy") : ""}`;
|
|
58
|
+
};
|
|
44
59
|
const calendarProps = React.useMemo(() => {
|
|
45
60
|
const baseProps = {
|
|
46
61
|
...rest,
|
|
@@ -51,39 +66,46 @@ function DatePicker({
|
|
|
51
66
|
...baseProps,
|
|
52
67
|
mode: "single",
|
|
53
68
|
selected: singleDate,
|
|
54
|
-
onSelect: handleSelect,
|
|
69
|
+
onSelect: (date) => handleSelect(date),
|
|
55
70
|
defaultMonth: singleDate ?? /* @__PURE__ */ new Date()
|
|
56
71
|
};
|
|
57
|
-
} else {
|
|
58
|
-
return {
|
|
59
|
-
...baseProps,
|
|
60
|
-
mode: "range",
|
|
61
|
-
selected: dateRange,
|
|
62
|
-
onSelect: handleSelect,
|
|
63
|
-
defaultMonth: dateRange?.from ?? /* @__PURE__ */ new Date(),
|
|
64
|
-
numberOfMonths: rest.numberOfMonths ?? 2
|
|
65
|
-
};
|
|
66
72
|
}
|
|
73
|
+
return {
|
|
74
|
+
...baseProps,
|
|
75
|
+
mode: "range",
|
|
76
|
+
selected: dateRange,
|
|
77
|
+
onSelect: (range) => handleSelect(range),
|
|
78
|
+
defaultMonth: dateRange?.from ?? /* @__PURE__ */ new Date(),
|
|
79
|
+
numberOfMonths: rest.numberOfMonths ?? 2
|
|
80
|
+
};
|
|
67
81
|
}, [variant, rest, singleDate, dateRange, handleSelect]);
|
|
68
|
-
return /* @__PURE__ */ jsxs(PopoverRoot, { children: [
|
|
82
|
+
return /* @__PURE__ */ jsxs(PopoverRoot, { open: isOpen, onOpenChange: setIsOpen, children: [
|
|
69
83
|
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
70
84
|
Button,
|
|
71
85
|
{
|
|
72
86
|
id: rest.id,
|
|
73
87
|
variant: "ghost",
|
|
74
88
|
className: cn(
|
|
75
|
-
buttonClassName,
|
|
76
89
|
"w-fit justify-start text-left font-normal",
|
|
77
|
-
!(variant === "single" ? singleDate : dateRange) && "text-muted-foreground"
|
|
90
|
+
!(variant === "single" ? singleDate : dateRange) && "text-muted-foreground",
|
|
91
|
+
buttonVariants({ variant: "input" }),
|
|
92
|
+
buttonClassName
|
|
78
93
|
),
|
|
79
94
|
disabled: typeof rest.disabled === "boolean" ? rest.disabled : false,
|
|
80
95
|
children: [
|
|
81
|
-
/* @__PURE__ */ jsx(CalendarBlank, { className: "mr-2 h-4 w-4" }),
|
|
96
|
+
/* @__PURE__ */ jsx(CalendarBlank, { className: "mr-2 h-4 w-4 shrink-0" }),
|
|
82
97
|
/* @__PURE__ */ jsx("span", { children: formatDate() })
|
|
83
98
|
]
|
|
84
99
|
}
|
|
85
100
|
) }),
|
|
86
|
-
/* @__PURE__ */ jsx(PopoverContent, { className: "w-auto p-0", align: "center", children: /* @__PURE__ */ jsx(
|
|
101
|
+
/* @__PURE__ */ jsx(PopoverContent, { className: "w-auto p-0", align: "center", children: /* @__PURE__ */ jsx(
|
|
102
|
+
Calendar,
|
|
103
|
+
{
|
|
104
|
+
...calendarProps,
|
|
105
|
+
className: "border-0",
|
|
106
|
+
showYearSwitcher: variant === "single" ? showYearSwitcher : false
|
|
107
|
+
}
|
|
108
|
+
) })
|
|
87
109
|
] });
|
|
88
110
|
}
|
|
89
111
|
DatePicker.displayName = "DatePicker";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DatePicker.js","sources":["../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { format } from 'date-fns'\nimport { DateRange,
|
|
1
|
+
{"version":3,"file":"DatePicker.js","sources":["../../../src/components/DatePicker/DatePicker.tsx"],"sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport { format } from 'date-fns'\nimport { DateRange, PropsBase } from 'react-day-picker'\n\nimport { cn } from '@/lib/utils'\nimport { PopoverContent, PopoverRoot, PopoverTrigger } from '../Popover'\nimport { Button, buttonVariants } from '../Button'\nimport { Calendar } from '../Calendar'\nimport { CalendarBlank } from '@phosphor-icons/react'\n\ntype BaseDatePickerProps = {\n /**\n * Placeholder text displayed when no date is selected\n * @default 'Pick a date'\n */\n placeholder?: string\n /**\n * Format string to use when displaying the selected date\n * @default 'MMM d, yyyy'\n */\n format?: string\n /**\n * ClassName for the button\n */\n buttonClassName?: string\n} & Omit<PropsBase, 'mode' | 'selected' | 'onSelect'>\n\ntype SingleDatePickerProps = {\n variant?: 'single'\n value?: Date\n initialValue?: Date\n onChange?: (date: Date | undefined) => void\n closeOnSelect?: boolean\n showYearSwitcher?: boolean\n} & BaseDatePickerProps\n\ntype RangeDatePickerProps = {\n variant: 'range'\n value?: DateRange\n initialValue?: DateRange\n onChange?: (date: DateRange | undefined) => void\n closeOnSelect?: never\n showYearSwitcher?: never\n} & BaseDatePickerProps\n\nexport type DatePickerProps = SingleDatePickerProps | RangeDatePickerProps\n\nfunction isDateRange(value: any): value is DateRange {\n return typeof value === 'object' && value !== null && 'from' in value\n}\n\nfunction DatePicker(props: DatePickerProps) {\n const {\n variant = 'single',\n placeholder = variant === 'single' ? 'Pick a date' : 'Pick a date range',\n initialValue,\n value,\n onChange,\n buttonClassName,\n closeOnSelect = variant === 'single',\n showYearSwitcher = variant === 'single',\n ...rest\n } = props\n\n const [isOpen, setIsOpen] = React.useState(false)\n\n const [internalSingleDate, setInternalSingleDate] = React.useState<Date | undefined>(\n variant === 'single' && initialValue instanceof Date ? initialValue : undefined,\n )\n const [internalDateRange, setInternalDateRange] = React.useState<DateRange | undefined>(\n variant === 'range' && isDateRange(initialValue) ? initialValue : undefined,\n )\n\n const singleDate = variant === 'single' && value instanceof Date ? value : internalSingleDate\n const dateRange = variant === 'range' && isDateRange(value) ? value : internalDateRange\n\n const handleSelect = React.useCallback(\n (selectedDate: Date | DateRange | undefined) => {\n if (variant === 'single') {\n const date = selectedDate as Date | undefined\n setInternalSingleDate(date)\n if (onChange && typeof onChange === 'function') (onChange as (date: Date | undefined) => void)(date)\n if (closeOnSelect) {\n setIsOpen(false)\n }\n } else {\n const range = selectedDate as DateRange | undefined\n setInternalDateRange(range)\n if (onChange && typeof onChange === 'function') (onChange as (range: DateRange | undefined) => void)(range)\n }\n },\n [variant, onChange, closeOnSelect],\n )\n\n const formatDate = () => {\n if (variant === 'single') {\n return singleDate ? format(singleDate, rest.format ?? 'MMM d, yyyy') : placeholder\n }\n\n if (!dateRange) return placeholder\n return `${dateRange.from ? format(dateRange.from, rest.format ?? 'MMM d, yyyy') : ''} - ${\n dateRange.to ? format(dateRange.to, rest.format ?? 'MMM d, yyyy') : ''\n }`\n }\n\n const calendarProps = React.useMemo(() => {\n const baseProps = {\n ...rest,\n initialFocus: true,\n }\n\n if (variant === 'single') {\n return {\n ...baseProps,\n mode: 'single' as const,\n selected: singleDate,\n onSelect: (date: Date | undefined) => handleSelect(date),\n defaultMonth: singleDate ?? new Date(),\n }\n }\n\n return {\n ...baseProps,\n mode: 'range' as const,\n selected: dateRange,\n onSelect: (range: DateRange | undefined) => handleSelect(range),\n defaultMonth: dateRange?.from ?? new Date(),\n numberOfMonths: rest.numberOfMonths ?? 2,\n }\n }, [variant, rest, singleDate, dateRange, handleSelect])\n\n return (\n <PopoverRoot open={isOpen} onOpenChange={setIsOpen}>\n <PopoverTrigger asChild>\n <Button\n id={rest.id}\n variant=\"ghost\"\n className={cn(\n 'w-fit justify-start text-left font-normal',\n !(variant === 'single' ? singleDate : dateRange) && 'text-muted-foreground',\n buttonVariants({ variant: 'input' }),\n buttonClassName,\n )}\n disabled={typeof rest.disabled === 'boolean' ? rest.disabled : false}\n >\n <CalendarBlank className=\"mr-2 h-4 w-4 shrink-0\" />\n <span>{formatDate()}</span>\n </Button>\n </PopoverTrigger>\n <PopoverContent className=\"w-auto p-0\" align=\"center\">\n <Calendar\n {...calendarProps}\n className=\"border-0\"\n showYearSwitcher={variant === 'single' ? showYearSwitcher : false}\n />\n </PopoverContent>\n </PopoverRoot>\n )\n}\n\nDatePicker.displayName = 'DatePicker'\n\nexport { DatePicker }\n"],"names":[],"mappings":";;;;;;;;;;AAiDA;AACE;AACF;AAEA;AACE;AAAM;AACM;AAC2C;AACrD;AACA;AACA;AACA;AAC4B;AACG;AAC5B;AAGL;AAEA;AAA0D;AACc;AAExE;AAAwD;AACY;AAGpE;AACA;AAEA;AAA2B;AAEvB;AACE;AACA;AACA;AACA;AACE;AAAe;AACjB;AAEA;AACA;AACA;AAA0G;AAC5G;AACF;AACiC;AAGnC;AACE;AACE;AAAuE;AAGzE;AACA;AAEA;AAGF;AACE;AAAkB;AACb;AACW;AAGhB;AACE;AAAO;AACF;AACG;AACI;AAC6C;AAClB;AACvC;AAGF;AAAO;AACF;AACG;AACI;AACoD;AACpB;AACH;AACzC;AAGF;AAEI;AACE;AAAC;AAAA;AACU;AACD;AACG;AACT;AACoD;AACjB;AACnC;AACF;AAC+D;AAE/D;AAAiD;AAC7B;AAAA;AAAA;AAExB;AAEE;AAAC;AAAA;AACK;AACM;AACkD;AAAA;AAEhE;AAGN;AAEA;;"}
|
|
@@ -5,7 +5,9 @@ declare const DialogTrigger: React.ForwardRefExoticComponent<DialogPrimitive.Dia
|
|
|
5
5
|
declare const DialogPortal: React.FC<DialogPrimitive.DialogPortalProps>;
|
|
6
6
|
declare const DialogClose: React.ForwardRefExoticComponent<DialogPrimitive.DialogCloseProps & React.RefAttributes<HTMLButtonElement>>;
|
|
7
7
|
declare const DialogOverlay: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogOverlayProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
8
|
-
declare const DialogContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> &
|
|
8
|
+
declare const DialogContent: React.ForwardRefExoticComponent<Omit<DialogPrimitive.DialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
9
|
+
closable?: boolean;
|
|
10
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
9
11
|
declare const DialogHeader: {
|
|
10
12
|
({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
|
|
11
13
|
displayName: string;
|