@gofreego/tsutils 0.1.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/LICENSE +21 -0
- package/README.md +253 -0
- package/dist/index.d.mts +555 -0
- package/dist/index.d.ts +555 -0
- package/dist/index.js +1178 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1153 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +80 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1153 @@
|
|
|
1
|
+
import { createContext, useState, useMemo, useEffect, useCallback, useContext } from 'react';
|
|
2
|
+
import { createTheme, ThemeProvider as ThemeProvider$1, CssBaseline, IconButton, Tooltip, Box, Drawer, List, Typography, ListItem, ListItemButton, ListItemIcon, ListItemText, Collapse } from '@mui/material';
|
|
3
|
+
import { Brightness4, Brightness7, ExpandLess, ExpandMore } from '@mui/icons-material';
|
|
4
|
+
import { BrowserRouter, useLocation, Routes, Route, Outlet, NavLink } from 'react-router-dom';
|
|
5
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var RouterMenuItem = ({ item, depth = 0, expanded, onToggle }) => {
|
|
13
|
+
const hasChildren = item.children && item.children.length > 0;
|
|
14
|
+
const isExpanded = expanded.has(item.id);
|
|
15
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
16
|
+
/* @__PURE__ */ jsx(ListItem, { disablePadding: true, children: /* @__PURE__ */ jsxs(
|
|
17
|
+
ListItemButton,
|
|
18
|
+
{
|
|
19
|
+
component: hasChildren ? "div" : NavLink,
|
|
20
|
+
to: !hasChildren ? item.path || `/${item.id}` : void 0,
|
|
21
|
+
onClick: hasChildren ? () => onToggle(item.id) : void 0,
|
|
22
|
+
sx: {
|
|
23
|
+
pl: 2 + depth * 2,
|
|
24
|
+
"&.active": {
|
|
25
|
+
backgroundColor: "action.selected",
|
|
26
|
+
color: "primary.main",
|
|
27
|
+
borderLeft: 3,
|
|
28
|
+
borderColor: "primary.main",
|
|
29
|
+
fontWeight: "medium",
|
|
30
|
+
"& .MuiListItemIcon-root": {
|
|
31
|
+
color: "primary.main"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"&:hover": {
|
|
35
|
+
backgroundColor: "action.hover"
|
|
36
|
+
},
|
|
37
|
+
transition: "all 0.15s"
|
|
38
|
+
},
|
|
39
|
+
children: [
|
|
40
|
+
item.icon && /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 40 }, children: item.icon }),
|
|
41
|
+
/* @__PURE__ */ jsx(
|
|
42
|
+
ListItemText,
|
|
43
|
+
{
|
|
44
|
+
primary: item.label,
|
|
45
|
+
primaryTypographyProps: {
|
|
46
|
+
fontSize: "0.875rem"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
),
|
|
50
|
+
hasChildren && /* @__PURE__ */ jsx(IconButton, { size: "small", sx: { p: 0 }, children: isExpanded ? /* @__PURE__ */ jsx(ExpandLess, {}) : /* @__PURE__ */ jsx(ExpandMore, {}) })
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
) }),
|
|
54
|
+
hasChildren && /* @__PURE__ */ jsx(Collapse, { in: isExpanded, timeout: "auto", unmountOnExit: true, children: /* @__PURE__ */ jsx(List, { component: "div", disablePadding: true, children: item.children.map((child) => /* @__PURE__ */ jsx(
|
|
55
|
+
RouterMenuItem,
|
|
56
|
+
{
|
|
57
|
+
item: child,
|
|
58
|
+
depth: depth + 1,
|
|
59
|
+
expanded,
|
|
60
|
+
onToggle
|
|
61
|
+
},
|
|
62
|
+
child.id
|
|
63
|
+
)) }) })
|
|
64
|
+
] });
|
|
65
|
+
};
|
|
66
|
+
var flattenMenuRoutes = (items) => {
|
|
67
|
+
const routes = [];
|
|
68
|
+
const flatten = (menuItems) => {
|
|
69
|
+
for (const item of menuItems) {
|
|
70
|
+
if (item.path && item.component) {
|
|
71
|
+
routes.push({ path: item.path, component: item.component });
|
|
72
|
+
}
|
|
73
|
+
if (item.children) {
|
|
74
|
+
flatten(item.children);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
flatten(items);
|
|
79
|
+
return routes;
|
|
80
|
+
};
|
|
81
|
+
var SidebarLayoutRouterInner = ({
|
|
82
|
+
menuItems,
|
|
83
|
+
sidebarWidth = 250,
|
|
84
|
+
className = "",
|
|
85
|
+
onMenuChange,
|
|
86
|
+
style,
|
|
87
|
+
sidebarStyle,
|
|
88
|
+
bodyStyle,
|
|
89
|
+
defaultExpanded = []
|
|
90
|
+
}) => {
|
|
91
|
+
const location = useLocation();
|
|
92
|
+
const [expanded, setExpanded] = useState(new Set(defaultExpanded));
|
|
93
|
+
const handleToggle = (id) => {
|
|
94
|
+
setExpanded((prev) => {
|
|
95
|
+
const next = new Set(prev);
|
|
96
|
+
if (next.has(id)) {
|
|
97
|
+
next.delete(id);
|
|
98
|
+
} else {
|
|
99
|
+
next.add(id);
|
|
100
|
+
}
|
|
101
|
+
return next;
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
if (onMenuChange) {
|
|
106
|
+
const findActiveItem = (items) => {
|
|
107
|
+
for (const item of items) {
|
|
108
|
+
if (item.path && location.pathname === item.path) {
|
|
109
|
+
return item;
|
|
110
|
+
}
|
|
111
|
+
if (item.children) {
|
|
112
|
+
const found = findActiveItem(item.children);
|
|
113
|
+
if (found) return found;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return void 0;
|
|
117
|
+
};
|
|
118
|
+
const activeItem = findActiveItem(menuItems);
|
|
119
|
+
if (activeItem) {
|
|
120
|
+
onMenuChange(activeItem.id);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}, [location.pathname, menuItems, onMenuChange]);
|
|
124
|
+
const hasComponents = menuItems.some(
|
|
125
|
+
(item) => item.component || item.children?.some((child) => child.component)
|
|
126
|
+
);
|
|
127
|
+
const routes = hasComponents ? flattenMenuRoutes(menuItems) : [];
|
|
128
|
+
return /* @__PURE__ */ jsxs(
|
|
129
|
+
Box,
|
|
130
|
+
{
|
|
131
|
+
className,
|
|
132
|
+
sx: {
|
|
133
|
+
display: "flex",
|
|
134
|
+
height: "100%",
|
|
135
|
+
width: "100%",
|
|
136
|
+
...style
|
|
137
|
+
},
|
|
138
|
+
children: [
|
|
139
|
+
/* @__PURE__ */ jsx(
|
|
140
|
+
Drawer,
|
|
141
|
+
{
|
|
142
|
+
variant: "permanent",
|
|
143
|
+
sx: {
|
|
144
|
+
width: sidebarWidth,
|
|
145
|
+
flexShrink: 0,
|
|
146
|
+
"& .MuiDrawer-paper": {
|
|
147
|
+
width: sidebarWidth,
|
|
148
|
+
boxSizing: "border-box",
|
|
149
|
+
position: "relative",
|
|
150
|
+
borderRight: "1px solid",
|
|
151
|
+
borderColor: "divider",
|
|
152
|
+
...sidebarStyle
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
children: /* @__PURE__ */ jsx(List, { sx: { p: 0 }, children: menuItems.map((item) => /* @__PURE__ */ jsx(
|
|
156
|
+
RouterMenuItem,
|
|
157
|
+
{
|
|
158
|
+
item,
|
|
159
|
+
expanded,
|
|
160
|
+
onToggle: handleToggle
|
|
161
|
+
},
|
|
162
|
+
item.id
|
|
163
|
+
)) })
|
|
164
|
+
}
|
|
165
|
+
),
|
|
166
|
+
/* @__PURE__ */ jsx(
|
|
167
|
+
Box,
|
|
168
|
+
{
|
|
169
|
+
component: "main",
|
|
170
|
+
sx: {
|
|
171
|
+
flexGrow: 1,
|
|
172
|
+
overflow: "auto",
|
|
173
|
+
...bodyStyle
|
|
174
|
+
},
|
|
175
|
+
children: hasComponents ? /* @__PURE__ */ jsx(Routes, { children: routes.map(({ path, component }) => /* @__PURE__ */ jsx(
|
|
176
|
+
Route,
|
|
177
|
+
{
|
|
178
|
+
path,
|
|
179
|
+
element: component
|
|
180
|
+
},
|
|
181
|
+
path
|
|
182
|
+
)) }) : /* @__PURE__ */ jsx(Outlet, {})
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
);
|
|
188
|
+
};
|
|
189
|
+
var SidebarLayoutWithRouter = (props) => {
|
|
190
|
+
return /* @__PURE__ */ jsx(BrowserRouter, { children: /* @__PURE__ */ jsx(SidebarLayoutRouterInner, { ...props }) });
|
|
191
|
+
};
|
|
192
|
+
var StateMenuItem = ({ item, selectedId, depth = 0, expanded, onToggle, onClick }) => {
|
|
193
|
+
const hasChildren = item.children && item.children.length > 0;
|
|
194
|
+
const isExpanded = expanded.has(item.id);
|
|
195
|
+
const isActive = item.id === selectedId;
|
|
196
|
+
const handleClick = () => {
|
|
197
|
+
if (hasChildren) {
|
|
198
|
+
onToggle(item.id);
|
|
199
|
+
} else {
|
|
200
|
+
onClick(item.id);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
204
|
+
/* @__PURE__ */ jsx(ListItem, { disablePadding: true, children: /* @__PURE__ */ jsxs(
|
|
205
|
+
ListItemButton,
|
|
206
|
+
{
|
|
207
|
+
selected: isActive,
|
|
208
|
+
onClick: handleClick,
|
|
209
|
+
sx: {
|
|
210
|
+
pl: 2 + depth * 2,
|
|
211
|
+
"&.Mui-selected": {
|
|
212
|
+
backgroundColor: "action.selected",
|
|
213
|
+
color: "primary.main",
|
|
214
|
+
borderLeft: 3,
|
|
215
|
+
borderColor: "primary.main",
|
|
216
|
+
fontWeight: "medium",
|
|
217
|
+
"& .MuiListItemIcon-root": {
|
|
218
|
+
color: "primary.main"
|
|
219
|
+
},
|
|
220
|
+
"&:hover": {
|
|
221
|
+
backgroundColor: "action.selected"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"&:hover": {
|
|
225
|
+
backgroundColor: "action.hover"
|
|
226
|
+
},
|
|
227
|
+
transition: "all 0.15s"
|
|
228
|
+
},
|
|
229
|
+
children: [
|
|
230
|
+
item.icon && /* @__PURE__ */ jsx(ListItemIcon, { sx: { minWidth: 40 }, children: item.icon }),
|
|
231
|
+
/* @__PURE__ */ jsx(
|
|
232
|
+
ListItemText,
|
|
233
|
+
{
|
|
234
|
+
primary: item.label,
|
|
235
|
+
primaryTypographyProps: {
|
|
236
|
+
fontSize: "0.875rem"
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
),
|
|
240
|
+
hasChildren && /* @__PURE__ */ jsx(IconButton, { size: "small", sx: { p: 0 }, children: isExpanded ? /* @__PURE__ */ jsx(ExpandLess, {}) : /* @__PURE__ */ jsx(ExpandMore, {}) })
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
) }),
|
|
244
|
+
hasChildren && /* @__PURE__ */ jsx(Collapse, { in: isExpanded, timeout: "auto", unmountOnExit: true, children: /* @__PURE__ */ jsx(List, { component: "div", disablePadding: true, children: item.children.map((child) => /* @__PURE__ */ jsx(
|
|
245
|
+
StateMenuItem,
|
|
246
|
+
{
|
|
247
|
+
item: child,
|
|
248
|
+
selectedId,
|
|
249
|
+
depth: depth + 1,
|
|
250
|
+
expanded,
|
|
251
|
+
onToggle,
|
|
252
|
+
onClick
|
|
253
|
+
},
|
|
254
|
+
child.id
|
|
255
|
+
)) }) })
|
|
256
|
+
] });
|
|
257
|
+
};
|
|
258
|
+
var SidebarLayoutWithState = ({
|
|
259
|
+
menuItems,
|
|
260
|
+
sidebarWidth = 250,
|
|
261
|
+
className = "",
|
|
262
|
+
defaultSelected,
|
|
263
|
+
onMenuChange,
|
|
264
|
+
style,
|
|
265
|
+
sidebarStyle,
|
|
266
|
+
bodyStyle,
|
|
267
|
+
defaultExpanded = []
|
|
268
|
+
}) => {
|
|
269
|
+
const [selectedId, setSelectedId] = useState(
|
|
270
|
+
defaultSelected || menuItems[0]?.id
|
|
271
|
+
);
|
|
272
|
+
const [expanded, setExpanded] = useState(new Set(defaultExpanded));
|
|
273
|
+
const handleToggle = (id) => {
|
|
274
|
+
setExpanded((prev) => {
|
|
275
|
+
const next = new Set(prev);
|
|
276
|
+
if (next.has(id)) {
|
|
277
|
+
next.delete(id);
|
|
278
|
+
} else {
|
|
279
|
+
next.add(id);
|
|
280
|
+
}
|
|
281
|
+
return next;
|
|
282
|
+
});
|
|
283
|
+
};
|
|
284
|
+
const handleMenuClick = (id) => {
|
|
285
|
+
setSelectedId(id);
|
|
286
|
+
if (onMenuChange) {
|
|
287
|
+
onMenuChange(id);
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
const findActiveItem = (items, id) => {
|
|
291
|
+
for (const item of items) {
|
|
292
|
+
if (item.id === id) return item;
|
|
293
|
+
if (item.children) {
|
|
294
|
+
const found = findActiveItem(item.children, id);
|
|
295
|
+
if (found) return found;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return void 0;
|
|
299
|
+
};
|
|
300
|
+
const activeItem = findActiveItem(menuItems, selectedId);
|
|
301
|
+
return /* @__PURE__ */ jsxs(
|
|
302
|
+
Box,
|
|
303
|
+
{
|
|
304
|
+
className,
|
|
305
|
+
sx: {
|
|
306
|
+
display: "flex",
|
|
307
|
+
height: "100%",
|
|
308
|
+
width: "100%",
|
|
309
|
+
...style
|
|
310
|
+
},
|
|
311
|
+
children: [
|
|
312
|
+
/* @__PURE__ */ jsx(
|
|
313
|
+
Drawer,
|
|
314
|
+
{
|
|
315
|
+
variant: "permanent",
|
|
316
|
+
sx: {
|
|
317
|
+
width: sidebarWidth,
|
|
318
|
+
flexShrink: 0,
|
|
319
|
+
"& .MuiDrawer-paper": {
|
|
320
|
+
width: sidebarWidth,
|
|
321
|
+
boxSizing: "border-box",
|
|
322
|
+
position: "relative",
|
|
323
|
+
borderRight: "1px solid",
|
|
324
|
+
borderColor: "divider",
|
|
325
|
+
...sidebarStyle
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
children: /* @__PURE__ */ jsx(List, { sx: { p: 0 }, children: menuItems.map((item) => /* @__PURE__ */ jsx(
|
|
329
|
+
StateMenuItem,
|
|
330
|
+
{
|
|
331
|
+
item,
|
|
332
|
+
selectedId,
|
|
333
|
+
expanded,
|
|
334
|
+
onToggle: handleToggle,
|
|
335
|
+
onClick: handleMenuClick
|
|
336
|
+
},
|
|
337
|
+
item.id
|
|
338
|
+
)) })
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
/* @__PURE__ */ jsx(
|
|
342
|
+
Box,
|
|
343
|
+
{
|
|
344
|
+
component: "main",
|
|
345
|
+
sx: {
|
|
346
|
+
flexGrow: 1,
|
|
347
|
+
overflow: "auto",
|
|
348
|
+
p: 3,
|
|
349
|
+
...bodyStyle
|
|
350
|
+
},
|
|
351
|
+
children: activeItem?.component || /* @__PURE__ */ jsx(
|
|
352
|
+
Typography,
|
|
353
|
+
{
|
|
354
|
+
variant: "body2",
|
|
355
|
+
color: "text.secondary",
|
|
356
|
+
sx: { textAlign: "center", mt: 4 },
|
|
357
|
+
children: "No content available for this menu item"
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
]
|
|
363
|
+
}
|
|
364
|
+
);
|
|
365
|
+
};
|
|
366
|
+
var SidebarLayout = (props) => {
|
|
367
|
+
if (props.isRouter) {
|
|
368
|
+
return /* @__PURE__ */ jsx(SidebarLayoutWithRouter, { ...props });
|
|
369
|
+
}
|
|
370
|
+
return /* @__PURE__ */ jsx(SidebarLayoutWithState, { ...props });
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
// src/theme/tokens.ts
|
|
374
|
+
var tokens_exports = {};
|
|
375
|
+
__export(tokens_exports, {
|
|
376
|
+
borderRadius: () => borderRadius,
|
|
377
|
+
elevation: () => elevation,
|
|
378
|
+
fontSize: () => fontSize,
|
|
379
|
+
fontWeight: () => fontWeight,
|
|
380
|
+
lineHeight: () => lineHeight,
|
|
381
|
+
spacing: () => spacing,
|
|
382
|
+
transition: () => transition,
|
|
383
|
+
zIndex: () => zIndex
|
|
384
|
+
});
|
|
385
|
+
var spacing = {
|
|
386
|
+
xs: "0.25rem",
|
|
387
|
+
// 4px
|
|
388
|
+
sm: "0.5rem",
|
|
389
|
+
// 8px
|
|
390
|
+
md: "1rem",
|
|
391
|
+
// 16px
|
|
392
|
+
lg: "1.5rem",
|
|
393
|
+
// 24px
|
|
394
|
+
xl: "2rem",
|
|
395
|
+
// 32px
|
|
396
|
+
"2xl": "3rem",
|
|
397
|
+
// 48px
|
|
398
|
+
"3xl": "4rem"
|
|
399
|
+
// 64px
|
|
400
|
+
};
|
|
401
|
+
var borderRadius = {
|
|
402
|
+
none: "0",
|
|
403
|
+
sm: "0.25rem",
|
|
404
|
+
// 4px
|
|
405
|
+
md: "0.375rem",
|
|
406
|
+
// 6px
|
|
407
|
+
lg: "0.5rem",
|
|
408
|
+
// 8px
|
|
409
|
+
xl: "0.75rem",
|
|
410
|
+
// 12px
|
|
411
|
+
"2xl": "1rem",
|
|
412
|
+
// 16px
|
|
413
|
+
full: "9999px"
|
|
414
|
+
};
|
|
415
|
+
var fontSize = {
|
|
416
|
+
xs: "0.75rem",
|
|
417
|
+
// 12px
|
|
418
|
+
sm: "0.875rem",
|
|
419
|
+
// 14px
|
|
420
|
+
md: "1rem",
|
|
421
|
+
// 16px
|
|
422
|
+
lg: "1.125rem",
|
|
423
|
+
// 18px
|
|
424
|
+
xl: "1.25rem",
|
|
425
|
+
// 20px
|
|
426
|
+
"2xl": "1.5rem",
|
|
427
|
+
// 24px
|
|
428
|
+
"3xl": "1.875rem",
|
|
429
|
+
// 30px
|
|
430
|
+
"4xl": "2.25rem"
|
|
431
|
+
// 36px
|
|
432
|
+
};
|
|
433
|
+
var fontWeight = {
|
|
434
|
+
light: "300",
|
|
435
|
+
normal: "400",
|
|
436
|
+
medium: "500",
|
|
437
|
+
semibold: "600",
|
|
438
|
+
bold: "700"
|
|
439
|
+
};
|
|
440
|
+
var lineHeight = {
|
|
441
|
+
tight: "1.25",
|
|
442
|
+
normal: "1.5",
|
|
443
|
+
relaxed: "1.75"
|
|
444
|
+
};
|
|
445
|
+
var elevation = {
|
|
446
|
+
none: "none",
|
|
447
|
+
sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
|
|
448
|
+
md: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
|
|
449
|
+
lg: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
|
|
450
|
+
xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)"
|
|
451
|
+
};
|
|
452
|
+
var transition = {
|
|
453
|
+
fast: "150ms",
|
|
454
|
+
normal: "250ms",
|
|
455
|
+
slow: "350ms"
|
|
456
|
+
};
|
|
457
|
+
var zIndex = {
|
|
458
|
+
dropdown: 1e3,
|
|
459
|
+
sticky: 1020,
|
|
460
|
+
fixed: 1030,
|
|
461
|
+
modalBackdrop: 1040,
|
|
462
|
+
modal: 1050,
|
|
463
|
+
popover: 1060,
|
|
464
|
+
tooltip: 1070
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
// src/theme/light.ts
|
|
468
|
+
var lightTheme = {
|
|
469
|
+
colors: {
|
|
470
|
+
// Primary - Vibrant Indigo (Works beautifully on light backgrounds)
|
|
471
|
+
primary: "#6366f1",
|
|
472
|
+
// Indigo 500
|
|
473
|
+
primaryHover: "#4f46e5",
|
|
474
|
+
// Indigo 600
|
|
475
|
+
primaryActive: "#4338ca",
|
|
476
|
+
// Indigo 700
|
|
477
|
+
// Secondary - Gray 600 (WCAG AAA compliant: 7.66:1 on white)
|
|
478
|
+
secondary: "#4b5563",
|
|
479
|
+
secondaryHover: "#374151",
|
|
480
|
+
secondaryActive: "#1f2937",
|
|
481
|
+
// Background colors
|
|
482
|
+
background: "#ffffff",
|
|
483
|
+
backgroundSecondary: "#f9fafb",
|
|
484
|
+
backgroundTertiary: "#f3f4f6",
|
|
485
|
+
// Surface colors (for cards, modals, etc.)
|
|
486
|
+
surface: "#ffffff",
|
|
487
|
+
surfaceHover: "#f9fafb",
|
|
488
|
+
// Text colors (WCAG AAA compliant)
|
|
489
|
+
text: "#111827",
|
|
490
|
+
// Gray 900 (16.26:1 on white)
|
|
491
|
+
textSecondary: "#4b5563",
|
|
492
|
+
// Gray 600 (7.66:1 on white)
|
|
493
|
+
textTertiary: "#6b7280",
|
|
494
|
+
// Gray 500 (4.92:1 on white - AA compliant)
|
|
495
|
+
textDisabled: "#9ca3af",
|
|
496
|
+
// Gray 400
|
|
497
|
+
// Semantic colors
|
|
498
|
+
error: "#dc2626",
|
|
499
|
+
// Red 600 (6.05:1 on white - AAA compliant)
|
|
500
|
+
errorHover: "#b91c1c",
|
|
501
|
+
errorBackground: "#fef2f2",
|
|
502
|
+
success: "#16a34a",
|
|
503
|
+
// Green 600 (5.15:1 on white - AAA compliant)
|
|
504
|
+
successHover: "#15803d",
|
|
505
|
+
successBackground: "#f0fdf4",
|
|
506
|
+
warning: "#d97706",
|
|
507
|
+
// Amber 600 (5.33:1 on white - AAA compliant)
|
|
508
|
+
warningHover: "#b45309",
|
|
509
|
+
warningBackground: "#fffbeb",
|
|
510
|
+
info: "#2563eb",
|
|
511
|
+
// Blue 600 (6.64:1 on white - AAA compliant)
|
|
512
|
+
infoHover: "#1d4ed8",
|
|
513
|
+
infoBackground: "#eff6ff",
|
|
514
|
+
// Border colors
|
|
515
|
+
border: "#e5e7eb",
|
|
516
|
+
// Gray 200
|
|
517
|
+
borderHover: "#d1d5db",
|
|
518
|
+
// Gray 300
|
|
519
|
+
borderFocus: "#6366f1",
|
|
520
|
+
// Primary
|
|
521
|
+
// Divider
|
|
522
|
+
divider: "#e5e7eb",
|
|
523
|
+
// Overlay (for modals, dropdowns)
|
|
524
|
+
overlay: "rgba(0, 0, 0, 0.5)"
|
|
525
|
+
},
|
|
526
|
+
spacing,
|
|
527
|
+
borderRadius,
|
|
528
|
+
fontSize,
|
|
529
|
+
fontWeight,
|
|
530
|
+
lineHeight,
|
|
531
|
+
elevation,
|
|
532
|
+
transition,
|
|
533
|
+
zIndex
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
// src/theme/dark.ts
|
|
537
|
+
var darkTheme = {
|
|
538
|
+
colors: {
|
|
539
|
+
// Primary - Vibrant Indigo (Striking on VSCode Black)
|
|
540
|
+
primary: "#818cf8",
|
|
541
|
+
// Indigo 400
|
|
542
|
+
primaryHover: "#6366f1",
|
|
543
|
+
// Indigo 500
|
|
544
|
+
primaryActive: "#4f46e5",
|
|
545
|
+
// Indigo 600
|
|
546
|
+
// Secondary - VSCode Gray
|
|
547
|
+
secondary: "#858585",
|
|
548
|
+
secondaryHover: "#a6a6a6",
|
|
549
|
+
secondaryActive: "#cccccc",
|
|
550
|
+
// Background colors
|
|
551
|
+
background: "#1e1e1e",
|
|
552
|
+
// Main editor/workspace background
|
|
553
|
+
backgroundSecondary: "#252526",
|
|
554
|
+
// Sidebar, panels
|
|
555
|
+
backgroundTertiary: "#2d2d2d",
|
|
556
|
+
// Highlights, smaller surfaces
|
|
557
|
+
// Surface colors (for cards, modals, etc.)
|
|
558
|
+
surface: "#252526",
|
|
559
|
+
surfaceHover: "#2a2d2e",
|
|
560
|
+
// Text colors
|
|
561
|
+
text: "#cccccc",
|
|
562
|
+
// Primary text
|
|
563
|
+
textSecondary: "#999999",
|
|
564
|
+
// Secondary text, comments
|
|
565
|
+
textTertiary: "#6b6b6b",
|
|
566
|
+
// Disabled/tertiary
|
|
567
|
+
textDisabled: "#4d4d4d",
|
|
568
|
+
// Disabled
|
|
569
|
+
// Semantic colors
|
|
570
|
+
error: "#f48771",
|
|
571
|
+
errorHover: "#fca5a5",
|
|
572
|
+
errorBackground: "#5a1d1d",
|
|
573
|
+
success: "#89d185",
|
|
574
|
+
successHover: "#86efac",
|
|
575
|
+
successBackground: "#1e401e",
|
|
576
|
+
warning: "#cca700",
|
|
577
|
+
warningHover: "#d7ba7d",
|
|
578
|
+
warningBackground: "#5c4d00",
|
|
579
|
+
info: "#75beff",
|
|
580
|
+
infoHover: "#9cdcfe",
|
|
581
|
+
infoBackground: "#0a3254",
|
|
582
|
+
// Border colors
|
|
583
|
+
border: "#3c3c3c",
|
|
584
|
+
// VSCode borders
|
|
585
|
+
borderHover: "#444444",
|
|
586
|
+
borderFocus: "#818cf8",
|
|
587
|
+
// Focus border
|
|
588
|
+
// Divider
|
|
589
|
+
divider: "#333333",
|
|
590
|
+
// Overlay (for modals, dropdowns)
|
|
591
|
+
overlay: "rgba(0, 0, 0, 0.4)"
|
|
592
|
+
},
|
|
593
|
+
spacing,
|
|
594
|
+
borderRadius,
|
|
595
|
+
fontSize,
|
|
596
|
+
fontWeight,
|
|
597
|
+
lineHeight,
|
|
598
|
+
elevation,
|
|
599
|
+
transition,
|
|
600
|
+
zIndex
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// src/utils/localStorage.ts
|
|
604
|
+
var LocalStorage = class {
|
|
605
|
+
/**
|
|
606
|
+
* Get an item from localStorage
|
|
607
|
+
* @param key - The key to retrieve
|
|
608
|
+
* @returns The value or null if not found or error occurs
|
|
609
|
+
*/
|
|
610
|
+
static getItem(key) {
|
|
611
|
+
try {
|
|
612
|
+
if (typeof window === "undefined") {
|
|
613
|
+
return null;
|
|
614
|
+
}
|
|
615
|
+
const item = window.localStorage.getItem(key);
|
|
616
|
+
if (item === null) {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
try {
|
|
620
|
+
return JSON.parse(item);
|
|
621
|
+
} catch {
|
|
622
|
+
return item;
|
|
623
|
+
}
|
|
624
|
+
} catch (error) {
|
|
625
|
+
console.error(`Error getting item from localStorage: ${error}`);
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Set an item in localStorage
|
|
631
|
+
* @param key - The key to store
|
|
632
|
+
* @param value - The value to store
|
|
633
|
+
* @returns true if successful, false otherwise
|
|
634
|
+
*/
|
|
635
|
+
static setItem(key, value) {
|
|
636
|
+
try {
|
|
637
|
+
if (typeof window === "undefined") {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
const serializedValue = typeof value === "string" ? value : JSON.stringify(value);
|
|
641
|
+
window.localStorage.setItem(key, serializedValue);
|
|
642
|
+
return true;
|
|
643
|
+
} catch (error) {
|
|
644
|
+
console.error(`Error setting item in localStorage: ${error}`);
|
|
645
|
+
return false;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Remove an item from localStorage
|
|
650
|
+
* @param key - The key to remove
|
|
651
|
+
* @returns true if successful, false otherwise
|
|
652
|
+
*/
|
|
653
|
+
static removeItem(key) {
|
|
654
|
+
try {
|
|
655
|
+
if (typeof window === "undefined") {
|
|
656
|
+
return false;
|
|
657
|
+
}
|
|
658
|
+
window.localStorage.removeItem(key);
|
|
659
|
+
return true;
|
|
660
|
+
} catch (error) {
|
|
661
|
+
console.error(`Error removing item from localStorage: ${error}`);
|
|
662
|
+
return false;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* Clear all items from localStorage
|
|
667
|
+
* @returns true if successful, false otherwise
|
|
668
|
+
*/
|
|
669
|
+
static clear() {
|
|
670
|
+
try {
|
|
671
|
+
if (typeof window === "undefined") {
|
|
672
|
+
return false;
|
|
673
|
+
}
|
|
674
|
+
window.localStorage.clear();
|
|
675
|
+
return true;
|
|
676
|
+
} catch (error) {
|
|
677
|
+
console.error(`Error clearing localStorage: ${error}`);
|
|
678
|
+
return false;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Check if a key exists in localStorage
|
|
683
|
+
* @param key - The key to check
|
|
684
|
+
* @returns true if key exists, false otherwise
|
|
685
|
+
*/
|
|
686
|
+
static hasItem(key) {
|
|
687
|
+
try {
|
|
688
|
+
if (typeof window === "undefined") {
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
return window.localStorage.getItem(key) !== null;
|
|
692
|
+
} catch (error) {
|
|
693
|
+
console.error(`Error checking item in localStorage: ${error}`);
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* Get all keys from localStorage
|
|
699
|
+
* @returns Array of keys
|
|
700
|
+
*/
|
|
701
|
+
static keys() {
|
|
702
|
+
try {
|
|
703
|
+
if (typeof window === "undefined") {
|
|
704
|
+
return [];
|
|
705
|
+
}
|
|
706
|
+
return Object.keys(window.localStorage);
|
|
707
|
+
} catch (error) {
|
|
708
|
+
console.error(`Error getting keys from localStorage: ${error}`);
|
|
709
|
+
return [];
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
};
|
|
713
|
+
var ThemeContext = createContext(void 0);
|
|
714
|
+
var THEME_STORAGE_KEY = "app-theme-mode";
|
|
715
|
+
var getSystemTheme = () => {
|
|
716
|
+
if (typeof window === "undefined") return "light";
|
|
717
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
718
|
+
};
|
|
719
|
+
var applyCssVariables = (theme, resolvedMode) => {
|
|
720
|
+
if (typeof document === "undefined") return;
|
|
721
|
+
const root = document.documentElement;
|
|
722
|
+
root.setAttribute("data-theme", resolvedMode);
|
|
723
|
+
Object.entries(theme.colors).forEach(([key, value]) => {
|
|
724
|
+
root.style.setProperty(`--color-${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`, value);
|
|
725
|
+
});
|
|
726
|
+
Object.entries(theme.spacing).forEach(([key, value]) => {
|
|
727
|
+
root.style.setProperty(`--spacing-${key}`, value);
|
|
728
|
+
});
|
|
729
|
+
Object.entries(theme.borderRadius).forEach(([key, value]) => {
|
|
730
|
+
root.style.setProperty(`--radius-${key}`, value);
|
|
731
|
+
});
|
|
732
|
+
Object.entries(theme.fontSize).forEach(([key, value]) => {
|
|
733
|
+
root.style.setProperty(`--text-${key}`, value);
|
|
734
|
+
});
|
|
735
|
+
Object.entries(theme.fontWeight).forEach(([key, value]) => {
|
|
736
|
+
root.style.setProperty(`--font-${key}`, value);
|
|
737
|
+
});
|
|
738
|
+
Object.entries(theme.lineHeight).forEach(([key, value]) => {
|
|
739
|
+
root.style.setProperty(`--leading-${key}`, value);
|
|
740
|
+
});
|
|
741
|
+
Object.entries(theme.elevation).forEach(([key, value]) => {
|
|
742
|
+
root.style.setProperty(`--shadow-${key}`, value);
|
|
743
|
+
});
|
|
744
|
+
Object.entries(theme.transition).forEach(([key, value]) => {
|
|
745
|
+
root.style.setProperty(`--transition-${key}`, value);
|
|
746
|
+
});
|
|
747
|
+
};
|
|
748
|
+
var ThemeProvider = ({
|
|
749
|
+
children,
|
|
750
|
+
initialTheme,
|
|
751
|
+
initialMode = "system",
|
|
752
|
+
storageKey = THEME_STORAGE_KEY,
|
|
753
|
+
enableCssVariables = true
|
|
754
|
+
}) => {
|
|
755
|
+
const getInitialMode = () => {
|
|
756
|
+
const savedMode = LocalStorage.getItem(storageKey);
|
|
757
|
+
return savedMode || initialMode;
|
|
758
|
+
};
|
|
759
|
+
const [themeMode, setThemeModeState] = useState(getInitialMode);
|
|
760
|
+
const [systemTheme, setSystemTheme] = useState(getSystemTheme);
|
|
761
|
+
const [customTheme, setCustomTheme] = useState(initialTheme);
|
|
762
|
+
const resolvedThemeMode = useMemo(() => {
|
|
763
|
+
if (themeMode === "system") return systemTheme;
|
|
764
|
+
return themeMode;
|
|
765
|
+
}, [themeMode, systemTheme]);
|
|
766
|
+
const theme = useMemo(() => {
|
|
767
|
+
if (customTheme) return customTheme;
|
|
768
|
+
return resolvedThemeMode === "light" ? lightTheme : darkTheme;
|
|
769
|
+
}, [customTheme, resolvedThemeMode]);
|
|
770
|
+
useEffect(() => {
|
|
771
|
+
if (typeof window === "undefined") return;
|
|
772
|
+
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
|
773
|
+
const handleChange = (e) => {
|
|
774
|
+
setSystemTheme(e.matches ? "dark" : "light");
|
|
775
|
+
};
|
|
776
|
+
if (mediaQuery.addEventListener) {
|
|
777
|
+
mediaQuery.addEventListener("change", handleChange);
|
|
778
|
+
return () => mediaQuery.removeEventListener("change", handleChange);
|
|
779
|
+
} else if (mediaQuery.addListener) {
|
|
780
|
+
mediaQuery.addListener(handleChange);
|
|
781
|
+
return () => mediaQuery.removeListener(handleChange);
|
|
782
|
+
}
|
|
783
|
+
}, []);
|
|
784
|
+
useEffect(() => {
|
|
785
|
+
if (enableCssVariables) {
|
|
786
|
+
applyCssVariables(theme, resolvedThemeMode);
|
|
787
|
+
}
|
|
788
|
+
}, [theme, resolvedThemeMode, enableCssVariables]);
|
|
789
|
+
useEffect(() => {
|
|
790
|
+
LocalStorage.setItem(storageKey, themeMode);
|
|
791
|
+
}, [themeMode, storageKey]);
|
|
792
|
+
const setThemeMode = useCallback((mode) => {
|
|
793
|
+
setThemeModeState(mode);
|
|
794
|
+
}, []);
|
|
795
|
+
const setTheme = useCallback((newTheme) => {
|
|
796
|
+
setCustomTheme(newTheme);
|
|
797
|
+
}, []);
|
|
798
|
+
const toggleTheme = useCallback(() => {
|
|
799
|
+
const newMode = resolvedThemeMode === "light" ? "dark" : "light";
|
|
800
|
+
setThemeModeState(newMode);
|
|
801
|
+
}, [resolvedThemeMode]);
|
|
802
|
+
const contextValue = useMemo(() => ({
|
|
803
|
+
theme,
|
|
804
|
+
themeMode,
|
|
805
|
+
resolvedThemeMode,
|
|
806
|
+
setTheme,
|
|
807
|
+
setThemeMode,
|
|
808
|
+
toggleTheme
|
|
809
|
+
}), [theme, themeMode, resolvedThemeMode, setTheme, setThemeMode, toggleTheme]);
|
|
810
|
+
const muiTheme = useMemo(() => createTheme({
|
|
811
|
+
palette: {
|
|
812
|
+
mode: resolvedThemeMode,
|
|
813
|
+
primary: {
|
|
814
|
+
main: theme.colors.primary
|
|
815
|
+
},
|
|
816
|
+
secondary: {
|
|
817
|
+
main: theme.colors.secondary
|
|
818
|
+
},
|
|
819
|
+
error: {
|
|
820
|
+
main: theme.colors.error
|
|
821
|
+
},
|
|
822
|
+
success: {
|
|
823
|
+
main: theme.colors.success
|
|
824
|
+
},
|
|
825
|
+
background: {
|
|
826
|
+
default: theme.colors.background,
|
|
827
|
+
paper: theme.colors.backgroundSecondary
|
|
828
|
+
},
|
|
829
|
+
text: {
|
|
830
|
+
primary: theme.colors.text,
|
|
831
|
+
secondary: theme.colors.textSecondary
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
}), [resolvedThemeMode, theme]);
|
|
835
|
+
return /* @__PURE__ */ jsx(ThemeContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs(ThemeProvider$1, { theme: muiTheme, children: [
|
|
836
|
+
/* @__PURE__ */ jsx(CssBaseline, {}),
|
|
837
|
+
children
|
|
838
|
+
] }) });
|
|
839
|
+
};
|
|
840
|
+
var useTheme = () => {
|
|
841
|
+
const context = useContext(ThemeContext);
|
|
842
|
+
if (!context) {
|
|
843
|
+
throw new Error("useTheme must be used within a ThemeProvider");
|
|
844
|
+
}
|
|
845
|
+
return context;
|
|
846
|
+
};
|
|
847
|
+
var ThemeToggle = ({
|
|
848
|
+
lightModeTooltip = "Switch to dark mode",
|
|
849
|
+
darkModeTooltip = "Switch to light mode",
|
|
850
|
+
showTooltip = true,
|
|
851
|
+
sx,
|
|
852
|
+
...props
|
|
853
|
+
}) => {
|
|
854
|
+
const { resolvedThemeMode, toggleTheme } = useTheme();
|
|
855
|
+
const button = /* @__PURE__ */ jsx(
|
|
856
|
+
IconButton,
|
|
857
|
+
{
|
|
858
|
+
onClick: toggleTheme,
|
|
859
|
+
color: "inherit",
|
|
860
|
+
sx: {
|
|
861
|
+
borderRadius: "50%",
|
|
862
|
+
...sx
|
|
863
|
+
},
|
|
864
|
+
...props,
|
|
865
|
+
children: resolvedThemeMode === "light" ? /* @__PURE__ */ jsx(Brightness4, {}) : /* @__PURE__ */ jsx(Brightness7, {})
|
|
866
|
+
}
|
|
867
|
+
);
|
|
868
|
+
if (!showTooltip) {
|
|
869
|
+
return button;
|
|
870
|
+
}
|
|
871
|
+
return /* @__PURE__ */ jsx(
|
|
872
|
+
Tooltip,
|
|
873
|
+
{
|
|
874
|
+
title: resolvedThemeMode === "light" ? lightModeTooltip : darkModeTooltip,
|
|
875
|
+
arrow: true,
|
|
876
|
+
children: button
|
|
877
|
+
}
|
|
878
|
+
);
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
// src/http/HttpClient.ts
|
|
882
|
+
var HttpClient = class {
|
|
883
|
+
constructor(config = {}) {
|
|
884
|
+
this.baseURL = config.baseURL || "";
|
|
885
|
+
this.timeout = config.timeout || 3e4;
|
|
886
|
+
this.defaultHeaders = config.headers || {};
|
|
887
|
+
}
|
|
888
|
+
buildURL(url, params) {
|
|
889
|
+
const fullURL = url.startsWith("http") ? url : `${this.baseURL}${url}`;
|
|
890
|
+
if (!params) return fullURL;
|
|
891
|
+
const searchParams = new URLSearchParams();
|
|
892
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
893
|
+
searchParams.append(key, String(value));
|
|
894
|
+
});
|
|
895
|
+
return `${fullURL}?${searchParams.toString()}`;
|
|
896
|
+
}
|
|
897
|
+
async request(url, config = {}) {
|
|
898
|
+
const { params, data, timeout = this.timeout, headers = {}, ...restConfig } = config;
|
|
899
|
+
const controller = new AbortController();
|
|
900
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
901
|
+
try {
|
|
902
|
+
const response = await fetch(this.buildURL(url, params), {
|
|
903
|
+
...restConfig,
|
|
904
|
+
headers: {
|
|
905
|
+
"Content-Type": "application/json",
|
|
906
|
+
...this.defaultHeaders,
|
|
907
|
+
...headers
|
|
908
|
+
},
|
|
909
|
+
body: data ? JSON.stringify(data) : void 0,
|
|
910
|
+
signal: controller.signal
|
|
911
|
+
});
|
|
912
|
+
clearTimeout(timeoutId);
|
|
913
|
+
if (!response.ok) {
|
|
914
|
+
const error = new Error(`HTTP Error: ${response.statusText}`);
|
|
915
|
+
error.status = response.status;
|
|
916
|
+
error.statusText = response.statusText;
|
|
917
|
+
try {
|
|
918
|
+
error.data = await response.json();
|
|
919
|
+
} catch {
|
|
920
|
+
error.data = await response.text();
|
|
921
|
+
}
|
|
922
|
+
throw error;
|
|
923
|
+
}
|
|
924
|
+
const responseData = await response.json();
|
|
925
|
+
return {
|
|
926
|
+
data: responseData,
|
|
927
|
+
status: response.status,
|
|
928
|
+
statusText: response.statusText,
|
|
929
|
+
headers: response.headers
|
|
930
|
+
};
|
|
931
|
+
} catch (error) {
|
|
932
|
+
clearTimeout(timeoutId);
|
|
933
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
934
|
+
throw new Error("Request timeout");
|
|
935
|
+
}
|
|
936
|
+
throw error;
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
get(url, config) {
|
|
940
|
+
return this.request(url, { ...config, method: "GET" });
|
|
941
|
+
}
|
|
942
|
+
post(url, data, config) {
|
|
943
|
+
return this.request(url, { ...config, data, method: "POST" });
|
|
944
|
+
}
|
|
945
|
+
put(url, data, config) {
|
|
946
|
+
return this.request(url, { ...config, data, method: "PUT" });
|
|
947
|
+
}
|
|
948
|
+
patch(url, data, config) {
|
|
949
|
+
return this.request(url, { ...config, data, method: "PATCH" });
|
|
950
|
+
}
|
|
951
|
+
delete(url, config) {
|
|
952
|
+
return this.request(url, { ...config, method: "DELETE" });
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
// src/utils/cn.ts
|
|
957
|
+
function cn(...classes) {
|
|
958
|
+
return classes.filter(Boolean).join(" ");
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// src/utils/debounce.ts
|
|
962
|
+
function debounce(func, wait) {
|
|
963
|
+
let timeoutId = null;
|
|
964
|
+
return function(...args) {
|
|
965
|
+
if (timeoutId) {
|
|
966
|
+
clearTimeout(timeoutId);
|
|
967
|
+
}
|
|
968
|
+
timeoutId = setTimeout(() => {
|
|
969
|
+
func.apply(this, args);
|
|
970
|
+
}, wait);
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// src/utils/throttle.ts
|
|
975
|
+
function throttle(func, wait) {
|
|
976
|
+
let lastCall = 0;
|
|
977
|
+
return function(...args) {
|
|
978
|
+
const now = Date.now();
|
|
979
|
+
if (now - lastCall >= wait) {
|
|
980
|
+
lastCall = now;
|
|
981
|
+
func.apply(this, args);
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// src/utils/formatDate.ts
|
|
987
|
+
function formatDate(date, options = {
|
|
988
|
+
year: "numeric",
|
|
989
|
+
month: "long",
|
|
990
|
+
day: "numeric"
|
|
991
|
+
}) {
|
|
992
|
+
const dateObj = typeof date === "string" || typeof date === "number" ? new Date(date) : date;
|
|
993
|
+
return new Intl.DateTimeFormat("en-US", options).format(dateObj);
|
|
994
|
+
}
|
|
995
|
+
function getCookie(name) {
|
|
996
|
+
if (typeof document === "undefined") return null;
|
|
997
|
+
const value = `; ${document.cookie}`;
|
|
998
|
+
const parts = value.split(`; ${name}=`);
|
|
999
|
+
if (parts.length === 2) return parts.pop()?.split(";").shift() || null;
|
|
1000
|
+
return null;
|
|
1001
|
+
}
|
|
1002
|
+
function decodeJWT(token) {
|
|
1003
|
+
try {
|
|
1004
|
+
const base64Url = token.split(".")[1];
|
|
1005
|
+
if (!base64Url) return null;
|
|
1006
|
+
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
1007
|
+
const jsonPayload = decodeURIComponent(
|
|
1008
|
+
window.atob(base64).split("").map(function(c) {
|
|
1009
|
+
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
|
|
1010
|
+
}).join("")
|
|
1011
|
+
);
|
|
1012
|
+
return JSON.parse(jsonPayload);
|
|
1013
|
+
} catch (e) {
|
|
1014
|
+
return null;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
var AuthContext = createContext({
|
|
1018
|
+
isAuthenticated: false,
|
|
1019
|
+
isLoading: true
|
|
1020
|
+
});
|
|
1021
|
+
var AuthProvider = ({
|
|
1022
|
+
cookieName = "authorization",
|
|
1023
|
+
redirectUrl,
|
|
1024
|
+
children,
|
|
1025
|
+
onAuthFail,
|
|
1026
|
+
onAuthSuccess,
|
|
1027
|
+
loadingElement = null
|
|
1028
|
+
}) => {
|
|
1029
|
+
const [authState, setAuthState] = useState({
|
|
1030
|
+
isAuthenticated: false,
|
|
1031
|
+
isLoading: true
|
|
1032
|
+
// starts loading until cookie check completes
|
|
1033
|
+
});
|
|
1034
|
+
const [authFailed, setAuthFailed] = useState(false);
|
|
1035
|
+
useEffect(() => {
|
|
1036
|
+
const verifyAuth = () => {
|
|
1037
|
+
const token = getCookie(cookieName);
|
|
1038
|
+
if (!token) {
|
|
1039
|
+
handleFail();
|
|
1040
|
+
return;
|
|
1041
|
+
}
|
|
1042
|
+
const decoded = decodeJWT(token);
|
|
1043
|
+
if (!decoded) {
|
|
1044
|
+
handleFail();
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
if (decoded.exp) {
|
|
1048
|
+
const expirationDate = new Date(decoded.exp * 1e3);
|
|
1049
|
+
if (expirationDate < /* @__PURE__ */ new Date()) {
|
|
1050
|
+
handleFail();
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
setAuthState({
|
|
1055
|
+
isAuthenticated: true,
|
|
1056
|
+
isLoading: false
|
|
1057
|
+
});
|
|
1058
|
+
if (onAuthSuccess) {
|
|
1059
|
+
onAuthSuccess();
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
verifyAuth();
|
|
1063
|
+
}, [cookieName]);
|
|
1064
|
+
const handleFail = () => {
|
|
1065
|
+
setAuthState({
|
|
1066
|
+
isAuthenticated: false,
|
|
1067
|
+
isLoading: false
|
|
1068
|
+
});
|
|
1069
|
+
setAuthFailed(true);
|
|
1070
|
+
if (onAuthFail) {
|
|
1071
|
+
onAuthFail();
|
|
1072
|
+
} else if (redirectUrl && typeof window !== "undefined") {
|
|
1073
|
+
window.location.href = redirectUrl;
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
if (authState.isLoading || authFailed && !onAuthFail) {
|
|
1077
|
+
return /* @__PURE__ */ jsx(Fragment, { children: loadingElement });
|
|
1078
|
+
}
|
|
1079
|
+
if (authFailed && onAuthFail) {
|
|
1080
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
1081
|
+
}
|
|
1082
|
+
return /* @__PURE__ */ jsx(AuthContext.Provider, { value: authState, children });
|
|
1083
|
+
};
|
|
1084
|
+
var useAuth = () => {
|
|
1085
|
+
return useContext(AuthContext);
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
// src/auth/PermissionManager.ts
|
|
1089
|
+
var PERMISSIONS_STORAGE_KEY = "auth_permissions";
|
|
1090
|
+
var PermissionManager = class {
|
|
1091
|
+
/**
|
|
1092
|
+
* Saves an array of permission strings to local storage.
|
|
1093
|
+
* @param permissions Array of permission strings.
|
|
1094
|
+
*/
|
|
1095
|
+
static setPermissions(permissions) {
|
|
1096
|
+
this.cachedPermissions = new Set(permissions);
|
|
1097
|
+
if (typeof window === "undefined") return;
|
|
1098
|
+
try {
|
|
1099
|
+
localStorage.setItem(PERMISSIONS_STORAGE_KEY, JSON.stringify(permissions));
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
console.error("Failed to save permissions to localStorage", error);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* Retrieves the currently stored permissions from local storage or memory cache.
|
|
1106
|
+
* @returns Array of permission strings, or an empty array if none exist.
|
|
1107
|
+
*/
|
|
1108
|
+
static getPermissions() {
|
|
1109
|
+
if (this.cachedPermissions !== null) {
|
|
1110
|
+
return Array.from(this.cachedPermissions);
|
|
1111
|
+
}
|
|
1112
|
+
if (typeof window === "undefined") return [];
|
|
1113
|
+
try {
|
|
1114
|
+
const stored = localStorage.getItem(PERMISSIONS_STORAGE_KEY);
|
|
1115
|
+
if (!stored) {
|
|
1116
|
+
this.cachedPermissions = /* @__PURE__ */ new Set();
|
|
1117
|
+
return [];
|
|
1118
|
+
}
|
|
1119
|
+
const parsed = JSON.parse(stored);
|
|
1120
|
+
const permissionsArray = Array.isArray(parsed) ? parsed : [];
|
|
1121
|
+
this.cachedPermissions = new Set(permissionsArray);
|
|
1122
|
+
return permissionsArray;
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
console.error("Failed to parse permissions from localStorage", error);
|
|
1125
|
+
this.cachedPermissions = /* @__PURE__ */ new Set();
|
|
1126
|
+
return [];
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Clears all stored permissions from memory and local storage.
|
|
1131
|
+
*/
|
|
1132
|
+
static clearPermissions() {
|
|
1133
|
+
this.cachedPermissions = null;
|
|
1134
|
+
if (typeof window === "undefined") return;
|
|
1135
|
+
localStorage.removeItem(PERMISSIONS_STORAGE_KEY);
|
|
1136
|
+
}
|
|
1137
|
+
/**
|
|
1138
|
+
* Checks whether the given permission string exists in the currently stored permissions.
|
|
1139
|
+
* @param permission The permission string to check for.
|
|
1140
|
+
* @returns True if the permission exists, false otherwise.
|
|
1141
|
+
*/
|
|
1142
|
+
static hasPermission(permission) {
|
|
1143
|
+
if (this.cachedPermissions === null) {
|
|
1144
|
+
this.getPermissions();
|
|
1145
|
+
}
|
|
1146
|
+
return this.cachedPermissions.has(permission);
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
PermissionManager.cachedPermissions = null;
|
|
1150
|
+
|
|
1151
|
+
export { AuthProvider, HttpClient, LocalStorage, PermissionManager, SidebarLayout, ThemeProvider, ThemeToggle, borderRadius, cn, darkTheme, debounce, elevation, fontSize, fontWeight, formatDate, lightTheme, lineHeight, spacing, throttle, tokens_exports as tokens, transition, useAuth, useTheme, zIndex };
|
|
1152
|
+
//# sourceMappingURL=index.mjs.map
|
|
1153
|
+
//# sourceMappingURL=index.mjs.map
|