@shipfox/react-ui 0.21.0 → 0.23.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 (58) hide show
  1. package/dist/components/dashboard/components/charts/bar-chart.d.ts +3 -2
  2. package/dist/components/dashboard/components/charts/bar-chart.js +13 -7
  3. package/dist/components/dashboard/components/charts/line-chart.d.ts +3 -2
  4. package/dist/components/dashboard/components/charts/line-chart.js +13 -7
  5. package/dist/components/dashboard/context/dashboard-context.d.ts +12 -8
  6. package/dist/components/dashboard/context/dashboard-context.js +42 -7
  7. package/dist/components/dashboard/context/index.d.ts +2 -2
  8. package/dist/components/dashboard/context/index.js +1 -1
  9. package/dist/components/dashboard/context/types.d.ts +9 -7
  10. package/dist/components/dashboard/index.d.ts +2 -4
  11. package/dist/components/dashboard/index.js +1 -2
  12. package/dist/components/dashboard/pages/analytics-page.js +41 -11
  13. package/dist/components/dashboard/pages/jobs-page.js +2 -4
  14. package/dist/components/dashboard/toolbar/filter-button.d.ts +6 -10
  15. package/dist/components/dashboard/toolbar/filter-button.js +109 -76
  16. package/dist/components/dashboard/toolbar/page-toolbar.d.ts +7 -19
  17. package/dist/components/dashboard/toolbar/page-toolbar.js +11 -99
  18. package/dist/components/dashboard/toolbar/toolbar-actions.js +3 -3
  19. package/dist/components/index.d.ts +1 -0
  20. package/dist/components/index.js +1 -0
  21. package/dist/components/interval-selector/hooks/index.d.ts +4 -0
  22. package/dist/components/interval-selector/hooks/index.js +5 -0
  23. package/dist/components/interval-selector/hooks/use-interval-selector-input.d.ts +30 -0
  24. package/dist/components/interval-selector/hooks/use-interval-selector-input.js +125 -0
  25. package/dist/components/interval-selector/hooks/use-interval-selector-navigation.d.ts +21 -0
  26. package/dist/components/interval-selector/hooks/use-interval-selector-navigation.js +58 -0
  27. package/dist/components/interval-selector/hooks/use-interval-selector.d.ts +25 -0
  28. package/dist/components/interval-selector/hooks/use-interval-selector.js +75 -0
  29. package/dist/components/interval-selector/index.d.ts +3 -0
  30. package/dist/components/interval-selector/index.js +4 -0
  31. package/dist/components/interval-selector/interval-selector-calendar.d.ts +7 -0
  32. package/dist/components/interval-selector/interval-selector-calendar.js +47 -0
  33. package/dist/components/interval-selector/interval-selector-input.d.ts +8 -0
  34. package/dist/components/interval-selector/interval-selector-input.js +34 -0
  35. package/dist/components/interval-selector/interval-selector-suggestions.d.ts +11 -0
  36. package/dist/components/interval-selector/interval-selector-suggestions.js +107 -0
  37. package/dist/components/interval-selector/interval-selector.d.ts +12 -0
  38. package/dist/components/interval-selector/interval-selector.js +56 -0
  39. package/dist/components/interval-selector/interval-selector.stories.js +232 -0
  40. package/dist/components/interval-selector/types.d.ts +19 -0
  41. package/dist/components/interval-selector/types.js +3 -0
  42. package/dist/components/interval-selector/utils/constants.d.ts +24 -0
  43. package/dist/components/interval-selector/utils/constants.js +129 -0
  44. package/dist/components/interval-selector/utils/format.d.ts +16 -0
  45. package/dist/components/interval-selector/utils/format.js +23 -0
  46. package/dist/components/interval-selector/utils/index.d.ts +3 -0
  47. package/dist/components/interval-selector/utils/index.js +4 -0
  48. package/dist/components/popover/popover.d.ts +3 -1
  49. package/dist/components/popover/popover.js +2 -1
  50. package/dist/styles.css +1 -1
  51. package/dist/utils/date.js +130 -22
  52. package/dist/utils/format/date.d.ts +1 -0
  53. package/dist/utils/format/date.js +11 -4
  54. package/package.json +2 -1
  55. package/dist/components/dashboard/filters/expression-filter-bar.d.ts +0 -42
  56. package/dist/components/dashboard/filters/expression-filter-bar.js +0 -80
  57. package/dist/components/dashboard/filters/index.d.ts +0 -6
  58. package/dist/components/dashboard/filters/index.js +0 -5
@@ -1,73 +1,66 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Button } from '../../../components/button/index.js';
3
- import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '../../../components/dropdown-menu/index.js';
4
- import { Icon } from '../../../components/icon/index.js';
3
+ import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuTrigger } from '../../../components/dropdown-menu/index.js';
5
4
  import { Kbd } from '../../../components/kbd/index.js';
6
5
  import { useEffect, useState } from 'react';
7
6
  import { cn } from '../../../utils/cn.js';
8
- const defaultFilters = [
9
- {
10
- id: 'success',
11
- label: 'Success',
12
- checked: false
13
- },
14
- {
15
- id: 'failed',
16
- label: 'Failed',
17
- checked: false
18
- },
19
- {
20
- id: 'neutral',
21
- label: 'Neutral',
22
- checked: false
23
- },
24
- {
25
- id: 'flaked',
26
- label: 'Flaked',
27
- checked: false
28
- },
29
- {
30
- id: 'running',
31
- label: 'Running',
32
- checked: false
33
- }
34
- ];
35
- export function FilterButton({ filters: controlledFilters, onFiltersChange, className }) {
36
- const [internalFilters, setInternalFilters] = useState(defaultFilters);
7
+ import { RESOURCE_TYPE_LABELS, RESOURCE_TYPE_OPTIONS } from '../context/index.js';
8
+ export function FilterButton({ value, onValueChange, className, ...props }) {
37
9
  const [open, setOpen] = useState(false);
38
- const filters = controlledFilters ?? internalFilters;
39
- const handleFilterChange = (filterId, checked)=>{
40
- const updatedFilters = filters.map((f)=>f.id === filterId ? {
41
- ...f,
42
- checked
43
- } : f);
44
- if (onFiltersChange) {
45
- onFiltersChange(updatedFilters);
46
- } else {
47
- setInternalFilters(updatedFilters);
10
+ const filterOptions = RESOURCE_TYPE_OPTIONS.filter((opt)=>!opt.disabled);
11
+ const normalizedValue = filterOptions.some((opt)=>opt.id === value) ? value : filterOptions[0]?.id ?? value;
12
+ const selectedLabel = RESOURCE_TYPE_LABELS[normalizedValue] ?? normalizedValue;
13
+ const selectedIndex = filterOptions.findIndex((opt)=>opt.id === normalizedValue);
14
+ const handleFilterChange = (filterId)=>{
15
+ onValueChange(filterId);
16
+ setOpen(false);
17
+ };
18
+ const indicator = (index)=>{
19
+ if (index === 0) {
20
+ return /*#__PURE__*/ _jsx("span", {
21
+ className: "size-[8.3px] rotate-45 border border-tag-purple-icon"
22
+ });
23
+ }
24
+ return /*#__PURE__*/ _jsx("span", {
25
+ className: "size-10 rounded-full border border-tag-neutral-icon"
26
+ });
27
+ };
28
+ const calculatePaddingLeft = (index)=>{
29
+ switch(index){
30
+ case 0:
31
+ return 10;
32
+ case 1:
33
+ return 28;
34
+ case 2:
35
+ return 48;
36
+ default:
37
+ return 0;
38
+ }
39
+ };
40
+ const calculateLeftPosition = (index)=>{
41
+ switch(index){
42
+ case 0:
43
+ return 0;
44
+ case 1:
45
+ return 16;
46
+ case 2:
47
+ return 34;
48
+ default:
49
+ return 0;
48
50
  }
49
51
  };
50
- const activeCount = filters.filter((f)=>f.checked).length;
51
- // Keyboard shortcut handler for "F" key
52
52
  useEffect(()=>{
53
53
  const handleKeyDown = (event)=>{
54
- // Check if key is 'f' or 'F'
55
- if (event.key !== 'f' && event.key !== 'F') {
56
- return;
57
- }
58
- // Ignore if event is from input, textarea, or contentEditable
59
- const target = event.target;
60
- if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) {
61
- return;
54
+ if (event.key === 'ArrowDown' && (event.metaKey || event.ctrlKey) && event.target instanceof HTMLElement && ![
55
+ 'INPUT',
56
+ 'TEXTAREA'
57
+ ].includes(event.target.tagName) && !event.target.isContentEditable) {
58
+ event.preventDefault();
59
+ setOpen(true);
62
60
  }
63
- // Open the dropdown
64
- event.preventDefault();
65
- setOpen(true);
66
61
  };
67
62
  window.addEventListener('keydown', handleKeyDown);
68
- return ()=>{
69
- window.removeEventListener('keydown', handleKeyDown);
70
- };
63
+ return ()=>window.removeEventListener('keydown', handleKeyDown);
71
64
  }, []);
72
65
  return /*#__PURE__*/ _jsxs(DropdownMenu, {
73
66
  open: open,
@@ -78,25 +71,35 @@ export function FilterButton({ filters: controlledFilters, onFiltersChange, clas
78
71
  children: /*#__PURE__*/ _jsxs(Button, {
79
72
  variant: "secondary",
80
73
  size: "sm",
81
- className: cn(className),
74
+ className: cn('px-10', className),
75
+ ...props,
82
76
  children: [
83
- /*#__PURE__*/ _jsx(Icon, {
84
- name: "filterLine",
85
- className: "size-16 text-foreground-neutral-subtle block md:hidden"
86
- }),
87
77
  /*#__PURE__*/ _jsxs("span", {
88
78
  className: "hidden md:inline-flex items-center gap-6",
89
79
  children: [
90
- "Filter ",
80
+ /*#__PURE__*/ _jsxs("span", {
81
+ className: "inline-flex items-center gap-8",
82
+ children: [
83
+ selectedIndex >= 0 && indicator(selectedIndex),
84
+ /*#__PURE__*/ _jsx("span", {
85
+ children: selectedLabel
86
+ })
87
+ ]
88
+ }),
91
89
  /*#__PURE__*/ _jsx(Kbd, {
92
90
  className: "h-16 min-w-16 px-4 text-[10px]",
93
- children: "F"
91
+ children: "⌘↓"
94
92
  })
95
93
  ]
96
94
  }),
97
- activeCount > 0 && /*#__PURE__*/ _jsx("span", {
98
- className: "size-16 rounded-full bg-foreground-highlight-interactive text-[10px] font-medium text-foreground-neutral-on-color flex items-center justify-center",
99
- children: activeCount
95
+ /*#__PURE__*/ _jsxs("span", {
96
+ className: "flex md:hidden items-center gap-8",
97
+ children: [
98
+ selectedIndex >= 0 && indicator(selectedIndex),
99
+ /*#__PURE__*/ _jsx("span", {
100
+ children: selectedLabel
101
+ })
102
+ ]
100
103
  })
101
104
  ]
102
105
  })
@@ -106,15 +109,45 @@ export function FilterButton({ filters: controlledFilters, onFiltersChange, clas
106
109
  className: "w-200",
107
110
  children: [
108
111
  /*#__PURE__*/ _jsx(DropdownMenuLabel, {
109
- children: "Status"
112
+ className: "text-foreground-neutral-muted text-xs",
113
+ children: "CI Structure"
110
114
  }),
111
- /*#__PURE__*/ _jsx(DropdownMenuSeparator, {}),
112
- filters.map((filter)=>/*#__PURE__*/ _jsx(DropdownMenuCheckboxItem, {
113
- checked: filter.checked,
114
- onCheckedChange: (checked)=>handleFilterChange(filter.id, checked),
115
- closeOnSelect: false,
116
- children: filter.label
117
- }, filter.id))
115
+ /*#__PURE__*/ _jsx(DropdownMenuGroup, {
116
+ children: filterOptions.map((option, index)=>{
117
+ return /*#__PURE__*/ _jsxs(DropdownMenuItem, {
118
+ onClick: ()=>handleFilterChange(option.id),
119
+ style: {
120
+ paddingLeft: calculatePaddingLeft(index)
121
+ },
122
+ className: cn('relative hover:text-foreground-neutral-base', selectedIndex === index && 'text-foreground-neutral-base'),
123
+ children: [
124
+ index !== 0 && /*#__PURE__*/ _jsxs(_Fragment, {
125
+ children: [
126
+ /*#__PURE__*/ _jsx("span", {
127
+ className: "absolute top-0 bottom-0 w-px bg-border-neutral-strong h-16",
128
+ style: {
129
+ left: calculateLeftPosition(index)
130
+ }
131
+ }),
132
+ /*#__PURE__*/ _jsx("span", {
133
+ className: "absolute top-16 bottom-0 w-6 bg-border-neutral-strong h-px",
134
+ style: {
135
+ left: calculateLeftPosition(index)
136
+ }
137
+ })
138
+ ]
139
+ }),
140
+ /*#__PURE__*/ _jsxs("span", {
141
+ className: "inline-flex items-center gap-8 ml-2",
142
+ children: [
143
+ indicator(index),
144
+ option.label
145
+ ]
146
+ })
147
+ ]
148
+ }, option.id);
149
+ })
150
+ })
118
151
  ]
119
152
  })
120
153
  ]
@@ -5,26 +5,11 @@
5
5
  * time period selector, and playback controls.
6
6
  */
7
7
  import type { ComponentProps, ReactNode } from 'react';
8
- import type { TimePeriod } from '../context';
9
8
  export interface PageToolbarProps extends Omit<ComponentProps<'div'>, 'title' | 'children'> {
10
9
  /**
11
10
  * The title to display in the toolbar header
12
11
  */
13
12
  title: ReactNode;
14
- /**
15
- * Last updated timestamp text
16
- * @default '13s ago'
17
- */
18
- lastUpdated?: string;
19
- /**
20
- * Current time period value
21
- * @default '2days'
22
- */
23
- timePeriod?: TimePeriod;
24
- /**
25
- * Callback when time period changes
26
- */
27
- onTimePeriodChange?: (value: TimePeriod) => void;
28
13
  /**
29
14
  * Callback when refresh button is clicked
30
15
  */
@@ -54,22 +39,25 @@ export interface PageToolbarProps extends Omit<ComponentProps<'div'>, 'title' |
54
39
  * Children to render (e.g., mobile menu button)
55
40
  */
56
41
  children?: ReactNode;
42
+ /**
43
+ * Custom container for the interval selector popover
44
+ */
45
+ intervalSelectorContainer?: HTMLElement | null;
57
46
  }
58
47
  /**
59
48
  * Generic Page Toolbar
60
49
  *
61
50
  * A flexible toolbar component that can be used across different dashboard pages.
62
- * Supports title customization, time period selection, refresh indicator, and playback controls.
51
+ * Supports title customization, time interval selection, refresh indicator, and playback controls.
52
+ * Uses DashboardContext for interval state management.
63
53
  *
64
54
  * @example
65
55
  * ```tsx
66
56
  * <PageToolbar
67
57
  * title="Analytics"
68
- * timePeriod="2days"
69
- * onTimePeriodChange={setTimePeriod}
70
58
  * onRefresh={handleRefresh}
71
59
  * />
72
60
  * ```
73
61
  */
74
- export declare function PageToolbar({ title, lastUpdated, timePeriod, onTimePeriodChange, onRefresh, showPlaybackControls, onRewind, onPlay, onSpeed, actions, children, className, ...props }: PageToolbarProps): import("react/jsx-runtime").JSX.Element;
62
+ export declare function PageToolbar({ title, onRefresh, showPlaybackControls, onRewind, onPlay, onSpeed, actions, children, intervalSelectorContainer, className, ...props }: PageToolbarProps): import("react/jsx-runtime").JSX.Element;
75
63
  //# sourceMappingURL=page-toolbar.d.ts.map
@@ -7,26 +7,26 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
7
  */ import { Button } from '../../../components/button/index.js';
8
8
  import { ButtonGroup, ButtonGroupSeparator } from '../../../components/button-group/index.js';
9
9
  import { Icon } from '../../../components/icon/index.js';
10
- import { Kbd } from '../../../components/kbd/index.js';
11
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../components/select/index.js';
10
+ import { IntervalSelector } from '../../../components/interval-selector/index.js';
12
11
  import { Header, Text } from '../../../components/typography/index.js';
13
12
  import { cn } from '../../../utils/cn.js';
13
+ import { useDashboardContext } from '../context/index.js';
14
14
  /**
15
15
  * Generic Page Toolbar
16
16
  *
17
17
  * A flexible toolbar component that can be used across different dashboard pages.
18
- * Supports title customization, time period selection, refresh indicator, and playback controls.
18
+ * Supports title customization, time interval selection, refresh indicator, and playback controls.
19
+ * Uses DashboardContext for interval state management.
19
20
  *
20
21
  * @example
21
22
  * ```tsx
22
23
  * <PageToolbar
23
24
  * title="Analytics"
24
- * timePeriod="2days"
25
- * onTimePeriodChange={setTimePeriod}
26
25
  * onRefresh={handleRefresh}
27
26
  * />
28
27
  * ```
29
- */ export function PageToolbar({ title, lastUpdated = '13s ago', timePeriod = '2days', onTimePeriodChange, onRefresh, showPlaybackControls = true, onRewind, onPlay, onSpeed, actions, children, className, ...props }) {
28
+ */ export function PageToolbar({ title, onRefresh, showPlaybackControls = true, onRewind, onPlay, onSpeed, actions, children, intervalSelectorContainer, className, ...props }) {
29
+ const { selection, setSelection, lastUpdated } = useDashboardContext();
30
30
  return /*#__PURE__*/ _jsxs("div", {
31
31
  className: cn('flex flex-wrap gap-12 items-center justify-between px-12 py-20 md:px-24 md:py-40', className),
32
32
  ...props,
@@ -66,99 +66,11 @@ import { cn } from '../../../utils/cn.js';
66
66
  ]
67
67
  })
68
68
  }),
69
- /*#__PURE__*/ _jsxs(Select, {
70
- value: timePeriod,
71
- onValueChange: onTimePeriodChange,
72
- children: [
73
- /*#__PURE__*/ _jsx(SelectTrigger, {
74
- className: "w-full md:w-280",
75
- children: /*#__PURE__*/ _jsx("div", {
76
- className: "flex items-center gap-8 flex-1 min-w-0",
77
- children: /*#__PURE__*/ _jsx(SelectValue, {
78
- placeholder: "Select time range"
79
- })
80
- })
81
- }),
82
- /*#__PURE__*/ _jsxs(SelectContent, {
83
- children: [
84
- /*#__PURE__*/ _jsx(SelectItem, {
85
- value: "1hour",
86
- children: /*#__PURE__*/ _jsxs("div", {
87
- className: "flex items-center gap-8",
88
- children: [
89
- /*#__PURE__*/ _jsx(Kbd, {
90
- className: "h-16",
91
- children: "1h"
92
- }),
93
- /*#__PURE__*/ _jsx("span", {
94
- children: "Past 1 Hour"
95
- })
96
- ]
97
- })
98
- }),
99
- /*#__PURE__*/ _jsx(SelectItem, {
100
- value: "1day",
101
- children: /*#__PURE__*/ _jsxs("div", {
102
- className: "flex items-center gap-8",
103
- children: [
104
- /*#__PURE__*/ _jsx(Kbd, {
105
- className: "h-16",
106
- children: "1d"
107
- }),
108
- /*#__PURE__*/ _jsx("span", {
109
- children: "Past 1 Day"
110
- })
111
- ]
112
- })
113
- }),
114
- /*#__PURE__*/ _jsx(SelectItem, {
115
- value: "2days",
116
- children: /*#__PURE__*/ _jsxs("div", {
117
- className: "flex items-center gap-8",
118
- children: [
119
- /*#__PURE__*/ _jsx(Kbd, {
120
- className: "h-16",
121
- children: "2d"
122
- }),
123
- /*#__PURE__*/ _jsx("span", {
124
- children: "Past 2 Days"
125
- })
126
- ]
127
- })
128
- }),
129
- /*#__PURE__*/ _jsx(SelectItem, {
130
- value: "7days",
131
- children: /*#__PURE__*/ _jsxs("div", {
132
- className: "flex items-center gap-8",
133
- children: [
134
- /*#__PURE__*/ _jsx(Kbd, {
135
- className: "h-16",
136
- children: "7d"
137
- }),
138
- /*#__PURE__*/ _jsx("span", {
139
- children: "Past 7 Days"
140
- })
141
- ]
142
- })
143
- }),
144
- /*#__PURE__*/ _jsx(SelectItem, {
145
- value: "30days",
146
- children: /*#__PURE__*/ _jsxs("div", {
147
- className: "flex items-center gap-8",
148
- children: [
149
- /*#__PURE__*/ _jsx(Kbd, {
150
- className: "h-16",
151
- children: "30d"
152
- }),
153
- /*#__PURE__*/ _jsx("span", {
154
- children: "Past 30 Days"
155
- })
156
- ]
157
- })
158
- })
159
- ]
160
- })
161
- ]
69
+ /*#__PURE__*/ _jsx(IntervalSelector, {
70
+ selection: selection,
71
+ onSelectionChange: setSelection,
72
+ container: intervalSelectorContainer,
73
+ className: "w-[75vw] md:w-350"
162
74
  }),
163
75
  showPlaybackControls && /*#__PURE__*/ _jsxs(ButtonGroup, {
164
76
  "aria-label": "Playback controls",
@@ -26,14 +26,14 @@ import { ViewDropdown } from './view-dropdown.js';
26
26
  * </ToolbarActions>
27
27
  * ```
28
28
  */ export function ToolbarActions({ showFilter = true, showSearch = true, showView = true, searchPlaceholder = 'Try: job name, status, pipeline...', className, children, ...props }) {
29
- const { setSearchQuery, filters, setFilters, columns, setColumns } = useDashboardContext();
29
+ const { setSearchQuery, resourceType, setResourceType, columns, setColumns } = useDashboardContext();
30
30
  return /*#__PURE__*/ _jsxs("div", {
31
31
  className: cn('flex items-start md:items-center gap-8 md:gap-12', className),
32
32
  ...props,
33
33
  children: [
34
34
  showFilter && /*#__PURE__*/ _jsx(FilterButton, {
35
- filters: filters,
36
- onFiltersChange: setFilters
35
+ value: resourceType,
36
+ onValueChange: setResourceType
37
37
  }),
38
38
  showSearch && /*#__PURE__*/ _jsx(ToolbarSearch, {
39
39
  placeholder: searchPlaceholder,
@@ -23,6 +23,7 @@ export * from './form';
23
23
  export * from './icon';
24
24
  export * from './inline-tips';
25
25
  export * from './input';
26
+ export * from './interval-selector';
26
27
  export * from './item';
27
28
  export * from './kbd';
28
29
  export * from './label';
@@ -23,6 +23,7 @@ export * from './form/index.js';
23
23
  export * from './icon/index.js';
24
24
  export * from './inline-tips/index.js';
25
25
  export * from './input/index.js';
26
+ export * from './interval-selector/index.js';
26
27
  export * from './item/index.js';
27
28
  export * from './kbd/index.js';
28
29
  export * from './label/index.js';
@@ -0,0 +1,4 @@
1
+ export * from './use-interval-selector';
2
+ export * from './use-interval-selector-input';
3
+ export * from './use-interval-selector-navigation';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,5 @@
1
+ export * from './use-interval-selector.js';
2
+ export * from './use-interval-selector-input.js';
3
+ export * from './use-interval-selector-navigation.js';
4
+
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,30 @@
1
+ import type { IntervalSelection } from '../types';
2
+ export interface UseNewIntervalSelectorInputProps {
3
+ onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
4
+ onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
5
+ onFocus?: () => void;
6
+ onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
7
+ onSelect: (selection: IntervalSelection) => void;
8
+ selection: IntervalSelection;
9
+ isNavigating: boolean;
10
+ inputRef: React.RefObject<HTMLInputElement | null>;
11
+ isFocused: boolean;
12
+ setIsFocused: (isFocused: boolean) => void;
13
+ }
14
+ export declare function useIntervalSelectorInput({ onChange: onChangeProp, onKeyDown: onKeyDownProp, onFocus: onFocusProp, onBlur: onBlurProp, onSelect, selection, isNavigating, inputRef, isFocused, setIsFocused, }: UseNewIntervalSelectorInputProps): {
15
+ value: string;
16
+ shouldShake: boolean;
17
+ isFocused: boolean;
18
+ isInvalid: boolean;
19
+ displayValue: string;
20
+ shortcutValue: string;
21
+ setValue: import("react").Dispatch<import("react").SetStateAction<string>>;
22
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
23
+ onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void;
24
+ onFocus: () => void;
25
+ onMouseDown: () => void;
26
+ onMouseUp: () => void;
27
+ onBlur: (e: React.FocusEvent<HTMLInputElement>) => void;
28
+ inputRef: import("react").RefObject<HTMLInputElement | null>;
29
+ };
30
+ //# sourceMappingURL=use-interval-selector-input.d.ts.map
@@ -0,0 +1,125 @@
1
+ import { useCallback, useRef, useState } from 'react';
2
+ import { parseTextDurationShortcut, parseTextInterval } from '../../../utils/date.js';
3
+ import { formatSelection, formatSelectionForInput, formatShortcut } from '../utils/format.js';
4
+ export function useIntervalSelectorInput({ onChange: onChangeProp, onKeyDown: onKeyDownProp, onFocus: onFocusProp, onBlur: onBlurProp, onSelect, selection, isNavigating, inputRef, isFocused, setIsFocused }) {
5
+ const [value, setValue] = useState('');
6
+ const [isInvalid, setIsInvalid] = useState(false);
7
+ const [shouldShake, setShouldShake] = useState(false);
8
+ const shakeTimeoutRef = useRef(null);
9
+ const isMouseDownOnInputRef = useRef(false);
10
+ const onChange = useCallback((e)=>{
11
+ const newValue = e.target.value;
12
+ setValue(newValue);
13
+ const shortcut = parseTextDurationShortcut(newValue);
14
+ const interval = parseTextInterval(newValue);
15
+ const isValid = shortcut || interval || newValue.trim() === '';
16
+ setIsInvalid(!isValid);
17
+ onChangeProp?.(e);
18
+ }, [
19
+ onChangeProp
20
+ ]);
21
+ const triggerShakeAnimation = useCallback(()=>{
22
+ if (shakeTimeoutRef.current) clearTimeout(shakeTimeoutRef.current);
23
+ setIsInvalid(true);
24
+ setShouldShake(true);
25
+ shakeTimeoutRef.current = setTimeout(()=>{
26
+ setShouldShake(false);
27
+ shakeTimeoutRef.current = null;
28
+ }, 500);
29
+ }, []);
30
+ const handleConfirmInput = useCallback(()=>{
31
+ const shortcut = parseTextDurationShortcut(value);
32
+ if (shortcut) return onSelect({
33
+ type: 'relative',
34
+ duration: shortcut
35
+ });
36
+ const interval = parseTextInterval(value);
37
+ if (interval) return onSelect({
38
+ type: 'interval',
39
+ interval
40
+ });
41
+ triggerShakeAnimation();
42
+ }, [
43
+ value,
44
+ onSelect,
45
+ triggerShakeAnimation
46
+ ]);
47
+ const onKeyDown = useCallback((e)=>{
48
+ if (!isNavigating && e.key === 'Enter') {
49
+ e.preventDefault();
50
+ handleConfirmInput();
51
+ }
52
+ onKeyDownProp?.(e);
53
+ }, [
54
+ isNavigating,
55
+ handleConfirmInput,
56
+ onKeyDownProp
57
+ ]);
58
+ const onFocus = useCallback(()=>{
59
+ setIsFocused(true);
60
+ setValue(formatSelectionForInput(selection));
61
+ setIsInvalid(false);
62
+ requestAnimationFrame(()=>{
63
+ inputRef.current?.select();
64
+ });
65
+ onFocusProp?.();
66
+ }, [
67
+ selection,
68
+ onFocusProp,
69
+ inputRef,
70
+ setIsFocused
71
+ ]);
72
+ const onMouseDown = useCallback(()=>{
73
+ isMouseDownOnInputRef.current = true;
74
+ }, []);
75
+ const onMouseUp = useCallback(()=>{
76
+ setTimeout(()=>{
77
+ isMouseDownOnInputRef.current = false;
78
+ });
79
+ }, []);
80
+ const onBlur = useCallback((e)=>{
81
+ const relatedTarget = e.relatedTarget;
82
+ if (relatedTarget?.closest('[role="dialog"]')) {
83
+ return;
84
+ }
85
+ if (isMouseDownOnInputRef.current) {
86
+ requestAnimationFrame(()=>{
87
+ inputRef.current?.focus();
88
+ });
89
+ }
90
+ setIsFocused(false);
91
+ onBlurProp?.(e);
92
+ }, [
93
+ onBlurProp,
94
+ inputRef,
95
+ setIsFocused
96
+ ]);
97
+ const displayValue = formatSelection({
98
+ selection,
99
+ isFocused: isFocused,
100
+ inputValue: value
101
+ });
102
+ const shortcutValue = formatShortcut({
103
+ selection,
104
+ inputValue: value,
105
+ isFocused: isFocused
106
+ });
107
+ return {
108
+ value,
109
+ shouldShake,
110
+ isFocused,
111
+ isInvalid,
112
+ displayValue,
113
+ shortcutValue,
114
+ setValue,
115
+ onChange,
116
+ onKeyDown,
117
+ onFocus,
118
+ onMouseDown,
119
+ onMouseUp,
120
+ onBlur,
121
+ inputRef
122
+ };
123
+ }
124
+
125
+ //# sourceMappingURL=use-interval-selector-input.js.map
@@ -0,0 +1,21 @@
1
+ import type { IntervalSelection, IntervalSuggestion, RelativeSuggestion } from '../types';
2
+ interface UseIntervalSelectorNavigationProps {
3
+ relativeSuggestions: RelativeSuggestion[];
4
+ intervalSuggestions: IntervalSuggestion[];
5
+ highlightedIndex: number;
6
+ setHighlightedIndex: (index: number) => void;
7
+ popoverOpen: boolean;
8
+ calendarOpen: boolean;
9
+ onOpenCalendar: () => void;
10
+ onSelect: (selection: IntervalSelection) => void;
11
+ }
12
+ export declare function useIntervalSelectorNavigation({ relativeSuggestions, intervalSuggestions, highlightedIndex, setHighlightedIndex, popoverOpen, calendarOpen, onOpenCalendar, onSelect, }: UseIntervalSelectorNavigationProps): {
13
+ allNavigableItems: (RelativeSuggestion | IntervalSuggestion | {
14
+ type: "calendar";
15
+ label: string;
16
+ })[];
17
+ onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void;
18
+ isNavigating: boolean;
19
+ };
20
+ export {};
21
+ //# sourceMappingURL=use-interval-selector-navigation.d.ts.map