@lateralus-ai/shipping-ui 1.4.12 → 1.4.14

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 (64) hide show
  1. package/README.md +108 -108
  2. package/dist/index.cjs +38 -38
  3. package/dist/index.esm.js +4936 -4913
  4. package/dist/tailwind-theme.d.ts +12 -0
  5. package/package.json +99 -99
  6. package/src/components/DocumentEditor/DocumentEditor.tsx +871 -871
  7. package/src/components/HelloWorld.tsx +3 -3
  8. package/src/components/InputPrompt.tsx +96 -96
  9. package/src/components/ModalPanel.tsx +49 -49
  10. package/src/components/PdfViewer/ImageViewer.tsx +211 -211
  11. package/src/components/PdfViewer/PdfViewer.tsx +232 -209
  12. package/src/components/PdfViewer/index.ts +2 -2
  13. package/src/components/PdfViewer/usePageManagement.ts +32 -32
  14. package/src/components/PdfViewer/usePanning.ts +42 -42
  15. package/src/components/PdfViewer/useRefDimensions.ts +16 -16
  16. package/src/components/PdfViewer/useRotation.ts +13 -13
  17. package/src/components/PdfViewer/useZoom.ts +26 -26
  18. package/src/components/SearchModal.tsx +320 -320
  19. package/src/components/Sidebar/Button.tsx +20 -20
  20. package/src/components/Sidebar/Container.tsx +31 -31
  21. package/src/components/Sidebar/Item.tsx +39 -39
  22. package/src/components/Sidebar/Layout.tsx +32 -32
  23. package/src/components/Sidebar/Provider.tsx +47 -47
  24. package/src/components/Sidebar/SecondaryItem.tsx +39 -39
  25. package/src/components/Sidebar/SideContainer.tsx +31 -31
  26. package/src/components/Sidebar/ToggleCollapseButton.tsx +24 -24
  27. package/src/components/Sidebar/index.ts +8 -8
  28. package/src/components/Tabs.tsx +43 -43
  29. package/src/components/icons/CloseSidebarIcon.tsx +19 -19
  30. package/src/components/icons/CloseSidebarMidIcon.tsx +19 -19
  31. package/src/components/icons/ExpandIcon.tsx +21 -21
  32. package/src/components/icons/SendArrowIcon.tsx +23 -23
  33. package/src/components/icons/SendArrowIconGreen.tsx +17 -17
  34. package/src/components/icons/SettingsIcon.tsx +33 -33
  35. package/src/components/icons/XIcon.tsx +21 -21
  36. package/src/components/index.ts +6 -6
  37. package/src/index.ts +4 -4
  38. package/src/material-theme.ts +477 -477
  39. package/src/stories/Buttons.stories.tsx +15 -15
  40. package/src/stories/Buttons.tsx +52 -52
  41. package/src/stories/Checkbox.stories.tsx +15 -15
  42. package/src/stories/Checkbox.tsx +56 -56
  43. package/src/stories/ColorPalette.stories.tsx +15 -15
  44. package/src/stories/ColorPalette.tsx +85 -72
  45. package/src/stories/DocumentEditor.stories.tsx +287 -287
  46. package/src/stories/Dropdowns.stories.tsx +15 -15
  47. package/src/stories/Dropdowns.tsx +52 -52
  48. package/src/stories/InputPrompt.stories.tsx +15 -15
  49. package/src/stories/InputPrompt.tsx +63 -63
  50. package/src/stories/ModalHeader.stories.tsx +35 -35
  51. package/src/stories/PDFViewer.stories.tsx +39 -39
  52. package/src/stories/SearchModal.stories.tsx +132 -132
  53. package/src/stories/SearchModal.tsx +82 -82
  54. package/src/stories/Sidebar.stories.tsx +15 -15
  55. package/src/stories/Sidebar.tsx +108 -108
  56. package/src/stories/Tabs.stories.tsx +51 -51
  57. package/src/stories/Typography.stories.tsx +15 -15
  58. package/src/stories/Typography.tsx +110 -110
  59. package/src/style.css +2 -2
  60. package/src/styles/tabs.css +55 -55
  61. package/src/tailwind-theme.ts +243 -231
  62. package/src/types/documentEditor.ts +55 -55
  63. package/src/utils/checkboxModule.ts +241 -241
  64. package/src/utils/cn.ts +11 -11
@@ -1,320 +1,320 @@
1
- import { Dialog, DialogBody } from "@material-tailwind/react";
2
- import { useEffect, useRef, useState } from "react";
3
- import { useHotkeys } from "react-hotkeys-hook";
4
- import { useMediaQuery } from "@uidotdev/usehooks";
5
- import { cn } from "../utils/cn";
6
-
7
- export const formatDateReport = (date: string | Date) => {
8
- const formattedDate = new Date(date);
9
- return formattedDate.toLocaleDateString("en-US", {
10
- month: "long",
11
- day: "numeric",
12
- year: "numeric",
13
- });
14
- };
15
-
16
- const highlightText = (text: string, searchTerm: string) => {
17
- if (!searchTerm) return text;
18
-
19
- const regex = new RegExp(`(\\b${searchTerm}\\b)`, "gi");
20
- const parts = text.split(regex);
21
- const firstMatchIndex = parts.findIndex((part) => regex.test(part));
22
-
23
- if (firstMatchIndex === -1) return text;
24
-
25
- if (firstMatchIndex === 0) {
26
- return parts.map((part, i) =>
27
- regex.test(part) ? (
28
- <span key={i} className="text-gray-900 font-medium">
29
- {part}
30
- </span>
31
- ) : (
32
- part
33
- ),
34
- );
35
- }
36
-
37
- const matchedPart = parts[firstMatchIndex];
38
- const beforeMatch = parts.slice(0, firstMatchIndex).join("");
39
- const afterMatch = parts.slice(firstMatchIndex + 1).join("");
40
-
41
- return (
42
- <>
43
- <span key="match" className="text-gray-900 font-medium">
44
- {beforeMatch && <>...</>}
45
- {matchedPart}
46
- </span>
47
- {afterMatch && <span key="after"> {afterMatch}</span>}
48
- </>
49
- );
50
- };
51
-
52
- export interface SearchItem {
53
- id: string;
54
- title: string;
55
- subtitle?: string;
56
- icon?: React.ReactNode;
57
- metadata?: Record<string, any>;
58
- }
59
-
60
- export interface SearchModalProps {
61
- open: boolean;
62
- onClose: () => void;
63
- items?: SearchItem[];
64
- loading?: boolean;
65
- placeholder?: string;
66
- onSearch?: (query: string) => void;
67
- onSelect?: (item: SearchItem) => void;
68
- filterOptions?: string[];
69
- selectedFilter?: string;
70
- onFilterChange?: (filter: string) => void;
71
- recentlyViewedLabel?: string;
72
- noResultsText?: string;
73
- }
74
-
75
- const SearchFilters = ({
76
- filters,
77
- selected,
78
- onChange,
79
- }: {
80
- filters: string[];
81
- selected: string;
82
- onChange: (filter: string) => void;
83
- }) => {
84
- return (
85
- <div className="flex gap-2 px-2">
86
- {filters.map((filter) => (
87
- <button
88
- key={filter}
89
- onClick={() => onChange(filter)}
90
- className={cn(
91
- "px-3 py-1 text-sm rounded-full transition-colors",
92
- selected === filter
93
- ? "bg-brand-600 text-white"
94
- : "bg-gray-100 text-gray-600 hover:bg-gray-200",
95
- )}
96
- >
97
- {filter}
98
- </button>
99
- ))}
100
- </div>
101
- );
102
- };
103
-
104
- const SearchSkeleton = () => {
105
- return (
106
- <div className="p-4 flex flex-col gap-[6px]">
107
- {[...Array(8)].map((_, i) => (
108
- <div key={i} className="flex items-center gap-3 animate-pulse py-2">
109
- <div className="size-3 bg-gray-100 rounded-full" />
110
- <div className={cn(i % 2 === 0 ? "w-[250px]" : "w-[320px]","h-3 bg-gray-100 rounded-full")} />
111
- </div>
112
- ))}
113
- </div>
114
- );
115
- };
116
-
117
- const SearchItemComponent = ({
118
- item,
119
- isActive,
120
- onClick,
121
- searchTerm,
122
- }: {
123
- item: SearchItem;
124
- isActive: boolean;
125
- onClick: () => void;
126
- searchTerm: string;
127
- }) => {
128
- return (
129
- <div
130
- onClick={onClick}
131
- className={cn(
132
- "flex items-center gap-3 p-2 rounded-lg cursor-pointer transition-colors",
133
- isActive ? "bg-gray-100" : "hover:bg-gray-50",
134
- )}
135
- >
136
- {item.icon && <div className="shrink-0 text-gray-600">{item.icon}</div>}
137
- <div className="flex-1 min-w-0">
138
- <div className="text-sm font-medium text-gray-900 truncate">
139
- {item.title}
140
- </div>
141
- {item.subtitle && (
142
- <div className="text-sm text-gray-600 truncate">
143
- {typeof item.subtitle === "string"
144
- ? highlightText(item.subtitle, searchTerm)
145
- : item.subtitle}
146
- </div>
147
- )}
148
- </div>
149
- </div>
150
- );
151
- };
152
-
153
- const SearchModal = ({
154
- open,
155
- onClose,
156
- items = [],
157
- loading = false,
158
- placeholder = "Search...",
159
- onSearch,
160
- onSelect,
161
- filterOptions = [],
162
- selectedFilter = "All",
163
- onFilterChange,
164
- recentlyViewedLabel = "Recently viewed",
165
- noResultsText = "No results found.",
166
- }: SearchModalProps) => {
167
- const [searchValue, setSearchValue] = useState("");
168
- const [activeIndex, setActiveIndex] = useState(-1);
169
- const isMobile = useMediaQuery("only screen and (max-width: 768px)");
170
- const itemRefs = useRef<HTMLDivElement[]>([]);
171
-
172
- useEffect(() => {
173
- itemRefs.current = itemRefs.current.slice(0, items.length);
174
- }, [items]);
175
-
176
- useEffect(() => {
177
- if (activeIndex > items.length) {
178
- setActiveIndex(-1);
179
- }
180
- }, [items, activeIndex]);
181
-
182
- useEffect(() => {
183
- const currentItem = itemRefs.current[activeIndex];
184
- currentItem?.scrollIntoView({
185
- behavior: "smooth",
186
- block: "nearest",
187
- });
188
- }, [activeIndex]);
189
-
190
- useHotkeys(
191
- "ArrowUp",
192
- () => {
193
- if (items.length === 0) return;
194
- setActiveIndex((prev) => (prev <= 0 ? items.length - 1 : prev - 1));
195
- },
196
- {
197
- preventDefault: true,
198
- enableOnFormTags: true,
199
- enabled: open,
200
- },
201
- );
202
-
203
- useHotkeys(
204
- "ArrowDown",
205
- () => {
206
- if (items.length === 0) return;
207
- setActiveIndex((prev) => (prev >= items.length - 1 ? 0 : prev + 1));
208
- },
209
- {
210
- preventDefault: true,
211
- enableOnFormTags: true,
212
- enabled: open,
213
- },
214
- );
215
-
216
- useHotkeys(
217
- "Enter",
218
- () => {
219
- if (activeIndex >= 0 && activeIndex < items.length) {
220
- handleItemSelect(items[activeIndex]);
221
- }
222
- },
223
- {
224
- enableOnFormTags: true,
225
- preventDefault: true,
226
- enabled: open,
227
- },
228
- );
229
-
230
- const handleSearchChange = (value: string) => {
231
- setSearchValue(value);
232
- onSearch?.(value);
233
- };
234
-
235
- const handleItemSelect = (item: SearchItem) => {
236
- onSelect?.(item);
237
- handleClose();
238
- };
239
-
240
- const handleClose = () => {
241
- setSearchValue("");
242
- setActiveIndex(-1);
243
- onClose();
244
- };
245
-
246
- const showFilters = filterOptions.length > 0 && searchValue;
247
- const showRecentlyViewed = !searchValue && items.length > 0;
248
-
249
- return (
250
- <Dialog
251
- open={open}
252
- size="lg"
253
- handler={handleClose}
254
- className={cn(
255
- isMobile && "w-screen m-0",
256
- "focus:outline-none 2xl:min-w-px",
257
- )}
258
- >
259
- <DialogBody className="relative px-0 py-2">
260
- <div>
261
- <div className="border-b border-b-gray-100 px-6 pb-2">
262
- <input
263
- type="text"
264
- placeholder={placeholder}
265
- value={searchValue}
266
- onChange={(e) => handleSearchChange(e.target.value)}
267
- className="w-full bg-transparent p-2 px-0 text-sm font-medium text-gray-600 placeholder-gray-400 outline-none focus:outline-none"
268
- />
269
- </div>
270
-
271
- {loading ? (
272
- <SearchSkeleton />
273
- ) : (
274
- <div className="p-4 flex flex-col gap-2">
275
- {showRecentlyViewed && (
276
- <p className="text-sm font-normal text-gray-600 px-2">
277
- {recentlyViewedLabel}
278
- </p>
279
- )}
280
-
281
- {showFilters && (
282
- <SearchFilters
283
- filters={filterOptions}
284
- selected={selectedFilter}
285
- onChange={(filter) => onFilterChange?.(filter)}
286
- />
287
- )}
288
-
289
- <div className="overflow-y-auto max-h-[320px]" tabIndex={0}>
290
- {items.map((item, index) => (
291
- <div
292
- key={item.id}
293
- ref={(el) => {
294
- if (el) itemRefs.current[index] = el;
295
- }}
296
- >
297
- <SearchItemComponent
298
- item={item}
299
- isActive={activeIndex === index}
300
- onClick={() => handleItemSelect(item)}
301
- searchTerm={searchValue}
302
- />
303
- </div>
304
- ))}
305
-
306
- {!loading && items.length === 0 && (
307
- <div className="text-sm text-gray-900 font-medium">
308
- {noResultsText}
309
- </div>
310
- )}
311
- </div>
312
- </div>
313
- )}
314
- </div>
315
- </DialogBody>
316
- </Dialog>
317
- );
318
- };
319
-
320
- export default SearchModal;
1
+ import { Dialog, DialogBody } from "@material-tailwind/react";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { useHotkeys } from "react-hotkeys-hook";
4
+ import { useMediaQuery } from "@uidotdev/usehooks";
5
+ import { cn } from "../utils/cn";
6
+
7
+ export const formatDateReport = (date: string | Date) => {
8
+ const formattedDate = new Date(date);
9
+ return formattedDate.toLocaleDateString("en-US", {
10
+ month: "long",
11
+ day: "numeric",
12
+ year: "numeric",
13
+ });
14
+ };
15
+
16
+ const highlightText = (text: string, searchTerm: string) => {
17
+ if (!searchTerm) return text;
18
+
19
+ const regex = new RegExp(`(\\b${searchTerm}\\b)`, "gi");
20
+ const parts = text.split(regex);
21
+ const firstMatchIndex = parts.findIndex((part) => regex.test(part));
22
+
23
+ if (firstMatchIndex === -1) return text;
24
+
25
+ if (firstMatchIndex === 0) {
26
+ return parts.map((part, i) =>
27
+ regex.test(part) ? (
28
+ <span key={i} className="text-gray-900 font-medium">
29
+ {part}
30
+ </span>
31
+ ) : (
32
+ part
33
+ ),
34
+ );
35
+ }
36
+
37
+ const matchedPart = parts[firstMatchIndex];
38
+ const beforeMatch = parts.slice(0, firstMatchIndex).join("");
39
+ const afterMatch = parts.slice(firstMatchIndex + 1).join("");
40
+
41
+ return (
42
+ <>
43
+ <span key="match" className="text-gray-900 font-medium">
44
+ {beforeMatch && <>...</>}
45
+ {matchedPart}
46
+ </span>
47
+ {afterMatch && <span key="after"> {afterMatch}</span>}
48
+ </>
49
+ );
50
+ };
51
+
52
+ export interface SearchItem {
53
+ id: string;
54
+ title: string;
55
+ subtitle?: string;
56
+ icon?: React.ReactNode;
57
+ metadata?: Record<string, any>;
58
+ }
59
+
60
+ export interface SearchModalProps {
61
+ open: boolean;
62
+ onClose: () => void;
63
+ items?: SearchItem[];
64
+ loading?: boolean;
65
+ placeholder?: string;
66
+ onSearch?: (query: string) => void;
67
+ onSelect?: (item: SearchItem) => void;
68
+ filterOptions?: string[];
69
+ selectedFilter?: string;
70
+ onFilterChange?: (filter: string) => void;
71
+ recentlyViewedLabel?: string;
72
+ noResultsText?: string;
73
+ }
74
+
75
+ const SearchFilters = ({
76
+ filters,
77
+ selected,
78
+ onChange,
79
+ }: {
80
+ filters: string[];
81
+ selected: string;
82
+ onChange: (filter: string) => void;
83
+ }) => {
84
+ return (
85
+ <div className="flex gap-2 px-2">
86
+ {filters.map((filter) => (
87
+ <button
88
+ key={filter}
89
+ onClick={() => onChange(filter)}
90
+ className={cn(
91
+ "px-3 py-1 text-sm rounded-full transition-colors",
92
+ selected === filter
93
+ ? "bg-brand-600 text-white"
94
+ : "bg-gray-100 text-gray-600 hover:bg-gray-200",
95
+ )}
96
+ >
97
+ {filter}
98
+ </button>
99
+ ))}
100
+ </div>
101
+ );
102
+ };
103
+
104
+ const SearchSkeleton = () => {
105
+ return (
106
+ <div className="p-4 flex flex-col gap-[6px]">
107
+ {[...Array(8)].map((_, i) => (
108
+ <div key={i} className="flex items-center gap-3 animate-pulse py-2">
109
+ <div className="size-3 bg-gray-100 rounded-full" />
110
+ <div className={cn(i % 2 === 0 ? "w-[250px]" : "w-[320px]","h-3 bg-gray-100 rounded-full")} />
111
+ </div>
112
+ ))}
113
+ </div>
114
+ );
115
+ };
116
+
117
+ const SearchItemComponent = ({
118
+ item,
119
+ isActive,
120
+ onClick,
121
+ searchTerm,
122
+ }: {
123
+ item: SearchItem;
124
+ isActive: boolean;
125
+ onClick: () => void;
126
+ searchTerm: string;
127
+ }) => {
128
+ return (
129
+ <div
130
+ onClick={onClick}
131
+ className={cn(
132
+ "flex items-center gap-3 p-2 rounded-lg cursor-pointer transition-colors",
133
+ isActive ? "bg-gray-100" : "hover:bg-gray-50",
134
+ )}
135
+ >
136
+ {item.icon && <div className="shrink-0 text-gray-600">{item.icon}</div>}
137
+ <div className="flex-1 min-w-0">
138
+ <div className="text-sm font-medium text-gray-900 truncate">
139
+ {item.title}
140
+ </div>
141
+ {item.subtitle && (
142
+ <div className="text-sm text-gray-600 truncate">
143
+ {typeof item.subtitle === "string"
144
+ ? highlightText(item.subtitle, searchTerm)
145
+ : item.subtitle}
146
+ </div>
147
+ )}
148
+ </div>
149
+ </div>
150
+ );
151
+ };
152
+
153
+ const SearchModal = ({
154
+ open,
155
+ onClose,
156
+ items = [],
157
+ loading = false,
158
+ placeholder = "Search...",
159
+ onSearch,
160
+ onSelect,
161
+ filterOptions = [],
162
+ selectedFilter = "All",
163
+ onFilterChange,
164
+ recentlyViewedLabel = "Recently viewed",
165
+ noResultsText = "No results found.",
166
+ }: SearchModalProps) => {
167
+ const [searchValue, setSearchValue] = useState("");
168
+ const [activeIndex, setActiveIndex] = useState(-1);
169
+ const isMobile = useMediaQuery("only screen and (max-width: 768px)");
170
+ const itemRefs = useRef<HTMLDivElement[]>([]);
171
+
172
+ useEffect(() => {
173
+ itemRefs.current = itemRefs.current.slice(0, items.length);
174
+ }, [items]);
175
+
176
+ useEffect(() => {
177
+ if (activeIndex > items.length) {
178
+ setActiveIndex(-1);
179
+ }
180
+ }, [items, activeIndex]);
181
+
182
+ useEffect(() => {
183
+ const currentItem = itemRefs.current[activeIndex];
184
+ currentItem?.scrollIntoView({
185
+ behavior: "smooth",
186
+ block: "nearest",
187
+ });
188
+ }, [activeIndex]);
189
+
190
+ useHotkeys(
191
+ "ArrowUp",
192
+ () => {
193
+ if (items.length === 0) return;
194
+ setActiveIndex((prev) => (prev <= 0 ? items.length - 1 : prev - 1));
195
+ },
196
+ {
197
+ preventDefault: true,
198
+ enableOnFormTags: true,
199
+ enabled: open,
200
+ },
201
+ );
202
+
203
+ useHotkeys(
204
+ "ArrowDown",
205
+ () => {
206
+ if (items.length === 0) return;
207
+ setActiveIndex((prev) => (prev >= items.length - 1 ? 0 : prev + 1));
208
+ },
209
+ {
210
+ preventDefault: true,
211
+ enableOnFormTags: true,
212
+ enabled: open,
213
+ },
214
+ );
215
+
216
+ useHotkeys(
217
+ "Enter",
218
+ () => {
219
+ if (activeIndex >= 0 && activeIndex < items.length) {
220
+ handleItemSelect(items[activeIndex]);
221
+ }
222
+ },
223
+ {
224
+ enableOnFormTags: true,
225
+ preventDefault: true,
226
+ enabled: open,
227
+ },
228
+ );
229
+
230
+ const handleSearchChange = (value: string) => {
231
+ setSearchValue(value);
232
+ onSearch?.(value);
233
+ };
234
+
235
+ const handleItemSelect = (item: SearchItem) => {
236
+ onSelect?.(item);
237
+ handleClose();
238
+ };
239
+
240
+ const handleClose = () => {
241
+ setSearchValue("");
242
+ setActiveIndex(-1);
243
+ onClose();
244
+ };
245
+
246
+ const showFilters = filterOptions.length > 0 && searchValue;
247
+ const showRecentlyViewed = !searchValue && items.length > 0;
248
+
249
+ return (
250
+ <Dialog
251
+ open={open}
252
+ size="lg"
253
+ handler={handleClose}
254
+ className={cn(
255
+ isMobile && "w-screen m-0",
256
+ "focus:outline-none 2xl:min-w-px",
257
+ )}
258
+ >
259
+ <DialogBody className="relative px-0 py-2">
260
+ <div>
261
+ <div className="border-b border-b-gray-100 px-6 pb-2">
262
+ <input
263
+ type="text"
264
+ placeholder={placeholder}
265
+ value={searchValue}
266
+ onChange={(e) => handleSearchChange(e.target.value)}
267
+ className="w-full bg-transparent p-2 px-0 text-sm font-medium text-gray-600 placeholder-gray-400 outline-none focus:outline-none"
268
+ />
269
+ </div>
270
+
271
+ {loading ? (
272
+ <SearchSkeleton />
273
+ ) : (
274
+ <div className="p-4 flex flex-col gap-2">
275
+ {showRecentlyViewed && (
276
+ <p className="text-sm font-normal text-gray-600 px-2">
277
+ {recentlyViewedLabel}
278
+ </p>
279
+ )}
280
+
281
+ {showFilters && (
282
+ <SearchFilters
283
+ filters={filterOptions}
284
+ selected={selectedFilter}
285
+ onChange={(filter) => onFilterChange?.(filter)}
286
+ />
287
+ )}
288
+
289
+ <div className="overflow-y-auto max-h-[320px]" tabIndex={0}>
290
+ {items.map((item, index) => (
291
+ <div
292
+ key={item.id}
293
+ ref={(el) => {
294
+ if (el) itemRefs.current[index] = el;
295
+ }}
296
+ >
297
+ <SearchItemComponent
298
+ item={item}
299
+ isActive={activeIndex === index}
300
+ onClick={() => handleItemSelect(item)}
301
+ searchTerm={searchValue}
302
+ />
303
+ </div>
304
+ ))}
305
+
306
+ {!loading && items.length === 0 && (
307
+ <div className="text-sm text-gray-900 font-medium">
308
+ {noResultsText}
309
+ </div>
310
+ )}
311
+ </div>
312
+ </div>
313
+ )}
314
+ </div>
315
+ </DialogBody>
316
+ </Dialog>
317
+ );
318
+ };
319
+
320
+ export default SearchModal;
@@ -1,20 +1,20 @@
1
- import { useContext, type ReactNode } from "react";
2
- import { collapsedContext } from "./Provider";
3
-
4
- interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
5
- children?: ReactNode;
6
- icon?: ReactNode;
7
- }
8
-
9
- export const Button = ({ icon, children, ...props }: ButtonProps) => {
10
- const { isCollapsed, isHovered } = useContext(collapsedContext)!;
11
-
12
- return (
13
- <button
14
- className="flex items-center gap-2 w-full hover:bg-gray-100 text-caption-1-em text-gray-600 hover:text-gray-900 h-[38px] rounded-lg px-2"
15
- {...props}
16
- >
17
- {icon} {(!isCollapsed || isHovered) && children}
18
- </button>
19
- );
20
- };
1
+ import { useContext, type ReactNode } from "react";
2
+ import { collapsedContext } from "./Provider";
3
+
4
+ interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
5
+ children?: ReactNode;
6
+ icon?: ReactNode;
7
+ }
8
+
9
+ export const Button = ({ icon, children, ...props }: ButtonProps) => {
10
+ const { isCollapsed, isHovered } = useContext(collapsedContext)!;
11
+
12
+ return (
13
+ <button
14
+ className="flex items-center gap-2 w-full hover:bg-gray-100 text-caption-1-em text-gray-600 hover:text-gray-900 h-[38px] rounded-lg px-2"
15
+ {...props}
16
+ >
17
+ {icon} {(!isCollapsed || isHovered) && children}
18
+ </button>
19
+ );
20
+ };