@shipfox/react-ui 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/dist/components/calendar/calendar.js +12 -12
  2. package/dist/components/combobox/combobox.d.ts +21 -0
  3. package/dist/components/combobox/combobox.js +84 -0
  4. package/dist/components/combobox/combobox.stories.js +191 -0
  5. package/dist/components/combobox/index.d.ts +2 -0
  6. package/dist/components/combobox/index.js +3 -0
  7. package/dist/components/command/command.d.ts +2 -1
  8. package/dist/components/command/command.js +9 -5
  9. package/dist/components/dashboard/components/kpi-card.js +4 -1
  10. package/dist/components/date-picker/date-picker.d.ts +1 -0
  11. package/dist/components/date-picker/date-picker.js +20 -4
  12. package/dist/components/date-picker/date-picker.stories.js +16 -0
  13. package/dist/components/date-time-range-picker/date-time-range-picker.d.ts +1 -0
  14. package/dist/components/date-time-range-picker/date-time-range-picker.js +51 -23
  15. package/dist/components/dropdown-input/dropdown-input.d.ts +25 -0
  16. package/dist/components/dropdown-input/dropdown-input.js +188 -0
  17. package/dist/components/dropdown-input/dropdown-input.stories.js +240 -0
  18. package/dist/components/dropdown-input/index.d.ts +2 -0
  19. package/dist/components/dropdown-input/index.js +3 -0
  20. package/dist/components/index.d.ts +3 -0
  21. package/dist/components/index.js +3 -0
  22. package/dist/components/input/input.d.ts +6 -3
  23. package/dist/components/input/input.js +27 -11
  24. package/dist/components/input/input.stories.js +66 -0
  25. package/dist/components/scroll-area/index.d.ts +2 -0
  26. package/dist/components/scroll-area/index.js +3 -0
  27. package/dist/components/scroll-area/scroll-area.d.ts +6 -0
  28. package/dist/components/scroll-area/scroll-area.js +34 -0
  29. package/dist/styles.css +1 -1
  30. package/package.json +2 -2
@@ -5,34 +5,34 @@ import { cn } from '../../utils/cn.js';
5
5
  export function Calendar({ className, classNames, showOutsideDays = true, ...props }) {
6
6
  return /*#__PURE__*/ _jsx(DayPicker, {
7
7
  showOutsideDays: showOutsideDays,
8
- className: cn('p-16 transition-colors', className),
8
+ className: cn('transition-colors', className),
9
9
  classNames: {
10
- months: 'flex flex-col sm:flex-row gap-24',
11
- month: 'space-y-16 relative',
12
- month_caption: 'flex items-center justify-center mb-8 px-4 relative h-32',
10
+ months: 'flex flex-col sm:flex-row gap-0',
11
+ month: 'space-y-16 relative p-16 border-r border-border-neutral-base-component last:border-r-0',
12
+ month_caption: 'flex items-center justify-center mb-8 px-4 relative h-32 bg-background-field-base rounded-8 shadow-tooltip',
13
13
  caption_label: 'text-sm font-medium text-foreground-neutral-base',
14
14
  nav: 'flex items-center gap-4 fixed left-0 top-16 w-full z-10',
15
- button_previous: cn('size-32 bg-transparent p-0 absolute left-16 top-0', 'inline-flex items-center justify-center rounded-6', 'text-foreground-neutral-base', 'hover:bg-background-button-transparent-hover', 'active:bg-background-button-transparent-pressed', 'transition-colors outline-none', 'focus-visible:shadow-border-interactive-with-active', 'disabled:pointer-events-none disabled:opacity-50'),
16
- button_next: cn('size-32 bg-transparent p-0 absolute right-16 top-0', 'inline-flex items-center justify-center rounded-6', 'text-foreground-neutral-base', 'hover:bg-background-button-transparent-hover', 'active:bg-background-button-transparent-pressed', 'transition-colors outline-none', 'focus-visible:shadow-border-interactive-with-active', 'disabled:pointer-events-none disabled:opacity-50'),
15
+ button_previous: cn('size-32 bg-transparent p-0 absolute left-16 top-0 cursor-pointer', 'inline-flex items-center justify-center rounded-6', 'text-foreground-neutral-muted', 'hover:bg-transparent hover:text-foreground-neutral-subtle', 'active:bg-transparent', 'transition-colors outline-none', 'focus-visible:shadow-border-interactive-with-active', 'disabled:pointer-events-none disabled:opacity-50'),
16
+ button_next: cn('size-32 bg-transparent p-0 absolute right-16 top-0 cursor-pointer', 'inline-flex items-center justify-center rounded-6', 'text-foreground-neutral-muted', 'hover:bg-transparent hover:text-foreground-neutral-subtle', 'active:bg-transparent', 'transition-colors outline-none', 'focus-visible:shadow-border-interactive-with-active', 'disabled:pointer-events-none disabled:opacity-50'),
17
17
  month_grid: 'w-full border-collapse mt-8',
18
- weekdays: 'flex mb-8',
18
+ weekdays: 'flex gap-8',
19
19
  weekday: 'text-foreground-neutral-subtle text-xs font-medium w-36 h-32 flex items-center justify-center',
20
- week: 'flex mt-4',
20
+ week: 'flex mt-8 gap-8',
21
21
  day: cn('relative text-center size-36 p-0 text-sm font-normal [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none', '[&:last-child[data-selected=true]_button]:rounded-r-6', props.showWeekNumber ? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-6' : '[&:first-child[data-selected=true]_button]:rounded-l-6'),
22
22
  day_button: cn('size-36 p-0 text-sm font-normal rounded-6', 'inline-flex items-center justify-center', 'hover:bg-background-button-transparent-hover', 'focus-visible:shadow-border-interactive-with-active', 'transition-colors outline-none', 'aria-selected:opacity-100'),
23
23
  range_start: 'day-range-start rounded-6',
24
24
  range_end: 'day-range-end rounded-6',
25
- selected: cn('bg-foreground-highlight-interactive/80 !text-foreground-neutral-base font-medium rounded-6', 'hover:bg-foreground-highlight-interactive-hover/80', 'focus:bg-foreground-highlight-interactive/80'),
26
- today: cn('bg-background-field-base text-foreground-neutral-base font-medium rounded-6', 'border border-border-neutral-base'),
25
+ selected: cn('bg-foreground-highlight-interactive/80 !text-foreground-neutral-on-inverted font-medium rounded-6', 'hover:bg-foreground-highlight-interactive-hover/80', 'focus:bg-foreground-highlight-interactive/80'),
26
+ today: cn('relative font-medium rounded-6', 'after:absolute after:bottom-[6px] after:left-1/2 after:-translate-x-1/2', 'after:size-[3px] after:rounded-full after:bg-[var(--color-primary-400)]'),
27
27
  outside: 'day-outside text-foreground-neutral-muted',
28
28
  disabled: 'text-foreground-neutral-disabled opacity-30 cursor-not-allowed',
29
- range_middle: cn('aria-selected:bg-foreground-highlight-interactive/10 aria-selected:text-foreground-neutral-base', 'rounded-none', 'first:rounded-l-6 first:rounded-r-none', 'last:rounded-r-6 last:rounded-l-none'),
29
+ range_middle: cn('aria-selected:bg-background-highlight-base aria-selected:!text-foreground-highlight-interactive'),
30
30
  hidden: 'invisible',
31
31
  ...classNames
32
32
  },
33
33
  components: {
34
34
  Chevron: ({ orientation })=>{
35
- const iconName = orientation === 'left' ? 'arrowLeftSLine' : 'arrowRightSLine';
35
+ const iconName = orientation === 'left' ? 'arrowLeftSFill' : 'arrowRightSFill';
36
36
  return /*#__PURE__*/ _jsx(Icon, {
37
37
  name: iconName,
38
38
  className: "size-20"
@@ -0,0 +1,21 @@
1
+ import * as React from 'react';
2
+ import { type CommandTriggerProps } from '../command';
3
+ export type ComboboxOption = {
4
+ value: string;
5
+ label: string;
6
+ };
7
+ export type ComboboxProps = Omit<CommandTriggerProps, 'children' | 'placeholder'> & {
8
+ options: ComboboxOption[];
9
+ value?: string;
10
+ onValueChange?: (value: string) => void;
11
+ placeholder?: string;
12
+ emptyState?: string | React.ReactNode;
13
+ searchPlaceholder?: string;
14
+ className?: string;
15
+ popoverClassName?: string;
16
+ align?: 'start' | 'center' | 'end';
17
+ sideOffset?: number;
18
+ isLoading?: boolean;
19
+ };
20
+ export declare function Combobox({ options, value, onValueChange, placeholder, emptyState, searchPlaceholder, className, popoverClassName, align, sideOffset, variant, size, isLoading, ...triggerProps }: ComboboxProps): import("react/jsx-runtime").JSX.Element;
21
+ //# sourceMappingURL=combobox.d.ts.map
@@ -0,0 +1,84 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ScrollArea } from '../../components/scroll-area/index.js';
3
+ import * as React from 'react';
4
+ import { cn } from '../../utils/cn.js';
5
+ import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandTrigger } from '../command/index.js';
6
+ import { Icon } from '../icon/index.js';
7
+ import { Popover, PopoverContent, PopoverTrigger } from '../popover/index.js';
8
+ export function Combobox({ options, value, onValueChange, placeholder = 'Select option...', emptyState = 'No option found.', searchPlaceholder = 'Search...', className, popoverClassName, align = 'start', sideOffset = 4, variant, size, isLoading, ...triggerProps }) {
9
+ const [open, setOpen] = React.useState(false);
10
+ const [internalValue, setInternalValue] = React.useState('');
11
+ const isControlled = value !== undefined;
12
+ const currentValue = isControlled ? value : internalValue;
13
+ const handleValueChange = React.useCallback((newValue)=>{
14
+ if (!isControlled) {
15
+ setInternalValue(newValue);
16
+ }
17
+ onValueChange?.(newValue);
18
+ }, [
19
+ isControlled,
20
+ onValueChange
21
+ ]);
22
+ const selectedOption = options.find((option)=>option.value === currentValue);
23
+ return /*#__PURE__*/ _jsxs(Popover, {
24
+ open: open,
25
+ onOpenChange: setOpen,
26
+ children: [
27
+ /*#__PURE__*/ _jsx(PopoverTrigger, {
28
+ asChild: true,
29
+ children: /*#__PURE__*/ _jsx(CommandTrigger, {
30
+ variant: variant,
31
+ size: size,
32
+ placeholder: placeholder,
33
+ className: className,
34
+ isLoading: isLoading,
35
+ ...triggerProps,
36
+ children: selectedOption?.label
37
+ })
38
+ }),
39
+ /*#__PURE__*/ _jsx(PopoverContent, {
40
+ className: cn('w-(--radix-popover-trigger-width) p-0', popoverClassName),
41
+ align: align,
42
+ sideOffset: sideOffset,
43
+ onWheel: (e)=>e.stopPropagation(),
44
+ onTouchStart: (e)=>e.stopPropagation(),
45
+ onTouchMove: (e)=>e.stopPropagation(),
46
+ children: /*#__PURE__*/ _jsxs(Command, {
47
+ children: [
48
+ /*#__PURE__*/ _jsx(CommandInput, {
49
+ placeholder: searchPlaceholder
50
+ }),
51
+ /*#__PURE__*/ _jsx(ScrollArea, {
52
+ children: /*#__PURE__*/ _jsxs(CommandList, {
53
+ className: "max-h-300",
54
+ children: [
55
+ /*#__PURE__*/ _jsx(CommandEmpty, {
56
+ children: emptyState
57
+ }),
58
+ /*#__PURE__*/ _jsx(CommandGroup, {
59
+ children: options.map((option)=>/*#__PURE__*/ _jsxs(CommandItem, {
60
+ value: option.value,
61
+ onSelect: (selectedValue)=>{
62
+ handleValueChange(currentValue === selectedValue ? '' : selectedValue);
63
+ setOpen(false);
64
+ },
65
+ children: [
66
+ /*#__PURE__*/ _jsx(Icon, {
67
+ name: "check",
68
+ className: cn('size-16 mr-8', currentValue === option.value ? 'opacity-100' : 'opacity-0')
69
+ }),
70
+ option.label
71
+ ]
72
+ }, option.value))
73
+ })
74
+ ]
75
+ })
76
+ })
77
+ ]
78
+ })
79
+ })
80
+ ]
81
+ });
82
+ }
83
+
84
+ //# sourceMappingURL=combobox.js.map
@@ -0,0 +1,191 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Label } from '../../components/label/index.js';
3
+ import { useState } from 'react';
4
+ import { Combobox } from './combobox.js';
5
+ const sampleItems = [
6
+ {
7
+ value: 'apache',
8
+ label: 'apache'
9
+ },
10
+ {
11
+ value: 'apache-superset',
12
+ label: 'apache-superset'
13
+ },
14
+ {
15
+ value: 'apaleo',
16
+ label: 'apaleo'
17
+ },
18
+ {
19
+ value: 'apollo',
20
+ label: 'apollo'
21
+ },
22
+ {
23
+ value: 'apple',
24
+ label: 'apple'
25
+ },
26
+ {
27
+ value: 'apache-kafka',
28
+ label: 'apache-kafka'
29
+ },
30
+ {
31
+ value: 'apex',
32
+ label: 'apex'
33
+ },
34
+ {
35
+ value: 'appsmith',
36
+ label: 'appsmith'
37
+ },
38
+ {
39
+ value: 'applitools',
40
+ label: 'applitools'
41
+ },
42
+ {
43
+ value: 'approzium',
44
+ label: 'approzium'
45
+ },
46
+ {
47
+ value: 'apify',
48
+ label: 'apify'
49
+ },
50
+ {
51
+ value: 'apicurio',
52
+ label: 'apicurio'
53
+ },
54
+ {
55
+ value: 'apitable',
56
+ label: 'apitable'
57
+ },
58
+ {
59
+ value: 'apollographql',
60
+ label: 'apollographql'
61
+ },
62
+ {
63
+ value: 'aptos',
64
+ label: 'aptos'
65
+ }
66
+ ];
67
+ const meta = {
68
+ title: 'Components/Combobox',
69
+ component: Combobox,
70
+ tags: [
71
+ 'autodocs'
72
+ ],
73
+ parameters: {
74
+ layout: 'centered'
75
+ }
76
+ };
77
+ export default meta;
78
+ export const Default = {
79
+ args: {},
80
+ render: ()=>{
81
+ const [value, setValue] = useState('');
82
+ return /*#__PURE__*/ _jsxs("div", {
83
+ className: "w-[80vw] md:w-500",
84
+ children: [
85
+ /*#__PURE__*/ _jsx(Label, {
86
+ htmlFor: "combobox-default",
87
+ children: "Search repositories"
88
+ }),
89
+ /*#__PURE__*/ _jsx(Combobox, {
90
+ id: "combobox-default",
91
+ options: sampleItems,
92
+ value: value,
93
+ onValueChange: setValue,
94
+ placeholder: "Type to search...",
95
+ searchPlaceholder: "Search repositories...",
96
+ emptyState: "No repository found."
97
+ })
98
+ ]
99
+ });
100
+ }
101
+ };
102
+ export const EmptyState = {
103
+ args: {},
104
+ render: ()=>{
105
+ const [value, setValue] = useState('abcxyz');
106
+ return /*#__PURE__*/ _jsxs("div", {
107
+ className: "w-[80vw] md:w-500",
108
+ children: [
109
+ /*#__PURE__*/ _jsx(Label, {
110
+ htmlFor: "combobox-empty",
111
+ children: "No results"
112
+ }),
113
+ /*#__PURE__*/ _jsx(Combobox, {
114
+ id: "combobox-empty",
115
+ options: [],
116
+ value: value,
117
+ onValueChange: setValue,
118
+ placeholder: "Type to search...",
119
+ searchPlaceholder: "Search repositories...",
120
+ emptyState: /*#__PURE__*/ _jsxs("p", {
121
+ className: "px-4 whitespace-pre-wrap",
122
+ children: [
123
+ "Repository list is limited to 100.",
124
+ ' ',
125
+ /*#__PURE__*/ _jsx("a", {
126
+ href: "https://support.example.com",
127
+ target: "_blank",
128
+ rel: "noopener noreferrer",
129
+ className: "underline text-foreground-neutral-base",
130
+ children: "Contact us"
131
+ }),
132
+ ' ',
133
+ "for support."
134
+ ]
135
+ })
136
+ })
137
+ ]
138
+ });
139
+ }
140
+ };
141
+ export const LoadingState = {
142
+ args: {},
143
+ render: ()=>{
144
+ const [value, setValue] = useState('');
145
+ return /*#__PURE__*/ _jsxs("div", {
146
+ className: "w-[80vw] md:w-500",
147
+ children: [
148
+ /*#__PURE__*/ _jsx(Label, {
149
+ htmlFor: "combobox-loading",
150
+ children: "Loading"
151
+ }),
152
+ /*#__PURE__*/ _jsx(Combobox, {
153
+ id: "combobox-loading",
154
+ options: sampleItems,
155
+ value: value,
156
+ onValueChange: setValue,
157
+ placeholder: "Type to search...",
158
+ searchPlaceholder: "Search repositories...",
159
+ isLoading: true
160
+ })
161
+ ]
162
+ });
163
+ }
164
+ };
165
+ export const DisabledState = {
166
+ args: {},
167
+ render: ()=>{
168
+ const [value, setValue] = useState('apache');
169
+ return /*#__PURE__*/ _jsxs("div", {
170
+ className: "w-[80vw] md:w-500",
171
+ children: [
172
+ /*#__PURE__*/ _jsx(Label, {
173
+ htmlFor: "combobox-disabled",
174
+ children: "Disabled"
175
+ }),
176
+ /*#__PURE__*/ _jsx(Combobox, {
177
+ id: "combobox-disabled",
178
+ options: sampleItems,
179
+ value: value,
180
+ onValueChange: setValue,
181
+ disabled: true,
182
+ placeholder: "Disabled input",
183
+ searchPlaceholder: "Search repositories...",
184
+ emptyState: "No results found"
185
+ })
186
+ ]
187
+ });
188
+ }
189
+ };
190
+
191
+ //# sourceMappingURL=combobox.stories.js.map
@@ -0,0 +1,2 @@
1
+ export * from './combobox';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ export * from './combobox.js';
2
+
3
+ //# sourceMappingURL=index.js.map
@@ -8,6 +8,7 @@ declare const commandTriggerVariants: (props?: ({
8
8
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
9
9
  type CommandTriggerProps = ComponentProps<'button'> & VariantProps<typeof commandTriggerVariants> & {
10
10
  placeholder?: string;
11
+ isLoading?: boolean;
11
12
  };
12
13
  declare const CommandTrigger: import("react").ForwardRefExoticComponent<Omit<CommandTriggerProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
13
14
  declare function Command({ className, ...props }: ComponentProps<typeof CommandPrimitive>): import("react/jsx-runtime").JSX.Element;
@@ -18,7 +19,7 @@ type CommandInputProps = ComponentProps<typeof CommandPrimitive.Input> & {
18
19
  };
19
20
  declare function CommandInput({ className, value, onValueChange, onClear, showClearButton, ...props }: CommandInputProps): import("react/jsx-runtime").JSX.Element;
20
21
  declare function CommandList({ className, ...props }: ComponentProps<typeof CommandPrimitive.List>): import("react/jsx-runtime").JSX.Element;
21
- declare function CommandEmpty({ className, ...props }: ComponentProps<typeof CommandPrimitive.Empty>): import("react/jsx-runtime").JSX.Element;
22
+ declare function CommandEmpty({ className, children, ...props }: ComponentProps<typeof CommandPrimitive.Empty>): import("react/jsx-runtime").JSX.Element;
22
23
  declare function CommandGroup({ className, ...props }: ComponentProps<typeof CommandPrimitive.Group>): import("react/jsx-runtime").JSX.Element;
23
24
  declare function CommandSeparator({ className, ...props }: ComponentProps<typeof CommandPrimitive.Separator>): import("react/jsx-runtime").JSX.Element;
24
25
  declare function CommandItem({ className, ...props }: ComponentProps<typeof CommandPrimitive.Item>): import("react/jsx-runtime").JSX.Element;
@@ -29,7 +29,7 @@ const commandTriggerVariants = cva([
29
29
  size: 'base'
30
30
  }
31
31
  });
32
- const CommandTrigger = /*#__PURE__*/ forwardRef(({ className, variant, size, placeholder, children, ...props }, ref)=>{
32
+ const CommandTrigger = /*#__PURE__*/ forwardRef(({ className, variant, size, placeholder, children, isLoading, ...props }, ref)=>{
33
33
  const hasValue = Boolean(children);
34
34
  return /*#__PURE__*/ _jsxs("button", {
35
35
  ref: ref,
@@ -46,8 +46,11 @@ const CommandTrigger = /*#__PURE__*/ forwardRef(({ className, variant, size, pla
46
46
  className: "flex-1 text-left truncate",
47
47
  children: hasValue ? children : placeholder
48
48
  }),
49
- /*#__PURE__*/ _jsx(Icon, {
50
- name: "arrowDownSLine",
49
+ isLoading ? /*#__PURE__*/ _jsx(Icon, {
50
+ name: "spinner",
51
+ className: "size-16 text-foreground-neutral-base shrink-0"
52
+ }) : /*#__PURE__*/ _jsx(Icon, {
53
+ name: "expandUpDownLine",
51
54
  className: "size-16 text-foreground-neutral-muted shrink-0"
52
55
  })
53
56
  ]
@@ -149,11 +152,12 @@ function CommandList({ className, ...props }) {
149
152
  ...props
150
153
  });
151
154
  }
152
- function CommandEmpty({ className, ...props }) {
155
+ function CommandEmpty({ className, children, ...props }) {
153
156
  return /*#__PURE__*/ _jsx(CommandPrimitive.Empty, {
154
157
  "data-slot": "command-empty",
155
158
  className: cn('py-24 text-center text-sm text-foreground-neutral-muted', className),
156
- ...props
159
+ ...props,
160
+ children: children
157
161
  });
158
162
  }
159
163
  function CommandGroup({ className, ...props }) {
@@ -29,10 +29,13 @@ export function KpiCard({ label, value, variant = 'neutral', isLoading, classNam
29
29
  }),
30
30
  isLoading ? /*#__PURE__*/ _jsx(Skeleton, {
31
31
  className: "w-48 h-20 rounded-4"
32
- }) : /*#__PURE__*/ _jsx(Text, {
32
+ }) : typeof value === 'string' || typeof value === 'number' ? /*#__PURE__*/ _jsx(Text, {
33
33
  size: "sm",
34
34
  className: "font-medium text-foreground-neutral-base",
35
35
  children: value
36
+ }) : /*#__PURE__*/ _jsx("div", {
37
+ className: "text-sm font-medium text-foreground-neutral-base",
38
+ children: value
36
39
  })
37
40
  ]
38
41
  })
@@ -14,6 +14,7 @@ export type DatePickerProps = Omit<ComponentProps<'input'>, 'size' | 'type'> & V
14
14
  rightIcon?: ReactNode;
15
15
  onClear?: () => void;
16
16
  closeOnSelect?: boolean;
17
+ maxDisabledOffsetDays?: number;
17
18
  };
18
19
  export declare const DatePicker: import("react").ForwardRefExoticComponent<Omit<DatePickerProps, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
19
20
  //# sourceMappingURL=date-picker.d.ts.map
@@ -3,8 +3,8 @@ import { cva } from 'class-variance-authority';
3
3
  import { Calendar } from '../../components/calendar/index.js';
4
4
  import { Icon } from '../../components/icon/index.js';
5
5
  import { Popover, PopoverContent, PopoverTrigger } from '../../components/popover/index.js';
6
- import { format } from 'date-fns';
7
- import { forwardRef, useState } from 'react';
6
+ import { addDays, format, subDays } from 'date-fns';
7
+ import { forwardRef, useMemo, useState } from 'react';
8
8
  import { cn } from '../../utils/cn.js';
9
9
  export const datePickerVariants = cva('relative flex items-center rounded-6 shadow-button-neutral transition-[background-color,box-shadow] outline-none', {
10
10
  variants: {
@@ -28,10 +28,22 @@ export const datePickerVariants = cva('relative flex items-center rounded-6 shad
28
28
  state: 'default'
29
29
  }
30
30
  });
31
- export const DatePicker = /*#__PURE__*/ forwardRef(({ className, variant, size, state, date, onDateSelect, placeholder = 'DD/MM/YYYY', dateFormat = 'dd/MM/yyyy', leftIcon, rightIcon, onClear, disabled, closeOnSelect = false, ...props }, ref)=>{
31
+ export const DatePicker = /*#__PURE__*/ forwardRef(({ className, variant, size, state, date, onDateSelect, placeholder = 'DD/MM/YYYY', dateFormat = 'dd/MM/yyyy', leftIcon, rightIcon, onClear, disabled, closeOnSelect = false, maxDisabledOffsetDays, ...props }, ref)=>{
32
32
  const [open, setOpen] = useState(false);
33
33
  const isDisabled = disabled || state === 'disabled';
34
34
  const displayValue = date ? format(date, dateFormat) : '';
35
+ // Disable dates beyond maxDisabledDays before and after today
36
+ const disabledDates = useMemo(()=>{
37
+ if (!maxDisabledOffsetDays) return undefined;
38
+ const today = new Date();
39
+ const minDate = subDays(today, maxDisabledOffsetDays);
40
+ const maxDate = addDays(today, maxDisabledOffsetDays);
41
+ return (date)=>{
42
+ return date < minDate || date > maxDate;
43
+ };
44
+ }, [
45
+ maxDisabledOffsetDays
46
+ ]);
35
47
  const handleSelect = (selectedDate)=>{
36
48
  onDateSelect?.(selectedDate);
37
49
  if (closeOnSelect) {
@@ -103,7 +115,11 @@ export const DatePicker = /*#__PURE__*/ forwardRef(({ className, variant, size,
103
115
  children: /*#__PURE__*/ _jsx(Calendar, {
104
116
  mode: "single",
105
117
  selected: date,
106
- onSelect: handleSelect
118
+ onSelect: handleSelect,
119
+ disabled: disabledDates,
120
+ formatters: {
121
+ formatWeekdayName: (date)=>format(date, 'EEEEE')
122
+ }
107
123
  })
108
124
  })
109
125
  ]
@@ -51,6 +51,22 @@ export const DatePickerStory = {
51
51
  });
52
52
  }
53
53
  };
54
+ export const DatePickerWithThresholdStory = {
55
+ play: (ctx)=>openCalendarAndScreenshot(ctx, 'DatePicker With Threshold Calendar Open'),
56
+ render: ()=>{
57
+ const [date, setDate] = useState(new Date());
58
+ return /*#__PURE__*/ _jsx("div", {
59
+ className: "relative flex h-600 w-500 items-center justify-center rounded-16 bg-background-subtle-base shadow-tooltip overflow-visible",
60
+ children: /*#__PURE__*/ _jsx(DatePicker, {
61
+ date: date,
62
+ onDateSelect: setDate,
63
+ onClear: ()=>setDate(undefined),
64
+ placeholder: "DD/MM/YYYY",
65
+ maxDisabledOffsetDays: 30
66
+ })
67
+ });
68
+ }
69
+ };
54
70
  export const DateRangePickerStory = {
55
71
  play: (ctx)=>openCalendarAndScreenshot(ctx, 'DateRangePicker Calendar Open'),
56
72
  render: ()=>{
@@ -19,6 +19,7 @@ export type DateTimeRangePickerProps = Omit<ComponentProps<'input'>, 'size' | 't
19
19
  onClear?: () => void;
20
20
  numberOfMonths?: number;
21
21
  closeOnSelect?: boolean;
22
+ maxRangeDays?: number;
22
23
  };
23
24
  export declare const DateTimeRangePicker: import("react").ForwardRefExoticComponent<Omit<DateTimeRangePickerProps, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
24
25
  //# sourceMappingURL=date-time-range-picker.d.ts.map
@@ -3,8 +3,8 @@ import { cva } from 'class-variance-authority';
3
3
  import { Calendar } from '../../components/calendar/index.js';
4
4
  import { Icon } from '../../components/icon/index.js';
5
5
  import { Popover, PopoverContent, PopoverTrigger } from '../../components/popover/index.js';
6
- import { format } from 'date-fns';
7
- import { forwardRef, useState } from 'react';
6
+ import { addDays, differenceInDays, format } from 'date-fns';
7
+ import { forwardRef, useMemo, useState } from 'react';
8
8
  import { cn } from '../../utils/cn.js';
9
9
  export const dateTimeRangePickerVariants = cva('min-w-240 relative flex items-center rounded-6 shadow-button-neutral transition-[background-color,box-shadow] outline-none', {
10
10
  variants: {
@@ -28,28 +28,52 @@ export const dateTimeRangePickerVariants = cva('min-w-240 relative flex items-ce
28
28
  state: 'default'
29
29
  }
30
30
  });
31
- export const DateTimeRangePicker = /*#__PURE__*/ forwardRef(({ className, variant, size, state, dateRange, onDateRangeSelect, placeholder = 'DD/MM/YYYY - DD/MM/YYYY', dateFormat = 'dd/MM/yyyy', leftIcon, rightIcon, onClear, disabled, numberOfMonths = 2, closeOnSelect = false, ...props }, ref)=>{
31
+ export const DateTimeRangePicker = /*#__PURE__*/ forwardRef(({ className, variant, size, state, dateRange, onDateRangeSelect, placeholder = 'DD/MM/YYYY - DD/MM/YYYY', dateFormat = 'dd/MM/yyyy', leftIcon, rightIcon, onClear, disabled, numberOfMonths = 2, closeOnSelect = false, maxRangeDays, ...props }, ref)=>{
32
32
  const [open, setOpen] = useState(false);
33
33
  const isDisabled = disabled || state === 'disabled';
34
- const hasRange = dateRange?.start && dateRange?.end;
35
- const displayValue = hasRange && dateRange.start && dateRange.end ? `${format(dateRange.start, dateFormat)} - ${format(dateRange.end, dateFormat)}` : '';
36
- // Convert our DateRange to react-day-picker's DateRange format
37
- const dayPickerRange = dateRange?.start || dateRange?.end ? {
38
- from: dateRange?.start,
39
- to: dateRange?.end
34
+ const startDate = dateRange?.start;
35
+ const endDate = dateRange?.end;
36
+ const hasRange = Boolean(startDate && endDate);
37
+ // Format display value
38
+ const displayValue = hasRange && startDate && endDate ? `${format(startDate, dateFormat)} - ${format(endDate, dateFormat)}` : '';
39
+ // Convert to react-day-picker format
40
+ const dayPickerRange = startDate || endDate ? {
41
+ from: startDate,
42
+ to: endDate
40
43
  } : undefined;
44
+ // Get default month for calendar (prioritize selected start date, then today)
45
+ const defaultMonth = startDate ?? new Date();
46
+ // Disable dates beyond maxRangeDays (only if maxRangeDays is provided)
47
+ const disabledDates = useMemo(()=>{
48
+ if (!startDate || maxRangeDays === undefined) return undefined;
49
+ return (date)=>{
50
+ const daysFromStart = differenceInDays(date, startDate);
51
+ return daysFromStart > maxRangeDays;
52
+ };
53
+ }, [
54
+ startDate,
55
+ maxRangeDays
56
+ ]);
41
57
  const handleSelect = (selectedRange)=>{
42
- if (selectedRange) {
43
- onDateRangeSelect?.({
44
- start: selectedRange.from,
45
- end: selectedRange.to
46
- });
47
- // Only close if both dates are selected and closeOnSelect is true
48
- if (closeOnSelect && selectedRange.from && selectedRange.to) {
49
- setOpen(false);
50
- }
51
- } else {
58
+ if (!selectedRange) {
52
59
  onDateRangeSelect?.(undefined);
60
+ return;
61
+ }
62
+ const { from, to } = selectedRange;
63
+ let finalEndDate = to;
64
+ // Cap end date to maxRangeDays if both dates are selected and maxRangeDays is provided
65
+ if (from && to && maxRangeDays !== undefined) {
66
+ const daysFromStart = differenceInDays(to, from);
67
+ if (daysFromStart > maxRangeDays) {
68
+ finalEndDate = addDays(from, maxRangeDays);
69
+ }
70
+ }
71
+ onDateRangeSelect?.({
72
+ start: from,
73
+ end: finalEndDate
74
+ });
75
+ if (closeOnSelect && from && finalEndDate) {
76
+ setOpen(false);
53
77
  }
54
78
  };
55
79
  const handleClear = (e)=>{
@@ -98,11 +122,11 @@ export const DateTimeRangePicker = /*#__PURE__*/ forwardRef(({ className, varian
98
122
  /*#__PURE__*/ _jsx("button", {
99
123
  type: "button",
100
124
  onClick: handleClear,
101
- className: cn('flex items-center justify-center shrink-0 transition-colors hover:text-foreground-neutral-base', size === 'small' ? 'size-28' : 'size-32', hasRange && onClear && !isDisabled ? 'visible' : 'invisible'),
125
+ className: cn('flex items-center justify-center shrink-0 transition-colors hover:text-foreground-neutral-base cursor-pointer', size === 'small' ? 'size-28' : 'size-32', hasRange && onClear && !isDisabled ? 'visible' : 'invisible'),
102
126
  "aria-label": "Clear date range",
103
127
  children: /*#__PURE__*/ _jsx(Icon, {
104
128
  name: "closeLine",
105
- className: "size-16 text-foreground-neutral-muted"
129
+ className: "size-16 text-foreground-neutral-muted hover:text-foreground-neutral-subtle transition-colors"
106
130
  })
107
131
  }),
108
132
  rightIcon && !hasRange && /*#__PURE__*/ _jsx("div", {
@@ -116,10 +140,14 @@ export const DateTimeRangePicker = /*#__PURE__*/ forwardRef(({ className, varian
116
140
  align: "start",
117
141
  children: /*#__PURE__*/ _jsx(Calendar, {
118
142
  mode: "range",
119
- defaultMonth: dayPickerRange?.from,
143
+ defaultMonth: defaultMonth,
120
144
  selected: dayPickerRange,
121
145
  onSelect: handleSelect,
122
- numberOfMonths: numberOfMonths
146
+ numberOfMonths: numberOfMonths,
147
+ disabled: disabledDates,
148
+ formatters: {
149
+ formatWeekdayName: (date)=>format(date, 'EEEEE')
150
+ }
123
151
  })
124
152
  })
125
153
  ]