rud-dashboard 0.1.4
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/README.md +148 -0
- package/dist/preact/index.preact.cjs +1707 -0
- package/dist/preact/index.preact.cjs.map +1 -0
- package/dist/preact/index.preact.d.cts +261 -0
- package/dist/preact/index.preact.d.ts +261 -0
- package/dist/preact/index.preact.js +1656 -0
- package/dist/preact/index.preact.js.map +1 -0
- package/dist/react/index.cjs +1707 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +261 -0
- package/dist/react/index.d.ts +261 -0
- package/dist/react/index.js +1656 -0
- package/dist/react/index.js.map +1 -0
- package/dist/styles.css +94 -0
- package/package.json +89 -0
- package/tailwind.preset.js +48 -0
|
@@ -0,0 +1,1656 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
// src/components/Dashboard.tsx
|
|
4
|
+
import * as React2 from "react";
|
|
5
|
+
import { useState as useState3, useRef as useRef2, useCallback as useCallback2, useEffect as useEffect2, useMemo } from "react";
|
|
6
|
+
import { Plus as Plus2, X as X2, MoreHorizontal } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
// src/components/DashboardToolbar.tsx
|
|
9
|
+
import React from "react";
|
|
10
|
+
import { Zap, Edit3, Eye, Plus, X, Grid as Grid2, TrendingUp, ChevronLeft, ChevronRight } from "lucide-react";
|
|
11
|
+
|
|
12
|
+
// src/ui/button.tsx
|
|
13
|
+
import { cva } from "class-variance-authority";
|
|
14
|
+
|
|
15
|
+
// src/lib/utils.ts
|
|
16
|
+
import { clsx } from "clsx";
|
|
17
|
+
import { twMerge } from "tailwind-merge";
|
|
18
|
+
function cn(...inputs) {
|
|
19
|
+
return twMerge(clsx(inputs));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/ui/button.tsx
|
|
23
|
+
import { jsx } from "preact/jsx-runtime";
|
|
24
|
+
var buttonVariants = cva(
|
|
25
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
26
|
+
{
|
|
27
|
+
variants: {
|
|
28
|
+
variant: {
|
|
29
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
30
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
31
|
+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
32
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
33
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
34
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
35
|
+
},
|
|
36
|
+
size: {
|
|
37
|
+
default: "h-10 px-4 py-2",
|
|
38
|
+
sm: "h-9 rounded-md px-3",
|
|
39
|
+
lg: "h-11 rounded-md px-8",
|
|
40
|
+
icon: "h-10 w-10"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
defaultVariants: {
|
|
44
|
+
variant: "default",
|
|
45
|
+
size: "default"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
function Button({ className, variant, size, ref, ...props }) {
|
|
50
|
+
return /* @__PURE__ */ jsx(
|
|
51
|
+
"button",
|
|
52
|
+
{
|
|
53
|
+
className: cn(buttonVariants({ variant, size }), className),
|
|
54
|
+
ref,
|
|
55
|
+
...props
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
Button.displayName = "Button";
|
|
60
|
+
|
|
61
|
+
// src/ui/tooltip.tsx
|
|
62
|
+
import { createContext, useContext, useState, useRef, useEffect, useLayoutEffect } from "react";
|
|
63
|
+
import { createPortal } from "react-dom";
|
|
64
|
+
import { Fragment, jsx as jsx2 } from "preact/jsx-runtime";
|
|
65
|
+
var TooltipContext = createContext(void 0);
|
|
66
|
+
function TooltipProvider({ children }) {
|
|
67
|
+
return /* @__PURE__ */ jsx2(Fragment, { children });
|
|
68
|
+
}
|
|
69
|
+
function Tooltip({ children, delayDuration = 200 }) {
|
|
70
|
+
const [open, setOpen] = useState(false);
|
|
71
|
+
const triggerRef = useRef(null);
|
|
72
|
+
const content = /* @__PURE__ */ jsx2("div", { ref: triggerRef, className: "relative inline-block", children });
|
|
73
|
+
return /* @__PURE__ */ jsx2(TooltipContext.Provider, { value: { open, setOpen, delay: delayDuration, triggerRef }, children: content });
|
|
74
|
+
}
|
|
75
|
+
function TooltipTrigger({ children, asChild, className, ...props }) {
|
|
76
|
+
const context = useContext(TooltipContext);
|
|
77
|
+
if (!context) throw new Error("TooltipTrigger must be used within Tooltip");
|
|
78
|
+
const { setOpen, delay } = context;
|
|
79
|
+
const timeoutRef = useRef();
|
|
80
|
+
const handleMouseEnter = () => {
|
|
81
|
+
timeoutRef.current = setTimeout(() => setOpen(true), delay);
|
|
82
|
+
};
|
|
83
|
+
const handleMouseLeave = () => {
|
|
84
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
85
|
+
setOpen(false);
|
|
86
|
+
};
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
return () => {
|
|
89
|
+
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
90
|
+
};
|
|
91
|
+
}, []);
|
|
92
|
+
return /* @__PURE__ */ jsx2(
|
|
93
|
+
"div",
|
|
94
|
+
{
|
|
95
|
+
onMouseEnter: handleMouseEnter,
|
|
96
|
+
onMouseLeave: handleMouseLeave,
|
|
97
|
+
className: cn("inline-block", className),
|
|
98
|
+
...props,
|
|
99
|
+
children
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
TooltipTrigger.displayName = "TooltipTrigger";
|
|
104
|
+
function TooltipContent({ className, sideOffset = 4, side = "top", children, ...props }) {
|
|
105
|
+
const context = useContext(TooltipContext);
|
|
106
|
+
const contentRef = useRef(null);
|
|
107
|
+
const [position, setPosition] = useState({ top: 0, left: 0 });
|
|
108
|
+
const [isPositioned, setIsPositioned] = useState(false);
|
|
109
|
+
if (!context) throw new Error("TooltipContent must be used within Tooltip");
|
|
110
|
+
const { open, triggerRef } = context;
|
|
111
|
+
useLayoutEffect(() => {
|
|
112
|
+
if (open && triggerRef.current && contentRef.current) {
|
|
113
|
+
const updatePosition = () => {
|
|
114
|
+
const triggerRect = triggerRef.current.getBoundingClientRect();
|
|
115
|
+
const contentRect = contentRef.current.getBoundingClientRect();
|
|
116
|
+
const scrollY = window.scrollY;
|
|
117
|
+
const scrollX = window.scrollX;
|
|
118
|
+
let top = 0;
|
|
119
|
+
let left = 0;
|
|
120
|
+
left = triggerRect.left + scrollX + triggerRect.width / 2 - contentRect.width / 2;
|
|
121
|
+
top = triggerRect.top + scrollY - contentRect.height - sideOffset;
|
|
122
|
+
if (triggerRect.top - contentRect.height - sideOffset < 0) {
|
|
123
|
+
top = triggerRect.bottom + scrollY + sideOffset;
|
|
124
|
+
}
|
|
125
|
+
setPosition({ top, left });
|
|
126
|
+
setIsPositioned(true);
|
|
127
|
+
};
|
|
128
|
+
updatePosition();
|
|
129
|
+
window.addEventListener("resize", updatePosition);
|
|
130
|
+
window.addEventListener("scroll", updatePosition, true);
|
|
131
|
+
return () => {
|
|
132
|
+
window.removeEventListener("resize", updatePosition);
|
|
133
|
+
window.removeEventListener("scroll", updatePosition, true);
|
|
134
|
+
setIsPositioned(false);
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}, [open, sideOffset]);
|
|
138
|
+
if (!open) return null;
|
|
139
|
+
return createPortal(
|
|
140
|
+
/* @__PURE__ */ jsx2(
|
|
141
|
+
"div",
|
|
142
|
+
{
|
|
143
|
+
ref: contentRef,
|
|
144
|
+
style: {
|
|
145
|
+
top: position.top,
|
|
146
|
+
left: position.left,
|
|
147
|
+
position: "absolute",
|
|
148
|
+
opacity: isPositioned ? 1 : 0,
|
|
149
|
+
pointerEvents: "none"
|
|
150
|
+
},
|
|
151
|
+
className: cn(
|
|
152
|
+
"z-50 overflow-hidden rounded-md border border-gray-200 dark:border-gray-700",
|
|
153
|
+
"bg-white dark:bg-gray-800 px-3 py-1.5 text-sm",
|
|
154
|
+
"text-gray-900 dark:text-gray-100 shadow-lg dark:shadow-gray-900/50",
|
|
155
|
+
"animate-in fade-in-0 zoom-in-95 duration-200",
|
|
156
|
+
"whitespace-nowrap",
|
|
157
|
+
className
|
|
158
|
+
),
|
|
159
|
+
...props,
|
|
160
|
+
children
|
|
161
|
+
}
|
|
162
|
+
),
|
|
163
|
+
document.body
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
TooltipContent.displayName = "TooltipContent";
|
|
167
|
+
|
|
168
|
+
// src/components/widgets/BasicWidget.tsx
|
|
169
|
+
import { Grid } from "lucide-react";
|
|
170
|
+
import { jsx as jsx3, jsxs } from "preact/jsx-runtime";
|
|
171
|
+
function BasicWidget({ id, title }) {
|
|
172
|
+
return /* @__PURE__ */ jsx3("div", { className: "p-4 h-full flex flex-col justify-center overflow-hidden", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
173
|
+
/* @__PURE__ */ jsx3("div", { className: "w-12 h-12 rounded-full flex items-center justify-center mx-auto mb-3 bg-gradient-to-br from-blue-100 to-purple-100 dark:from-gray-700 dark:to-gray-600", children: /* @__PURE__ */ jsx3(Grid, { size: 20, className: "text-blue-600 dark:text-gray-200" }) }),
|
|
174
|
+
/* @__PURE__ */ jsx3("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2", children: title || "Basic Widget" }),
|
|
175
|
+
/* @__PURE__ */ jsx3("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Customizable content area" }),
|
|
176
|
+
id && /* @__PURE__ */ jsxs("div", { className: "mt-2 text-xs text-gray-400 dark:text-gray-500", children: [
|
|
177
|
+
"ID: ",
|
|
178
|
+
id
|
|
179
|
+
] })
|
|
180
|
+
] }) });
|
|
181
|
+
}
|
|
182
|
+
function BasicWidgetPreview() {
|
|
183
|
+
return /* @__PURE__ */ jsx3("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
184
|
+
/* @__PURE__ */ jsx3("div", { className: "w-8 h-8 rounded-full bg-gradient-to-br from-blue-100 to-purple-100 dark:from-gray-700 dark:to-gray-600 flex items-center justify-center", children: /* @__PURE__ */ jsx3(Grid, { size: 14, className: "text-blue-600 dark:text-gray-300" }) }),
|
|
185
|
+
/* @__PURE__ */ jsx3("div", { className: "text-xs text-gray-600 dark:text-gray-400", children: "Basic Widget" })
|
|
186
|
+
] }) });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/components/widgets/ProgressBarWidget.tsx
|
|
190
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
191
|
+
function ProgressBarWidget({ data }) {
|
|
192
|
+
const defaultData = {
|
|
193
|
+
label: "Progress",
|
|
194
|
+
value: 75,
|
|
195
|
+
max: 100,
|
|
196
|
+
color: "bg-blue-500"
|
|
197
|
+
};
|
|
198
|
+
const widgetData = data || defaultData;
|
|
199
|
+
const percentage = widgetData.value / widgetData.max * 100;
|
|
200
|
+
return /* @__PURE__ */ jsx4("div", { className: "p-4 h-full flex flex-col justify-center overflow-hidden", children: /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
|
|
201
|
+
/* @__PURE__ */ jsx4("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2", children: widgetData.label }),
|
|
202
|
+
/* @__PURE__ */ jsxs2("div", { className: "text-3xl font-bold text-gray-900 dark:text-gray-100 mb-4", children: [
|
|
203
|
+
widgetData.value,
|
|
204
|
+
"%"
|
|
205
|
+
] }),
|
|
206
|
+
/* @__PURE__ */ jsx4("div", { className: "w-full bg-gray-200 dark:bg-gray-700 rounded-full h-3 mb-2", children: /* @__PURE__ */ jsx4(
|
|
207
|
+
"div",
|
|
208
|
+
{
|
|
209
|
+
className: `h-3 rounded-full ${widgetData.color || "bg-blue-500"} transition-all duration-300`,
|
|
210
|
+
style: { width: `${percentage}%` }
|
|
211
|
+
}
|
|
212
|
+
) }),
|
|
213
|
+
/* @__PURE__ */ jsxs2("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: [
|
|
214
|
+
widgetData.value,
|
|
215
|
+
" of ",
|
|
216
|
+
widgetData.max
|
|
217
|
+
] })
|
|
218
|
+
] }) });
|
|
219
|
+
}
|
|
220
|
+
function ProgressBarPreview() {
|
|
221
|
+
return /* @__PURE__ */ jsx4("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: /* @__PURE__ */ jsxs2("div", { className: "w-full max-w-20", children: [
|
|
222
|
+
/* @__PURE__ */ jsx4("div", { className: "text-xs font-medium text-gray-600 dark:text-gray-400 mb-1 text-center", children: "Progress" }),
|
|
223
|
+
/* @__PURE__ */ jsx4("div", { className: "w-full bg-gray-200 dark:bg-gray-600 rounded-full h-1.5", children: /* @__PURE__ */ jsx4("div", { className: "bg-blue-400 h-1.5 rounded-full w-3/4 animate-pulse" }) })
|
|
224
|
+
] }) });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/components/widgets/PieChartWidget.tsx
|
|
228
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
229
|
+
function PieChartWidget({ data }) {
|
|
230
|
+
const defaultData = {
|
|
231
|
+
label: "Sales Distribution",
|
|
232
|
+
segments: [
|
|
233
|
+
{ name: "Product A", value: 40, color: "#3B82F6" },
|
|
234
|
+
{ name: "Product B", value: 30, color: "#EF4444" },
|
|
235
|
+
{ name: "Product C", value: 20, color: "#10B981" },
|
|
236
|
+
{ name: "Product D", value: 10, color: "#F59E0B" }
|
|
237
|
+
]
|
|
238
|
+
};
|
|
239
|
+
const widgetData = data || defaultData;
|
|
240
|
+
const total = widgetData.segments.reduce((sum, segment) => sum + segment.value, 0);
|
|
241
|
+
let cumulativePercentage = 0;
|
|
242
|
+
const gradientStops = widgetData.segments.map((segment) => {
|
|
243
|
+
const percentage = segment.value / total * 100;
|
|
244
|
+
const start = cumulativePercentage;
|
|
245
|
+
const end = cumulativePercentage + percentage;
|
|
246
|
+
cumulativePercentage = end;
|
|
247
|
+
return `${segment.color} ${start}% ${end}%`;
|
|
248
|
+
}).join(", ");
|
|
249
|
+
return /* @__PURE__ */ jsxs3("div", { className: "p-4 h-full flex flex-col overflow-hidden", children: [
|
|
250
|
+
/* @__PURE__ */ jsx5("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2 text-center flex-shrink-0", children: widgetData.label }),
|
|
251
|
+
/* @__PURE__ */ jsx5("div", { className: "flex-1 flex items-center justify-center min-h-0", children: /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
|
|
252
|
+
/* @__PURE__ */ jsx5(
|
|
253
|
+
"div",
|
|
254
|
+
{
|
|
255
|
+
className: "w-24 h-24 rounded-full",
|
|
256
|
+
style: {
|
|
257
|
+
background: `conic-gradient(${gradientStops})`
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
),
|
|
261
|
+
/* @__PURE__ */ jsx5("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx5("div", { className: "w-8 h-8 bg-white dark:bg-gray-800 rounded-full flex items-center justify-center", children: /* @__PURE__ */ jsx5("span", { className: "text-xs font-bold text-gray-600 dark:text-gray-300", children: total }) }) })
|
|
262
|
+
] }) }),
|
|
263
|
+
/* @__PURE__ */ jsx5("div", { className: "grid grid-cols-2 gap-1 mt-2 flex-shrink-0", children: widgetData.segments.map((segment, index) => /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 min-w-0", children: [
|
|
264
|
+
/* @__PURE__ */ jsx5(
|
|
265
|
+
"div",
|
|
266
|
+
{
|
|
267
|
+
className: "w-2 h-2 rounded-full flex-shrink-0",
|
|
268
|
+
style: { backgroundColor: segment.color }
|
|
269
|
+
}
|
|
270
|
+
),
|
|
271
|
+
/* @__PURE__ */ jsx5("span", { className: "text-xs text-gray-600 dark:text-gray-400 truncate", children: segment.name })
|
|
272
|
+
] }, index)) })
|
|
273
|
+
] });
|
|
274
|
+
}
|
|
275
|
+
function PieChartPreview() {
|
|
276
|
+
return /* @__PURE__ */ jsx5("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
277
|
+
/* @__PURE__ */ jsxs3("div", { className: "relative", children: [
|
|
278
|
+
/* @__PURE__ */ jsx5("div", { className: "w-8 h-8 rounded-full bg-gradient-to-r from-blue-400 via-red-400 to-green-400 animate-pulse" }),
|
|
279
|
+
/* @__PURE__ */ jsx5("div", { className: "absolute inset-0 flex items-center justify-center", children: /* @__PURE__ */ jsx5("div", { className: "w-3 h-3 bg-gray-50 dark:bg-gray-800 rounded-full" }) })
|
|
280
|
+
] }),
|
|
281
|
+
/* @__PURE__ */ jsx5("div", { className: "text-xs text-gray-600 dark:text-gray-400", children: "Pie Chart" })
|
|
282
|
+
] }) });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// src/components/widgets/BarChartWidget.tsx
|
|
286
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
287
|
+
function BarChartWidget({ data }) {
|
|
288
|
+
const defaultData = {
|
|
289
|
+
label: "Monthly Revenue",
|
|
290
|
+
data: [
|
|
291
|
+
{ name: "Jan", value: 65, color: "#3B82F6" },
|
|
292
|
+
{ name: "Feb", value: 85, color: "#3B82F6" },
|
|
293
|
+
{ name: "Mar", value: 75, color: "#3B82F6" },
|
|
294
|
+
{ name: "Apr", value: 95, color: "#3B82F6" },
|
|
295
|
+
{ name: "May", value: 80, color: "#3B82F6" },
|
|
296
|
+
{ name: "Jun", value: 90, color: "#3B82F6" }
|
|
297
|
+
]
|
|
298
|
+
};
|
|
299
|
+
const widgetData = data || defaultData;
|
|
300
|
+
const maxValue = Math.max(...widgetData.data.map((item) => item.value));
|
|
301
|
+
return /* @__PURE__ */ jsxs4("div", { className: "p-4 h-full flex flex-col overflow-hidden", children: [
|
|
302
|
+
/* @__PURE__ */ jsx6("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2 text-center flex-shrink-0", children: widgetData.label }),
|
|
303
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 flex items-end justify-between gap-1 px-2 min-h-0", children: widgetData.data.map((item, index) => {
|
|
304
|
+
const height = item.value / maxValue * 100;
|
|
305
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex-1 flex flex-col items-center", children: [
|
|
306
|
+
/* @__PURE__ */ jsx6("div", { className: "flex-1 flex items-end w-full", children: /* @__PURE__ */ jsx6(
|
|
307
|
+
"div",
|
|
308
|
+
{
|
|
309
|
+
className: "w-full rounded-t transition-all duration-500 hover:opacity-80",
|
|
310
|
+
style: {
|
|
311
|
+
height: `${height}%`,
|
|
312
|
+
backgroundColor: item.color || "#3B82F6",
|
|
313
|
+
minHeight: "4px"
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
) }),
|
|
317
|
+
/* @__PURE__ */ jsx6("div", { className: "text-xs text-gray-600 dark:text-gray-400 mt-1 text-center", children: item.name }),
|
|
318
|
+
/* @__PURE__ */ jsx6("div", { className: "text-xs text-gray-500 dark:text-gray-500", children: item.value })
|
|
319
|
+
] }, index);
|
|
320
|
+
}) })
|
|
321
|
+
] });
|
|
322
|
+
}
|
|
323
|
+
function BarChartPreview() {
|
|
324
|
+
const heights = [60, 80, 40, 90, 70];
|
|
325
|
+
return /* @__PURE__ */ jsxs4("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: [
|
|
326
|
+
/* @__PURE__ */ jsx6("div", { className: "flex items-end gap-1 h-8", children: heights.map((height, index) => /* @__PURE__ */ jsx6(
|
|
327
|
+
"div",
|
|
328
|
+
{
|
|
329
|
+
className: "w-2 bg-blue-400 rounded-t animate-pulse",
|
|
330
|
+
style: {
|
|
331
|
+
height: `${height}%`,
|
|
332
|
+
animationDelay: `${index * 0.1}s`
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
index
|
|
336
|
+
)) }),
|
|
337
|
+
/* @__PURE__ */ jsx6("div", { className: "ml-2 text-xs text-gray-600 dark:text-gray-400", children: "Bar Chart" })
|
|
338
|
+
] });
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// src/components/widgets/LineChartWidget.tsx
|
|
342
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
343
|
+
function LineChartWidget({ data }) {
|
|
344
|
+
const defaultData = {
|
|
345
|
+
label: "Performance Trend",
|
|
346
|
+
data: [
|
|
347
|
+
{ name: "Jan", value: 30 },
|
|
348
|
+
{ name: "Feb", value: 45 },
|
|
349
|
+
{ name: "Mar", value: 35 },
|
|
350
|
+
{ name: "Apr", value: 60 },
|
|
351
|
+
{ name: "May", value: 55 },
|
|
352
|
+
{ name: "Jun", value: 75 },
|
|
353
|
+
{ name: "Jul", value: 70 }
|
|
354
|
+
],
|
|
355
|
+
color: "#3B82F6"
|
|
356
|
+
};
|
|
357
|
+
const widgetData = data || defaultData;
|
|
358
|
+
const maxValue = Math.max(...widgetData.data.map((item) => item.value));
|
|
359
|
+
const minValue = Math.min(...widgetData.data.map((item) => item.value));
|
|
360
|
+
const range = maxValue - minValue;
|
|
361
|
+
const width = 200;
|
|
362
|
+
const height = 80;
|
|
363
|
+
const padding = 10;
|
|
364
|
+
const points = widgetData.data.map((item, index) => {
|
|
365
|
+
const x = padding + index / (widgetData.data.length - 1) * (width - 2 * padding);
|
|
366
|
+
const y = height - padding - (item.value - minValue) / range * (height - 2 * padding);
|
|
367
|
+
return { x, y, value: item.value, name: item.name };
|
|
368
|
+
});
|
|
369
|
+
const pathData = points.reduce((path, point, index) => {
|
|
370
|
+
const command = index === 0 ? "M" : "L";
|
|
371
|
+
return `${path} ${command} ${point.x} ${point.y}`;
|
|
372
|
+
}, "");
|
|
373
|
+
return /* @__PURE__ */ jsxs5("div", { className: "p-4 h-full flex flex-col overflow-hidden", children: [
|
|
374
|
+
/* @__PURE__ */ jsx7("h3", { className: "text-lg font-semibold text-gray-800 dark:text-gray-100 mb-2 text-center flex-shrink-0", children: widgetData.label }),
|
|
375
|
+
/* @__PURE__ */ jsx7("div", { className: "flex-1 flex items-center justify-center min-h-0", children: /* @__PURE__ */ jsx7("div", { className: "relative max-w-full", children: /* @__PURE__ */ jsxs5("svg", { width, height, className: "max-w-full h-auto", children: [
|
|
376
|
+
[0, 25, 50, 75, 100].map((percent, index) => {
|
|
377
|
+
const y = height - padding - percent / 100 * (height - 2 * padding);
|
|
378
|
+
return /* @__PURE__ */ jsx7(
|
|
379
|
+
"line",
|
|
380
|
+
{
|
|
381
|
+
x1: padding,
|
|
382
|
+
y1: y,
|
|
383
|
+
x2: width - padding,
|
|
384
|
+
y2: y,
|
|
385
|
+
stroke: "currentColor",
|
|
386
|
+
strokeWidth: "0.5",
|
|
387
|
+
opacity: "0.2",
|
|
388
|
+
className: "text-gray-400"
|
|
389
|
+
},
|
|
390
|
+
index
|
|
391
|
+
);
|
|
392
|
+
}),
|
|
393
|
+
/* @__PURE__ */ jsx7(
|
|
394
|
+
"path",
|
|
395
|
+
{
|
|
396
|
+
d: pathData,
|
|
397
|
+
fill: "none",
|
|
398
|
+
stroke: widgetData.color || "#3B82F6",
|
|
399
|
+
strokeWidth: "2",
|
|
400
|
+
strokeLinecap: "round",
|
|
401
|
+
strokeLinejoin: "round"
|
|
402
|
+
}
|
|
403
|
+
),
|
|
404
|
+
points.map((point, index) => /* @__PURE__ */ jsx7(
|
|
405
|
+
"circle",
|
|
406
|
+
{
|
|
407
|
+
cx: point.x,
|
|
408
|
+
cy: point.y,
|
|
409
|
+
r: "3",
|
|
410
|
+
fill: widgetData.color || "#3B82F6",
|
|
411
|
+
className: "hover:r-4 transition-all"
|
|
412
|
+
},
|
|
413
|
+
index
|
|
414
|
+
))
|
|
415
|
+
] }) }) }),
|
|
416
|
+
/* @__PURE__ */ jsx7("div", { className: "flex justify-between text-xs text-gray-600 dark:text-gray-400 mt-2 flex-shrink-0 gap-1", children: widgetData.data.map((item, index) => /* @__PURE__ */ jsx7("span", { className: "text-center truncate min-w-0 flex-1", children: item.name }, index)) })
|
|
417
|
+
] });
|
|
418
|
+
}
|
|
419
|
+
function LineChartPreview() {
|
|
420
|
+
return /* @__PURE__ */ jsx7("div", { className: "p-2 h-16 flex items-center justify-center bg-gray-50 dark:bg-gray-800 rounded", children: /* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2", children: [
|
|
421
|
+
/* @__PURE__ */ jsxs5("svg", { width: "32", height: "20", className: "overflow-visible", children: [
|
|
422
|
+
/* @__PURE__ */ jsx7(
|
|
423
|
+
"path",
|
|
424
|
+
{
|
|
425
|
+
d: "M 2 18 L 8 12 L 14 15 L 20 8 L 26 10 L 30 6",
|
|
426
|
+
fill: "none",
|
|
427
|
+
stroke: "#3B82F6",
|
|
428
|
+
strokeWidth: "1.5",
|
|
429
|
+
strokeLinecap: "round",
|
|
430
|
+
className: "animate-pulse"
|
|
431
|
+
}
|
|
432
|
+
),
|
|
433
|
+
[2, 8, 14, 20, 26, 30].map((x, index) => /* @__PURE__ */ jsx7(
|
|
434
|
+
"circle",
|
|
435
|
+
{
|
|
436
|
+
cx: x,
|
|
437
|
+
cy: [18, 12, 15, 8, 10, 6][index],
|
|
438
|
+
r: "1",
|
|
439
|
+
fill: "#3B82F6",
|
|
440
|
+
className: "animate-pulse",
|
|
441
|
+
style: { animationDelay: `${index * 0.1}s` }
|
|
442
|
+
},
|
|
443
|
+
index
|
|
444
|
+
))
|
|
445
|
+
] }),
|
|
446
|
+
/* @__PURE__ */ jsx7("div", { className: "text-xs text-gray-600 dark:text-gray-400", children: "Line Chart" })
|
|
447
|
+
] }) });
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/components/widgets/index.ts
|
|
451
|
+
var widgetRegistry = {
|
|
452
|
+
basic: BasicWidget,
|
|
453
|
+
progress: ProgressBarWidget,
|
|
454
|
+
pie: PieChartWidget,
|
|
455
|
+
bar: BarChartWidget,
|
|
456
|
+
line: LineChartWidget
|
|
457
|
+
};
|
|
458
|
+
var previewRegistry = {
|
|
459
|
+
basic: BasicWidgetPreview,
|
|
460
|
+
progress: ProgressBarPreview,
|
|
461
|
+
pie: PieChartPreview,
|
|
462
|
+
bar: BarChartPreview,
|
|
463
|
+
line: LineChartPreview
|
|
464
|
+
};
|
|
465
|
+
function getWidgetComponent(type) {
|
|
466
|
+
return widgetRegistry[type] || BasicWidget;
|
|
467
|
+
}
|
|
468
|
+
function getPreviewComponent(type) {
|
|
469
|
+
return previewRegistry[type] || BasicWidgetPreview;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// src/components/DashboardToolbar.tsx
|
|
473
|
+
import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
474
|
+
var defaultWidgetTypes = [
|
|
475
|
+
{
|
|
476
|
+
id: "basic",
|
|
477
|
+
type: "basic",
|
|
478
|
+
title: "Basic Widget",
|
|
479
|
+
icon: /* @__PURE__ */ jsx8(Grid2, { size: 20 }),
|
|
480
|
+
defaultSize: { w: 4, h: 4 },
|
|
481
|
+
description: "A simple widget for general content",
|
|
482
|
+
component: widgetRegistry.basic,
|
|
483
|
+
preview: previewRegistry.basic
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
id: "progress",
|
|
487
|
+
type: "progress",
|
|
488
|
+
title: "Progress Bar",
|
|
489
|
+
icon: /* @__PURE__ */ jsx8(TrendingUp, { size: 20 }),
|
|
490
|
+
defaultSize: { w: 4, h: 3 },
|
|
491
|
+
description: "Display progress with visual bar",
|
|
492
|
+
component: widgetRegistry.progress,
|
|
493
|
+
preview: previewRegistry.progress
|
|
494
|
+
}
|
|
495
|
+
];
|
|
496
|
+
function DashboardToolbar({
|
|
497
|
+
isEditMode,
|
|
498
|
+
onToggleMode,
|
|
499
|
+
onAutoOrganize,
|
|
500
|
+
onToggleFixedHeight,
|
|
501
|
+
isFixedHeight,
|
|
502
|
+
gridDimensions,
|
|
503
|
+
itemCount,
|
|
504
|
+
isAddWidgetMode = false,
|
|
505
|
+
onToggleAddWidgetMode,
|
|
506
|
+
onAddWidget,
|
|
507
|
+
availableWidgetTypes = defaultWidgetTypes
|
|
508
|
+
}) {
|
|
509
|
+
const [scrollPosition, setScrollPosition] = React.useState(0);
|
|
510
|
+
const carouselRef = React.useRef(null);
|
|
511
|
+
const scroll = (direction) => {
|
|
512
|
+
if (!carouselRef.current) return;
|
|
513
|
+
const scrollAmount = 200;
|
|
514
|
+
const newPosition = direction === "left" ? Math.max(0, scrollPosition - scrollAmount) : scrollPosition + scrollAmount;
|
|
515
|
+
carouselRef.current.scrollTo({ left: newPosition, behavior: "smooth" });
|
|
516
|
+
setScrollPosition(newPosition);
|
|
517
|
+
};
|
|
518
|
+
return /* @__PURE__ */ jsx8(TooltipProvider, { children: /* @__PURE__ */ jsxs6("div", { className: "bg-white dark:bg-gray-800 shadow-sm border border-gray-200 dark:border-gray-700 rounded-lg p-4 mb-6", children: [
|
|
519
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-between", children: [
|
|
520
|
+
/* @__PURE__ */ jsx8("div", { className: "flex items-center gap-4", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
|
|
521
|
+
/* @__PURE__ */ jsx8(
|
|
522
|
+
Button,
|
|
523
|
+
{
|
|
524
|
+
onClick: onToggleMode,
|
|
525
|
+
variant: isEditMode ? "default" : "outline",
|
|
526
|
+
size: "sm",
|
|
527
|
+
className: "gap-2 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600",
|
|
528
|
+
children: isEditMode ? /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
529
|
+
/* @__PURE__ */ jsx8(Edit3, { size: 16 }),
|
|
530
|
+
"Edit Mode"
|
|
531
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
532
|
+
/* @__PURE__ */ jsx8(Eye, { size: 16 }),
|
|
533
|
+
"View Mode"
|
|
534
|
+
] })
|
|
535
|
+
}
|
|
536
|
+
),
|
|
537
|
+
/* @__PURE__ */ jsxs6("div", { className: "text-sm text-gray-500 dark:text-gray-200 px-2", children: [
|
|
538
|
+
"Grid: ",
|
|
539
|
+
gridDimensions.cols,
|
|
540
|
+
"\xD7",
|
|
541
|
+
gridDimensions.rows
|
|
542
|
+
] })
|
|
543
|
+
] }) }),
|
|
544
|
+
/* @__PURE__ */ jsx8("div", { className: "flex items-center gap-3", children: isEditMode && /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
545
|
+
isAddWidgetMode ? /* @__PURE__ */ jsxs6(
|
|
546
|
+
Button,
|
|
547
|
+
{
|
|
548
|
+
onClick: onToggleAddWidgetMode,
|
|
549
|
+
variant: "outline",
|
|
550
|
+
size: "sm",
|
|
551
|
+
className: "gap-2 text-red-600 hover:text-red-700 border-red-200 hover:border-red-300 bg-white dark:bg-gray-800",
|
|
552
|
+
children: [
|
|
553
|
+
/* @__PURE__ */ jsx8(X, { size: 16 }),
|
|
554
|
+
"Cancel"
|
|
555
|
+
]
|
|
556
|
+
}
|
|
557
|
+
) : /* @__PURE__ */ jsxs6(
|
|
558
|
+
Button,
|
|
559
|
+
{
|
|
560
|
+
onClick: onToggleAddWidgetMode,
|
|
561
|
+
variant: "default",
|
|
562
|
+
size: "sm",
|
|
563
|
+
className: "gap-2 bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200 hover:bg-gray-200 dark:hover:bg-gray-600",
|
|
564
|
+
children: [
|
|
565
|
+
/* @__PURE__ */ jsx8(Plus, { size: 16 }),
|
|
566
|
+
"Add Widget"
|
|
567
|
+
]
|
|
568
|
+
}
|
|
569
|
+
),
|
|
570
|
+
/* @__PURE__ */ jsx8(
|
|
571
|
+
Button,
|
|
572
|
+
{
|
|
573
|
+
onClick: onToggleFixedHeight,
|
|
574
|
+
variant: "secondary",
|
|
575
|
+
size: "sm",
|
|
576
|
+
className: "bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200",
|
|
577
|
+
children: isFixedHeight ? "Auto Height" : "Fix Height"
|
|
578
|
+
}
|
|
579
|
+
),
|
|
580
|
+
/* @__PURE__ */ jsxs6(
|
|
581
|
+
Button,
|
|
582
|
+
{
|
|
583
|
+
onClick: onAutoOrganize,
|
|
584
|
+
variant: "outline",
|
|
585
|
+
size: "sm",
|
|
586
|
+
disabled: itemCount === 0,
|
|
587
|
+
className: "gap-2 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800",
|
|
588
|
+
children: [
|
|
589
|
+
/* @__PURE__ */ jsx8(Zap, { size: 16 }),
|
|
590
|
+
"Auto Organize"
|
|
591
|
+
]
|
|
592
|
+
}
|
|
593
|
+
)
|
|
594
|
+
] }) })
|
|
595
|
+
] }),
|
|
596
|
+
isEditMode && isAddWidgetMode && /* @__PURE__ */ jsxs6("div", { className: "mt-4 pt-4 border-t border-gray-200 dark:border-gray-700", children: [
|
|
597
|
+
/* @__PURE__ */ jsx8("div", { className: "flex items-center gap-2 mb-3", children: /* @__PURE__ */ jsx8("h3", { className: "font-medium text-gray-800 dark:text-gray-200", children: "Select a widget to add:" }) }),
|
|
598
|
+
/* @__PURE__ */ jsx8("div", { className: "relative", children: /* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2", children: [
|
|
599
|
+
/* @__PURE__ */ jsx8(
|
|
600
|
+
Button,
|
|
601
|
+
{
|
|
602
|
+
variant: "outline",
|
|
603
|
+
size: "sm",
|
|
604
|
+
onClick: () => scroll("left"),
|
|
605
|
+
className: "flex-shrink-0 h-12 w-8 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200",
|
|
606
|
+
disabled: scrollPosition === 0,
|
|
607
|
+
children: /* @__PURE__ */ jsx8(ChevronLeft, { size: 16 })
|
|
608
|
+
}
|
|
609
|
+
),
|
|
610
|
+
/* @__PURE__ */ jsx8(
|
|
611
|
+
"div",
|
|
612
|
+
{
|
|
613
|
+
ref: carouselRef,
|
|
614
|
+
className: "flex gap-3 overflow-x-auto scrollbar-hide flex-1",
|
|
615
|
+
style: { scrollbarWidth: "none", msOverflowStyle: "none" },
|
|
616
|
+
children: availableWidgetTypes.map((widget) => {
|
|
617
|
+
const PreviewComponent = widget.preview || getPreviewComponent(widget.type);
|
|
618
|
+
return /* @__PURE__ */ jsxs6(Tooltip, { children: [
|
|
619
|
+
/* @__PURE__ */ jsx8(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs6(
|
|
620
|
+
Button,
|
|
621
|
+
{
|
|
622
|
+
variant: "outline",
|
|
623
|
+
onClick: () => onAddWidget?.(widget),
|
|
624
|
+
className: "flex-shrink-0 h-20 w-24 p-2 flex flex-col items-center gap-1 hover:border-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/50 bg-white dark:bg-gray-800 transition-colors group border-gray-200 dark:border-gray-700",
|
|
625
|
+
children: [
|
|
626
|
+
/* @__PURE__ */ jsx8("div", { className: "text-blue-600 dark:text-blue-400 group-hover:text-blue-700 dark:group-hover:text-blue-300", children: widget.icon }),
|
|
627
|
+
/* @__PURE__ */ jsx8("span", { className: "text-xs font-medium text-center leading-tight text-gray-700 dark:text-gray-200", children: widget.title })
|
|
628
|
+
]
|
|
629
|
+
}
|
|
630
|
+
) }),
|
|
631
|
+
/* @__PURE__ */ jsx8(TooltipContent, { children: /* @__PURE__ */ jsxs6("div", { className: "p-3", children: [
|
|
632
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
633
|
+
/* @__PURE__ */ jsx8("div", { className: "text-blue-600 dark:text-blue-400", children: widget.icon }),
|
|
634
|
+
/* @__PURE__ */ jsxs6("div", { children: [
|
|
635
|
+
/* @__PURE__ */ jsx8("h4", { className: "font-medium text-sm text-gray-800 dark:text-gray-200", children: widget.title }),
|
|
636
|
+
/* @__PURE__ */ jsxs6("p", { className: "text-xs text-gray-500 dark:text-gray-200", children: [
|
|
637
|
+
"Size: ",
|
|
638
|
+
widget.defaultSize.w,
|
|
639
|
+
"\xD7",
|
|
640
|
+
widget.defaultSize.h
|
|
641
|
+
] })
|
|
642
|
+
] })
|
|
643
|
+
] }),
|
|
644
|
+
/* @__PURE__ */ jsx8("p", { className: "text-xs text-gray-600 dark:text-gray-200 mb-3", children: widget.description }),
|
|
645
|
+
/* @__PURE__ */ jsx8("div", { className: "h-16 border border-gray-200 dark:border-gray-700 rounded bg-gray-50 dark:bg-gray-750 overflow-hidden", children: /* @__PURE__ */ jsx8(PreviewComponent, {}) })
|
|
646
|
+
] }) })
|
|
647
|
+
] }, widget.id);
|
|
648
|
+
})
|
|
649
|
+
}
|
|
650
|
+
),
|
|
651
|
+
/* @__PURE__ */ jsx8(
|
|
652
|
+
Button,
|
|
653
|
+
{
|
|
654
|
+
variant: "outline",
|
|
655
|
+
size: "sm",
|
|
656
|
+
onClick: () => scroll("right"),
|
|
657
|
+
className: "flex-shrink-0 h-12 w-8 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200",
|
|
658
|
+
children: /* @__PURE__ */ jsx8(ChevronRight, { size: 16 })
|
|
659
|
+
}
|
|
660
|
+
)
|
|
661
|
+
] }) })
|
|
662
|
+
] })
|
|
663
|
+
] }) });
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// src/lib/useDashboardController.ts
|
|
667
|
+
import { useState as useState2, useCallback } from "react";
|
|
668
|
+
function useDashboardController({
|
|
669
|
+
initialItems = [],
|
|
670
|
+
initialEditMode = false
|
|
671
|
+
} = {}) {
|
|
672
|
+
const [items, setItems] = useState2(initialItems);
|
|
673
|
+
const [isEditMode, setIsEditMode] = useState2(initialEditMode);
|
|
674
|
+
const addItem = useCallback((item) => {
|
|
675
|
+
setItems((prev) => [...prev, item]);
|
|
676
|
+
}, []);
|
|
677
|
+
const removeItem = useCallback((id) => {
|
|
678
|
+
setItems((prev) => prev.filter((item) => item.id !== id));
|
|
679
|
+
}, []);
|
|
680
|
+
const updateItem = useCallback((id, updates) => {
|
|
681
|
+
setItems(
|
|
682
|
+
(prev) => prev.map((item) => item.id === id ? { ...item, ...updates } : item)
|
|
683
|
+
);
|
|
684
|
+
}, []);
|
|
685
|
+
const clear = useCallback(() => {
|
|
686
|
+
setItems([]);
|
|
687
|
+
}, []);
|
|
688
|
+
const save = useCallback(() => {
|
|
689
|
+
const serializedItems = items.map(({ content: _content, ...rest }) => rest);
|
|
690
|
+
return {
|
|
691
|
+
items: serializedItems
|
|
692
|
+
};
|
|
693
|
+
}, [items]);
|
|
694
|
+
const load = useCallback((state) => {
|
|
695
|
+
if (state && Array.isArray(state.items)) {
|
|
696
|
+
setItems(state.items);
|
|
697
|
+
}
|
|
698
|
+
}, []);
|
|
699
|
+
const toggleEditMode = useCallback(() => {
|
|
700
|
+
setIsEditMode((prev) => !prev);
|
|
701
|
+
}, []);
|
|
702
|
+
return {
|
|
703
|
+
items,
|
|
704
|
+
addItem,
|
|
705
|
+
removeItem,
|
|
706
|
+
updateItem,
|
|
707
|
+
setItems,
|
|
708
|
+
save,
|
|
709
|
+
load,
|
|
710
|
+
clear,
|
|
711
|
+
isEditMode,
|
|
712
|
+
toggleEditMode,
|
|
713
|
+
setEditMode: setIsEditMode
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// src/constants.ts
|
|
718
|
+
var GRID_SIZE = 40;
|
|
719
|
+
var MARGIN = 8;
|
|
720
|
+
var CELL_SIZE = GRID_SIZE + MARGIN;
|
|
721
|
+
var ANIMATION_DURATION = 300;
|
|
722
|
+
var MIN_SIZE = 2;
|
|
723
|
+
var MAX_SIZE = 24;
|
|
724
|
+
var CONTAINER_PADDING = 16;
|
|
725
|
+
var MIN_CONTAINER_HEIGHT = 200;
|
|
726
|
+
var DEBOUNCE_DELAY = 150;
|
|
727
|
+
|
|
728
|
+
// src/components/Dashboard.tsx
|
|
729
|
+
import { Fragment as Fragment3, jsx as jsx9, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
730
|
+
var createWidgetRenderer = (widgetRegistry2) => (item) => {
|
|
731
|
+
let WidgetComponent;
|
|
732
|
+
if (widgetRegistry2 && widgetRegistry2[item.type]) {
|
|
733
|
+
WidgetComponent = widgetRegistry2[item.type];
|
|
734
|
+
} else {
|
|
735
|
+
WidgetComponent = getWidgetComponent(item.type);
|
|
736
|
+
}
|
|
737
|
+
return /* @__PURE__ */ jsx9(WidgetComponent, { id: item.id, title: item.title, type: item.type });
|
|
738
|
+
};
|
|
739
|
+
function Dashboard({
|
|
740
|
+
availableWidgetTypes = [],
|
|
741
|
+
initialItems = [],
|
|
742
|
+
widgetRegistry: widgetRegistry2,
|
|
743
|
+
onItemsChange,
|
|
744
|
+
className = "",
|
|
745
|
+
enableEditMode = true,
|
|
746
|
+
defaultEditMode = true,
|
|
747
|
+
// Grid appearance
|
|
748
|
+
gridMode = "elegant",
|
|
749
|
+
// Toolbar customization options
|
|
750
|
+
showDefaultToolbar = true,
|
|
751
|
+
customToolbar,
|
|
752
|
+
toolbarClassName = "",
|
|
753
|
+
// Exposed action callbacks
|
|
754
|
+
onEditModeChange,
|
|
755
|
+
onAddWidgetModeChange,
|
|
756
|
+
onFixedHeightChange,
|
|
757
|
+
controller: externalController
|
|
758
|
+
}) {
|
|
759
|
+
const renderWidgetContent = useMemo(() => createWidgetRenderer(widgetRegistry2), [widgetRegistry2]);
|
|
760
|
+
const transformInitialItems = useCallback2((items2) => {
|
|
761
|
+
return items2.map((item) => ({
|
|
762
|
+
...item,
|
|
763
|
+
content: () => renderWidgetContent(item)
|
|
764
|
+
}));
|
|
765
|
+
}, [renderWidgetContent]);
|
|
766
|
+
const internalController = useDashboardController({
|
|
767
|
+
initialItems: transformInitialItems(initialItems),
|
|
768
|
+
initialEditMode: enableEditMode ? defaultEditMode : false
|
|
769
|
+
});
|
|
770
|
+
const controller = externalController || internalController;
|
|
771
|
+
const items = useMemo(() => {
|
|
772
|
+
return controller.items.map((item) => {
|
|
773
|
+
if (!item.content) {
|
|
774
|
+
return { ...item, content: () => renderWidgetContent(item) };
|
|
775
|
+
}
|
|
776
|
+
return item;
|
|
777
|
+
});
|
|
778
|
+
}, [controller.items, renderWidgetContent]);
|
|
779
|
+
const setItems = controller.setItems;
|
|
780
|
+
const isEditMode = controller.isEditMode;
|
|
781
|
+
const setIsEditMode = controller.setEditMode;
|
|
782
|
+
const [nextId, setNextId] = useState3(() => {
|
|
783
|
+
if (initialItems.length === 0) return 1;
|
|
784
|
+
const maxId = Math.max(...initialItems.map((item) => parseInt(item.id) || 0));
|
|
785
|
+
return maxId + 1;
|
|
786
|
+
});
|
|
787
|
+
const [dragState, setDragState] = useState3(null);
|
|
788
|
+
const [resizeState, setResizeState] = useState3(null);
|
|
789
|
+
const [preview, setPreview] = useState3(null);
|
|
790
|
+
const [gridDimensions, setGridDimensions] = useState3({ width: 800, height: 600, cols: 16, rows: 12 });
|
|
791
|
+
const [maxHeight, setMaxHeight] = useState3(null);
|
|
792
|
+
const [isAddWidgetMode, setIsAddWidgetMode] = useState3(false);
|
|
793
|
+
const containerRef = useRef2(null);
|
|
794
|
+
const debounceTimer = useRef2(null);
|
|
795
|
+
const isInitialLoad = useRef2(true);
|
|
796
|
+
useEffect2(() => {
|
|
797
|
+
if (onItemsChange && !isInitialLoad.current) {
|
|
798
|
+
const itemsForCallback = items.map(({ content, ...item }) => item);
|
|
799
|
+
onItemsChange(itemsForCallback);
|
|
800
|
+
}
|
|
801
|
+
isInitialLoad.current = false;
|
|
802
|
+
}, [items, onItemsChange]);
|
|
803
|
+
useEffect2(() => {
|
|
804
|
+
if (externalController) return;
|
|
805
|
+
const hasNewItems = initialItems.some((initItem) => !items.find((item) => item.id === initItem.id));
|
|
806
|
+
const hasMissingItems = items.some((item) => !initialItems.find((initItem) => initItem.id === item.id) && initialItems.length > 0);
|
|
807
|
+
if (hasNewItems || hasMissingItems && items.length < initialItems.length) {
|
|
808
|
+
const transformedItems = transformInitialItems(initialItems);
|
|
809
|
+
setItems(transformedItems);
|
|
810
|
+
isInitialLoad.current = true;
|
|
811
|
+
}
|
|
812
|
+
}, [initialItems, transformInitialItems, externalController]);
|
|
813
|
+
const itemsOverlap = (a, b) => !(a.x + a.w <= b.x || b.x + b.w <= a.x || a.y + a.h <= b.y || b.y + b.h <= a.y);
|
|
814
|
+
const isValidPosition = (item, allItems, excludeId) => {
|
|
815
|
+
if (item.w < MIN_SIZE || item.h < MIN_SIZE) return false;
|
|
816
|
+
if (item.x < 0 || item.y < 0 || item.x + item.w > gridDimensions.cols || item.y + item.h > gridDimensions.rows) {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
return !allItems.some((other) => {
|
|
820
|
+
if (other.id === item.id || other.id === excludeId) return false;
|
|
821
|
+
return itemsOverlap(item, other);
|
|
822
|
+
});
|
|
823
|
+
};
|
|
824
|
+
function pixelToGrid(px, py, forCoord = "generic") {
|
|
825
|
+
if (forCoord === "x") return Math.round(px / CELL_SIZE);
|
|
826
|
+
if (forCoord === "y") return Math.round(py / CELL_SIZE);
|
|
827
|
+
return {
|
|
828
|
+
// Fallback to old logic if used generically, but prefer specific coord.
|
|
829
|
+
x: Math.round((px - CONTAINER_PADDING) / CELL_SIZE),
|
|
830
|
+
y: Math.round((py - CONTAINER_PADDING) / CELL_SIZE)
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
const gridToPixel = (x, y) => ({
|
|
834
|
+
x: x * CELL_SIZE,
|
|
835
|
+
y: y * CELL_SIZE
|
|
836
|
+
});
|
|
837
|
+
const findSafePosition = (item, existingItems, preferCurrentOverStoredOriginal = false) => {
|
|
838
|
+
let currentW = Math.min(item.w, gridDimensions.cols);
|
|
839
|
+
let currentH = item.h;
|
|
840
|
+
let startX = item.x;
|
|
841
|
+
let startY = item.y;
|
|
842
|
+
if (item.originalX !== void 0 && item.originalY !== void 0 && !preferCurrentOverStoredOriginal) {
|
|
843
|
+
startX = item.originalX;
|
|
844
|
+
startY = item.originalY;
|
|
845
|
+
if (item.originalW !== void 0) currentW = Math.min(item.originalW, gridDimensions.cols);
|
|
846
|
+
if (item.originalH !== void 0) currentH = item.originalH;
|
|
847
|
+
}
|
|
848
|
+
startX = Math.min(startX, gridDimensions.cols - currentW);
|
|
849
|
+
const testItemInitial = { ...item, x: startX, y: startY, w: currentW, h: currentH };
|
|
850
|
+
if (isValidPosition(testItemInitial, existingItems)) {
|
|
851
|
+
return { ...testItemInitial, isAnimating: true };
|
|
852
|
+
}
|
|
853
|
+
if (startX === 0 && startY === 0 && existingItems.length > 0) {
|
|
854
|
+
const maxY = Math.max(...existingItems.map((it) => it.y + it.h), 0);
|
|
855
|
+
for (let y = 0; y <= Math.max(maxY, gridDimensions.rows - currentH); y++) {
|
|
856
|
+
for (let x = 0; x <= gridDimensions.cols - currentW; x++) {
|
|
857
|
+
const candidateItem = { ...item, x, y, w: currentW, h: currentH };
|
|
858
|
+
if (isValidPosition(candidateItem, existingItems)) {
|
|
859
|
+
return { ...candidateItem, isAnimating: true };
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
const maxSearchDistance = Math.max(gridDimensions.cols, gridDimensions.rows);
|
|
865
|
+
for (let distance = 1; distance <= maxSearchDistance; distance++) {
|
|
866
|
+
for (let dy = -distance; dy <= distance; dy++) {
|
|
867
|
+
for (let dx = -distance; dx <= distance; dx++) {
|
|
868
|
+
if (Math.abs(dx) !== distance && Math.abs(dy) !== distance) continue;
|
|
869
|
+
const testX = startX + dx;
|
|
870
|
+
const testY = startY + dy;
|
|
871
|
+
if (testX < 0 || testX + currentW > gridDimensions.cols || testY < 0 || testY + currentH > gridDimensions.rows) continue;
|
|
872
|
+
const candidateItem = { ...item, x: testX, y: testY, w: currentW, h: currentH };
|
|
873
|
+
if (isValidPosition(candidateItem, existingItems)) {
|
|
874
|
+
return { ...candidateItem, isAnimating: true };
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
for (let y = 0; y <= gridDimensions.rows - currentH; y++) {
|
|
880
|
+
for (let x = 0; x <= gridDimensions.cols - currentW; x++) {
|
|
881
|
+
const candidateItem = { ...item, x, y, w: currentW, h: currentH };
|
|
882
|
+
if (isValidPosition(candidateItem, existingItems)) {
|
|
883
|
+
return { ...candidateItem, isAnimating: true };
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
const finalW = Math.min(currentW, gridDimensions.cols);
|
|
888
|
+
const finalH = Math.min(currentH, gridDimensions.rows);
|
|
889
|
+
return { ...item, x: 0, y: 0, w: finalW, h: finalH, isAnimating: true };
|
|
890
|
+
};
|
|
891
|
+
const detectAndFixOverlaps = (currentItems, tryReAnchorToOriginals = false) => {
|
|
892
|
+
const result = currentItems.map((i) => ({ ...i }));
|
|
893
|
+
let hasOverlaps = true;
|
|
894
|
+
let attempts = 0;
|
|
895
|
+
const maxAttempts = Math.max(20, result.length * 2);
|
|
896
|
+
while (hasOverlaps && attempts < maxAttempts) {
|
|
897
|
+
hasOverlaps = false;
|
|
898
|
+
attempts++;
|
|
899
|
+
result.sort((a, b) => a.y - b.y || a.x - b.x);
|
|
900
|
+
for (let i = 0; i < result.length; i++) {
|
|
901
|
+
for (let j = i + 1; j < result.length; j++) {
|
|
902
|
+
if (itemsOverlap(result[i], result[j])) {
|
|
903
|
+
hasOverlaps = true;
|
|
904
|
+
const itemToMoveIndex = result[j].y > result[i].y || result[j].y === result[i].y && result[j].x > result[i].x ? j : i;
|
|
905
|
+
const itemToMove = result[itemToMoveIndex];
|
|
906
|
+
const fixedItems = result.filter((_, idx) => idx !== itemToMoveIndex);
|
|
907
|
+
const newPositionData = findSafePosition(itemToMove, fixedItems, !tryReAnchorToOriginals);
|
|
908
|
+
result[itemToMoveIndex] = {
|
|
909
|
+
...itemToMove,
|
|
910
|
+
x: newPositionData.x,
|
|
911
|
+
y: newPositionData.y,
|
|
912
|
+
w: newPositionData.w,
|
|
913
|
+
h: newPositionData.h,
|
|
914
|
+
isAnimating: newPositionData.isAnimating
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
return result;
|
|
921
|
+
};
|
|
922
|
+
const calculateDimensions = useCallback2(() => {
|
|
923
|
+
if (!containerRef.current) return;
|
|
924
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
925
|
+
const availableWidth = rect.width - CONTAINER_PADDING * 2;
|
|
926
|
+
const newCols = Math.max(MIN_SIZE, Math.floor((availableWidth + MARGIN) / CELL_SIZE));
|
|
927
|
+
const tempClampedItems = items.map((it) => ({
|
|
928
|
+
...it,
|
|
929
|
+
w: Math.min(it.originalW !== void 0 ? it.originalW : it.w, newCols)
|
|
930
|
+
// Use originalW if available
|
|
931
|
+
}));
|
|
932
|
+
const maxY = tempClampedItems.length > 0 ? Math.max(...tempClampedItems.map((item) => item.y + item.h), 0) : MIN_SIZE;
|
|
933
|
+
const minRowsForMinHeight = Math.ceil((MIN_CONTAINER_HEIGHT - CONTAINER_PADDING * 2 + MARGIN) / CELL_SIZE);
|
|
934
|
+
const requiredRows = Math.max(minRowsForMinHeight, maxY + 4);
|
|
935
|
+
const newHeight = maxHeight || Math.max(
|
|
936
|
+
MIN_CONTAINER_HEIGHT,
|
|
937
|
+
requiredRows * CELL_SIZE - MARGIN + CONTAINER_PADDING * 2
|
|
938
|
+
);
|
|
939
|
+
const oldCols = gridDimensions.cols;
|
|
940
|
+
const oldRows = gridDimensions.rows;
|
|
941
|
+
setGridDimensions({
|
|
942
|
+
width: rect.width,
|
|
943
|
+
// Updated to use full container width
|
|
944
|
+
height: newHeight,
|
|
945
|
+
cols: newCols,
|
|
946
|
+
rows: requiredRows
|
|
947
|
+
});
|
|
948
|
+
if ((newCols !== oldCols || requiredRows > oldRows + 2) && newCols > 0 && items.length > 0) {
|
|
949
|
+
reflowItems(newCols, requiredRows);
|
|
950
|
+
}
|
|
951
|
+
}, [items, maxHeight, gridDimensions.cols, gridDimensions.rows]);
|
|
952
|
+
const reflowItems = (newCols, newRows) => {
|
|
953
|
+
const processedItems = [];
|
|
954
|
+
const isValidPositionForNewGrid = (item, allItems, excludeId) => {
|
|
955
|
+
if (item.w < MIN_SIZE || item.h < MIN_SIZE) return false;
|
|
956
|
+
if (item.x < 0 || item.y < 0 || item.x + item.w > newCols || item.y + item.h > newRows) {
|
|
957
|
+
return false;
|
|
958
|
+
}
|
|
959
|
+
return !allItems.some((other) => {
|
|
960
|
+
if (other.id === item.id || other.id === excludeId) return false;
|
|
961
|
+
return itemsOverlap(item, other);
|
|
962
|
+
});
|
|
963
|
+
};
|
|
964
|
+
const sortedItems = [...items].sort((a, b) => {
|
|
965
|
+
const aHasOriginal = a.originalX !== void 0 && a.originalY !== void 0;
|
|
966
|
+
const bHasOriginal = b.originalX !== void 0 && b.originalY !== void 0;
|
|
967
|
+
if (aHasOriginal && !bHasOriginal) return -1;
|
|
968
|
+
if (!aHasOriginal && bHasOriginal) return 1;
|
|
969
|
+
if (aHasOriginal && bHasOriginal) {
|
|
970
|
+
if (a.originalY !== b.originalY) return a.originalY - b.originalY;
|
|
971
|
+
return a.originalX - b.originalX;
|
|
972
|
+
}
|
|
973
|
+
return parseInt(a.id) - parseInt(b.id);
|
|
974
|
+
});
|
|
975
|
+
const itemsToProcess = sortedItems.map((item) => {
|
|
976
|
+
const targetW = item.originalW !== void 0 ? Math.min(item.originalW, newCols) : Math.min(item.w, newCols);
|
|
977
|
+
const targetH = item.originalH !== void 0 ? item.originalH : item.h;
|
|
978
|
+
return {
|
|
979
|
+
...item,
|
|
980
|
+
w: Math.max(MIN_SIZE, targetW),
|
|
981
|
+
// Ensure min width
|
|
982
|
+
h: Math.max(MIN_SIZE, targetH),
|
|
983
|
+
// Ensure min height
|
|
984
|
+
isAnimating: true
|
|
985
|
+
};
|
|
986
|
+
});
|
|
987
|
+
if (newCols > gridDimensions.cols) {
|
|
988
|
+
const restored = [];
|
|
989
|
+
const needsRepositioning = [];
|
|
990
|
+
for (const item of itemsToProcess) {
|
|
991
|
+
if (item.originalX !== void 0 && item.originalY !== void 0) {
|
|
992
|
+
const restoredItem = {
|
|
993
|
+
...item,
|
|
994
|
+
// Contains already adjusted w/h
|
|
995
|
+
x: item.originalX,
|
|
996
|
+
y: item.originalY
|
|
997
|
+
};
|
|
998
|
+
if (restoredItem.x + restoredItem.w <= newCols && restoredItem.y + restoredItem.h <= newRows && isValidPositionForNewGrid(restoredItem, restored)) {
|
|
999
|
+
restored.push(restoredItem);
|
|
1000
|
+
} else {
|
|
1001
|
+
needsRepositioning.push(item);
|
|
1002
|
+
}
|
|
1003
|
+
} else {
|
|
1004
|
+
needsRepositioning.push(item);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
for (const item of needsRepositioning) {
|
|
1008
|
+
const safePosData = findSafePositionForNewGrid(item, restored, false, newCols, newRows);
|
|
1009
|
+
restored.push({
|
|
1010
|
+
...item,
|
|
1011
|
+
// Contains item's target w/h
|
|
1012
|
+
x: safePosData.x,
|
|
1013
|
+
y: safePosData.y,
|
|
1014
|
+
w: safePosData.w,
|
|
1015
|
+
// Use width from findSafePosition
|
|
1016
|
+
h: safePosData.h
|
|
1017
|
+
// Use height from findSafePosition
|
|
1018
|
+
// originalX/Y/W/H are preserved from item
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
setItems(detectAndFixOverlaps(restored, true));
|
|
1022
|
+
} else {
|
|
1023
|
+
for (const item of itemsToProcess) {
|
|
1024
|
+
let targetX = item.x;
|
|
1025
|
+
if (newCols < gridDimensions.cols) {
|
|
1026
|
+
targetX = Math.min(item.x, Math.max(0, newCols - item.w));
|
|
1027
|
+
}
|
|
1028
|
+
const itemForSafePos = { ...item, x: targetX };
|
|
1029
|
+
const safePosData = findSafePositionForNewGrid(itemForSafePos, processedItems, true, newCols, newRows);
|
|
1030
|
+
processedItems.push({
|
|
1031
|
+
...item,
|
|
1032
|
+
// Contains item's target w/h
|
|
1033
|
+
x: safePosData.x,
|
|
1034
|
+
y: safePosData.y,
|
|
1035
|
+
w: safePosData.w,
|
|
1036
|
+
h: safePosData.h
|
|
1037
|
+
});
|
|
1038
|
+
}
|
|
1039
|
+
setItems(detectAndFixOverlaps(processedItems, true));
|
|
1040
|
+
}
|
|
1041
|
+
setTimeout(() => setItems((prev) => prev.map((it) => ({ ...it, isAnimating: false }))), ANIMATION_DURATION);
|
|
1042
|
+
};
|
|
1043
|
+
const findSafePositionForNewGrid = (item, existingItems, preferCurrentOverStoredOriginal = false, cols, rows) => {
|
|
1044
|
+
let currentW = Math.min(item.w, cols);
|
|
1045
|
+
let currentH = item.h;
|
|
1046
|
+
let startX = item.x;
|
|
1047
|
+
let startY = item.y;
|
|
1048
|
+
if (item.originalX !== void 0 && item.originalY !== void 0 && !preferCurrentOverStoredOriginal) {
|
|
1049
|
+
startX = item.originalX;
|
|
1050
|
+
startY = item.originalY;
|
|
1051
|
+
if (item.originalW !== void 0) currentW = Math.min(item.originalW, cols);
|
|
1052
|
+
if (item.originalH !== void 0) currentH = item.originalH;
|
|
1053
|
+
}
|
|
1054
|
+
startX = Math.min(startX, cols - currentW);
|
|
1055
|
+
const testItemInitial = { ...item, x: startX, y: startY, w: currentW, h: currentH };
|
|
1056
|
+
const isValidForNewGrid = (testItem) => {
|
|
1057
|
+
if (testItem.w < MIN_SIZE || testItem.h < MIN_SIZE) return false;
|
|
1058
|
+
if (testItem.x < 0 || testItem.y < 0 || testItem.x + testItem.w > cols || testItem.y + testItem.h > rows) {
|
|
1059
|
+
return false;
|
|
1060
|
+
}
|
|
1061
|
+
return !existingItems.some((other) => {
|
|
1062
|
+
if (other.id === testItem.id) return false;
|
|
1063
|
+
return itemsOverlap(testItem, other);
|
|
1064
|
+
});
|
|
1065
|
+
};
|
|
1066
|
+
if (isValidForNewGrid(testItemInitial)) {
|
|
1067
|
+
return { ...testItemInitial, isAnimating: true };
|
|
1068
|
+
}
|
|
1069
|
+
const maxSearchDistance = Math.max(cols, rows);
|
|
1070
|
+
for (let distance = 1; distance <= maxSearchDistance; distance++) {
|
|
1071
|
+
for (let dy = -distance; dy <= distance; dy++) {
|
|
1072
|
+
for (let dx = -distance; dx <= distance; dx++) {
|
|
1073
|
+
if (Math.abs(dx) !== distance && Math.abs(dy) !== distance) continue;
|
|
1074
|
+
const testX = startX + dx;
|
|
1075
|
+
const testY = startY + dy;
|
|
1076
|
+
if (testX < 0 || testX + currentW > cols || testY < 0 || testY + currentH > rows) continue;
|
|
1077
|
+
const candidateItem = { ...item, x: testX, y: testY, w: currentW, h: currentH };
|
|
1078
|
+
if (isValidForNewGrid(candidateItem)) {
|
|
1079
|
+
return { ...candidateItem, isAnimating: true };
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
for (let y = 0; y <= rows - currentH; y++) {
|
|
1085
|
+
for (let x = 0; x <= cols - currentW; x++) {
|
|
1086
|
+
const candidateItem = { ...item, x, y, w: currentW, h: currentH };
|
|
1087
|
+
if (isValidForNewGrid(candidateItem)) {
|
|
1088
|
+
return { ...candidateItem, isAnimating: true };
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
const finalW = Math.min(currentW, cols);
|
|
1093
|
+
const finalH = Math.min(currentH, rows);
|
|
1094
|
+
return { ...item, x: 0, y: 0, w: finalW, h: finalH, isAnimating: true };
|
|
1095
|
+
};
|
|
1096
|
+
const handleMouseMove = useCallback2((e) => {
|
|
1097
|
+
const gridContentRect = containerRef.current?.getBoundingClientRect();
|
|
1098
|
+
if (!gridContentRect) return;
|
|
1099
|
+
const mouseXInGridContent = e.clientX - gridContentRect.left - CONTAINER_PADDING;
|
|
1100
|
+
const mouseYInGridContent = e.clientY - gridContentRect.top - CONTAINER_PADDING;
|
|
1101
|
+
if (dragState && preview) {
|
|
1102
|
+
const itemX = mouseXInGridContent - dragState.startX;
|
|
1103
|
+
const itemY = mouseYInGridContent - dragState.startY;
|
|
1104
|
+
const gridPos = {
|
|
1105
|
+
x: pixelToGrid(itemX, 0, "x"),
|
|
1106
|
+
// Only X coordinate matters for X grid position
|
|
1107
|
+
y: pixelToGrid(0, itemY, "y")
|
|
1108
|
+
// Only Y coordinate matters for Y grid position
|
|
1109
|
+
};
|
|
1110
|
+
let newX = Math.max(0, Math.min(gridDimensions.cols - preview.w, gridPos.x));
|
|
1111
|
+
let newY = Math.max(0, Math.min(gridDimensions.rows - preview.h, gridPos.y));
|
|
1112
|
+
setPreview({ ...preview, x: newX, y: newY });
|
|
1113
|
+
}
|
|
1114
|
+
if (resizeState && preview) {
|
|
1115
|
+
let { x, y, w, h } = resizeState.originalItem;
|
|
1116
|
+
const handle = resizeState.handle;
|
|
1117
|
+
const dxInGridUnits = pixelToGrid(mouseXInGridContent - resizeState.startX, 0, "x");
|
|
1118
|
+
const dyInGridUnits = pixelToGrid(0, mouseYInGridContent - resizeState.startY, "y");
|
|
1119
|
+
if (handle.includes("e")) w = Math.max(MIN_SIZE, Math.min(MAX_SIZE, resizeState.originalItem.w + dxInGridUnits));
|
|
1120
|
+
if (handle.includes("w")) {
|
|
1121
|
+
const newW = Math.max(MIN_SIZE, Math.min(MAX_SIZE, resizeState.originalItem.w - dxInGridUnits));
|
|
1122
|
+
x = resizeState.originalItem.x + (resizeState.originalItem.w - newW);
|
|
1123
|
+
w = newW;
|
|
1124
|
+
}
|
|
1125
|
+
if (handle.includes("s")) h = Math.max(MIN_SIZE, Math.min(MAX_SIZE, resizeState.originalItem.h + dyInGridUnits));
|
|
1126
|
+
if (handle.includes("n")) {
|
|
1127
|
+
const newH = Math.max(MIN_SIZE, Math.min(MAX_SIZE, resizeState.originalItem.h - dyInGridUnits));
|
|
1128
|
+
y = resizeState.originalItem.y + (resizeState.originalItem.h - newH);
|
|
1129
|
+
h = newH;
|
|
1130
|
+
}
|
|
1131
|
+
x = Math.max(0, x);
|
|
1132
|
+
y = Math.max(0, y);
|
|
1133
|
+
w = Math.min(w, gridDimensions.cols - x);
|
|
1134
|
+
h = Math.min(h, gridDimensions.rows - y);
|
|
1135
|
+
w = Math.max(MIN_SIZE, w);
|
|
1136
|
+
h = Math.max(MIN_SIZE, h);
|
|
1137
|
+
setPreview({ ...preview, x, y, w, h });
|
|
1138
|
+
}
|
|
1139
|
+
}, [dragState, resizeState, preview, gridDimensions.cols, gridDimensions.rows]);
|
|
1140
|
+
const handleMouseUp = useCallback2(() => {
|
|
1141
|
+
if ((dragState || resizeState) && preview) {
|
|
1142
|
+
const finalX = Math.max(0, Math.min(preview.x, gridDimensions.cols - MIN_SIZE));
|
|
1143
|
+
const finalY = Math.max(0, Math.min(preview.y, gridDimensions.rows - MIN_SIZE));
|
|
1144
|
+
const finalW = Math.max(MIN_SIZE, Math.min(preview.w, gridDimensions.cols - finalX));
|
|
1145
|
+
const finalH = Math.max(MIN_SIZE, Math.min(preview.h, gridDimensions.rows - finalY));
|
|
1146
|
+
const finalPreview = {
|
|
1147
|
+
...preview,
|
|
1148
|
+
x: finalX,
|
|
1149
|
+
y: finalY,
|
|
1150
|
+
w: finalW,
|
|
1151
|
+
h: finalH
|
|
1152
|
+
};
|
|
1153
|
+
const updatedItems = items.map((item) => {
|
|
1154
|
+
if (item.id === (dragState?.id || resizeState?.id)) {
|
|
1155
|
+
return {
|
|
1156
|
+
...finalPreview,
|
|
1157
|
+
originalX: finalPreview.x,
|
|
1158
|
+
originalY: finalPreview.y,
|
|
1159
|
+
originalW: finalPreview.w,
|
|
1160
|
+
originalH: finalPreview.h,
|
|
1161
|
+
isAnimating: true
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
return item;
|
|
1165
|
+
});
|
|
1166
|
+
setItems(updatedItems);
|
|
1167
|
+
setItems((prev) => detectAndFixOverlaps(prev.map((it) => ({ ...it, isAnimating: false })), true));
|
|
1168
|
+
}
|
|
1169
|
+
setDragState(null);
|
|
1170
|
+
setResizeState(null);
|
|
1171
|
+
setPreview(null);
|
|
1172
|
+
}, [dragState, resizeState, preview, items, gridDimensions.cols, gridDimensions.rows]);
|
|
1173
|
+
useEffect2(() => {
|
|
1174
|
+
if (dragState || resizeState) {
|
|
1175
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
1176
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
1177
|
+
return () => {
|
|
1178
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
1179
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
}, [dragState, resizeState, handleMouseMove, handleMouseUp]);
|
|
1183
|
+
useEffect2(() => {
|
|
1184
|
+
const observer = new ResizeObserver(() => {
|
|
1185
|
+
if (debounceTimer.current) clearTimeout(debounceTimer.current);
|
|
1186
|
+
debounceTimer.current = setTimeout(calculateDimensions, DEBOUNCE_DELAY);
|
|
1187
|
+
});
|
|
1188
|
+
if (containerRef.current) {
|
|
1189
|
+
observer.observe(containerRef.current);
|
|
1190
|
+
calculateDimensions();
|
|
1191
|
+
}
|
|
1192
|
+
return () => {
|
|
1193
|
+
observer.disconnect();
|
|
1194
|
+
if (debounceTimer.current) clearTimeout(debounceTimer.current);
|
|
1195
|
+
};
|
|
1196
|
+
}, [calculateDimensions]);
|
|
1197
|
+
const toggleEditMode = () => {
|
|
1198
|
+
if (!enableEditMode) return;
|
|
1199
|
+
const newEditMode = !isEditMode;
|
|
1200
|
+
setIsEditMode(newEditMode);
|
|
1201
|
+
setIsAddWidgetMode(false);
|
|
1202
|
+
setDragState(null);
|
|
1203
|
+
setResizeState(null);
|
|
1204
|
+
setPreview(null);
|
|
1205
|
+
onEditModeChange?.(newEditMode);
|
|
1206
|
+
};
|
|
1207
|
+
const toggleAddWidgetMode = () => {
|
|
1208
|
+
if (!isEditMode) return;
|
|
1209
|
+
const newAddWidgetMode = !isAddWidgetMode;
|
|
1210
|
+
setIsAddWidgetMode(newAddWidgetMode);
|
|
1211
|
+
onAddWidgetModeChange?.(newAddWidgetMode);
|
|
1212
|
+
};
|
|
1213
|
+
const toggleFixedHeight = () => {
|
|
1214
|
+
const newMaxHeight = maxHeight === null ? MIN_CONTAINER_HEIGHT * 1.5 : null;
|
|
1215
|
+
setMaxHeight(newMaxHeight);
|
|
1216
|
+
onFixedHeightChange?.(newMaxHeight !== null);
|
|
1217
|
+
};
|
|
1218
|
+
const addWidgetAtPosition = (widgetConfig, x, y) => {
|
|
1219
|
+
if (!isEditMode) return;
|
|
1220
|
+
const tempItemForPositioning = {
|
|
1221
|
+
id: "temp",
|
|
1222
|
+
x: x ?? 0,
|
|
1223
|
+
y: y ?? 0,
|
|
1224
|
+
w: widgetConfig.defaultSize.w,
|
|
1225
|
+
h: widgetConfig.defaultSize.h,
|
|
1226
|
+
type: widgetConfig.type,
|
|
1227
|
+
title: widgetConfig.title,
|
|
1228
|
+
content: () => null,
|
|
1229
|
+
originalX: x ?? 0,
|
|
1230
|
+
originalY: y ?? 0,
|
|
1231
|
+
originalW: widgetConfig.defaultSize.w,
|
|
1232
|
+
originalH: widgetConfig.defaultSize.h
|
|
1233
|
+
};
|
|
1234
|
+
const safePosData = findSafePosition(tempItemForPositioning, items, false);
|
|
1235
|
+
const newItem = {
|
|
1236
|
+
id: nextId.toString(),
|
|
1237
|
+
x: safePosData.x,
|
|
1238
|
+
y: safePosData.y,
|
|
1239
|
+
w: safePosData.w,
|
|
1240
|
+
h: safePosData.h,
|
|
1241
|
+
type: widgetConfig.type,
|
|
1242
|
+
title: widgetConfig.title,
|
|
1243
|
+
content: () => renderWidgetContent({ id: nextId.toString(), type: widgetConfig.type, title: widgetConfig.title }),
|
|
1244
|
+
originalX: safePosData.x,
|
|
1245
|
+
originalY: safePosData.y,
|
|
1246
|
+
originalW: safePosData.w,
|
|
1247
|
+
originalH: safePosData.h,
|
|
1248
|
+
onMenuClick: widgetConfig.onMenuClick,
|
|
1249
|
+
menuIcon: widgetConfig.menuIcon,
|
|
1250
|
+
isAnimating: true
|
|
1251
|
+
};
|
|
1252
|
+
if (isValidPosition(newItem, items)) {
|
|
1253
|
+
const newItems = [...items, newItem];
|
|
1254
|
+
const itemsWithOverlapsFixed = detectAndFixOverlaps(newItems, true);
|
|
1255
|
+
setItems(itemsWithOverlapsFixed);
|
|
1256
|
+
setNextId((prevId) => prevId + 1);
|
|
1257
|
+
setTimeout(() => {
|
|
1258
|
+
setItems((prev) => prev.map((it) => ({ ...it, isAnimating: false })));
|
|
1259
|
+
}, ANIMATION_DURATION);
|
|
1260
|
+
} else {
|
|
1261
|
+
const fallbackItem = { ...newItem, x: 0, y: 0, originalX: 0, originalY: 0 };
|
|
1262
|
+
const newItems = [...items, fallbackItem];
|
|
1263
|
+
const itemsWithOverlapsFixed = detectAndFixOverlaps(newItems, true);
|
|
1264
|
+
setItems(itemsWithOverlapsFixed);
|
|
1265
|
+
setNextId((prevId) => prevId + 1);
|
|
1266
|
+
setTimeout(() => {
|
|
1267
|
+
setItems((prev) => prev.map((it) => ({ ...it, isAnimating: false })));
|
|
1268
|
+
}, ANIMATION_DURATION);
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
const removeItem = (id) => {
|
|
1272
|
+
if (!isEditMode) return;
|
|
1273
|
+
setItems(items.filter((item) => item.id !== id));
|
|
1274
|
+
};
|
|
1275
|
+
const autoOrganize = () => {
|
|
1276
|
+
if (!isEditMode) return;
|
|
1277
|
+
const sorted = [...items].sort((a, b) => {
|
|
1278
|
+
const aArea = (a.originalW ?? a.w) * (a.originalH ?? a.h);
|
|
1279
|
+
const bArea = (b.originalW ?? b.w) * (b.originalH ?? b.h);
|
|
1280
|
+
return bArea - aArea || parseInt(a.id) - parseInt(b.id);
|
|
1281
|
+
});
|
|
1282
|
+
const organized = [];
|
|
1283
|
+
for (const item of sorted) {
|
|
1284
|
+
const targetW = Math.min(
|
|
1285
|
+
item.originalW ?? item.w,
|
|
1286
|
+
gridDimensions.cols
|
|
1287
|
+
);
|
|
1288
|
+
const targetH = item.originalH ?? item.h;
|
|
1289
|
+
const itemToPlace = {
|
|
1290
|
+
...item,
|
|
1291
|
+
w: Math.max(MIN_SIZE, targetW),
|
|
1292
|
+
h: Math.max(MIN_SIZE, targetH),
|
|
1293
|
+
isAnimating: true
|
|
1294
|
+
};
|
|
1295
|
+
let bestPosition = null;
|
|
1296
|
+
let bestY = Infinity;
|
|
1297
|
+
let bestX = Infinity;
|
|
1298
|
+
for (let y = 0; y <= gridDimensions.rows - itemToPlace.h; y++) {
|
|
1299
|
+
for (let x = 0; x <= gridDimensions.cols - itemToPlace.w; x++) {
|
|
1300
|
+
const candidateItem = { ...itemToPlace, x, y };
|
|
1301
|
+
const isValid = !organized.some(
|
|
1302
|
+
(placedItem) => itemsOverlap(candidateItem, placedItem)
|
|
1303
|
+
);
|
|
1304
|
+
if (isValid) {
|
|
1305
|
+
if (y < bestY || y === bestY && x < bestX) {
|
|
1306
|
+
bestPosition = { x, y };
|
|
1307
|
+
bestY = y;
|
|
1308
|
+
bestX = x;
|
|
1309
|
+
break;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
if (bestPosition && bestY === y) break;
|
|
1314
|
+
}
|
|
1315
|
+
if (bestPosition) {
|
|
1316
|
+
const organizedItem = {
|
|
1317
|
+
...item,
|
|
1318
|
+
x: bestPosition.x,
|
|
1319
|
+
y: bestPosition.y,
|
|
1320
|
+
w: itemToPlace.w,
|
|
1321
|
+
h: itemToPlace.h,
|
|
1322
|
+
originalX: bestPosition.x,
|
|
1323
|
+
originalY: bestPosition.y,
|
|
1324
|
+
originalW: itemToPlace.w,
|
|
1325
|
+
originalH: itemToPlace.h,
|
|
1326
|
+
isAnimating: true
|
|
1327
|
+
};
|
|
1328
|
+
organized.push(organizedItem);
|
|
1329
|
+
} else {
|
|
1330
|
+
const safePositionData = findSafePosition(itemToPlace, organized, false);
|
|
1331
|
+
const organizedItem = {
|
|
1332
|
+
...item,
|
|
1333
|
+
x: safePositionData.x,
|
|
1334
|
+
y: safePositionData.y,
|
|
1335
|
+
w: safePositionData.w,
|
|
1336
|
+
h: safePositionData.h,
|
|
1337
|
+
originalX: safePositionData.x,
|
|
1338
|
+
originalY: safePositionData.y,
|
|
1339
|
+
originalW: safePositionData.w,
|
|
1340
|
+
originalH: safePositionData.h,
|
|
1341
|
+
isAnimating: true
|
|
1342
|
+
};
|
|
1343
|
+
organized.push(organizedItem);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
setItems(organized);
|
|
1347
|
+
setTimeout(() => {
|
|
1348
|
+
setItems((prev) => prev.map((item) => ({ ...item, isAnimating: false })));
|
|
1349
|
+
}, ANIMATION_DURATION);
|
|
1350
|
+
};
|
|
1351
|
+
const renderItem = (item, isPreview = false) => {
|
|
1352
|
+
const pos = gridToPixel(item.x, item.y);
|
|
1353
|
+
const itemWidth = Math.max(0, item.w);
|
|
1354
|
+
const itemHeight = Math.max(0, item.h);
|
|
1355
|
+
const size = {
|
|
1356
|
+
width: itemWidth * GRID_SIZE + (itemWidth > 0 ? (itemWidth - 1) * MARGIN : 0),
|
|
1357
|
+
height: itemHeight * GRID_SIZE + (itemHeight > 0 ? (itemHeight - 1) * MARGIN : 0)
|
|
1358
|
+
};
|
|
1359
|
+
const isActive = dragState?.id === item.id || resizeState?.id === item.id;
|
|
1360
|
+
const handleMouseDownOnWidget = (e) => {
|
|
1361
|
+
if (!isEditMode || isPreview) return;
|
|
1362
|
+
e.preventDefault();
|
|
1363
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
1364
|
+
const mouseX = e.clientX - rect.left;
|
|
1365
|
+
const mouseY = e.clientY - rect.top;
|
|
1366
|
+
const cornerSize = 20;
|
|
1367
|
+
const isInTopLeft = mouseX <= cornerSize && mouseY <= cornerSize;
|
|
1368
|
+
const isInTopRight = mouseX >= rect.width - cornerSize && mouseY <= cornerSize;
|
|
1369
|
+
const isInBottomLeft = mouseX <= cornerSize && mouseY >= rect.height - cornerSize;
|
|
1370
|
+
const isInBottomRight = mouseX >= rect.width - cornerSize && mouseY >= rect.height - cornerSize;
|
|
1371
|
+
if (isInTopLeft || isInTopRight || isInBottomLeft || isInBottomRight) {
|
|
1372
|
+
let handle = "";
|
|
1373
|
+
if (isInTopLeft) handle = "nw";
|
|
1374
|
+
else if (isInTopRight) handle = "ne";
|
|
1375
|
+
else if (isInBottomLeft) handle = "sw";
|
|
1376
|
+
else if (isInBottomRight) handle = "se";
|
|
1377
|
+
const gridContentRect = containerRef.current?.getBoundingClientRect();
|
|
1378
|
+
if (!gridContentRect) return;
|
|
1379
|
+
setResizeState({
|
|
1380
|
+
id: item.id,
|
|
1381
|
+
startX: e.clientX - gridContentRect.left - CONTAINER_PADDING,
|
|
1382
|
+
startY: e.clientY - gridContentRect.top - CONTAINER_PADDING,
|
|
1383
|
+
originalItem: { ...item },
|
|
1384
|
+
handle
|
|
1385
|
+
});
|
|
1386
|
+
setPreview({ ...item });
|
|
1387
|
+
} else {
|
|
1388
|
+
const gridContentRect = containerRef.current?.getBoundingClientRect();
|
|
1389
|
+
if (!gridContentRect) return;
|
|
1390
|
+
const mouseXInGridContent = e.clientX - gridContentRect.left - CONTAINER_PADDING;
|
|
1391
|
+
const mouseYInGridContent = e.clientY - gridContentRect.top - CONTAINER_PADDING;
|
|
1392
|
+
const itemPixelPos = gridToPixel(item.x, item.y);
|
|
1393
|
+
setDragState({
|
|
1394
|
+
id: item.id,
|
|
1395
|
+
startX: mouseXInGridContent - itemPixelPos.x,
|
|
1396
|
+
startY: mouseYInGridContent - itemPixelPos.y,
|
|
1397
|
+
originalItem: { ...item }
|
|
1398
|
+
});
|
|
1399
|
+
setPreview({ ...item });
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
return /* @__PURE__ */ jsxs7(
|
|
1403
|
+
"div",
|
|
1404
|
+
{
|
|
1405
|
+
className: `group absolute rounded-lg border ${item.isAnimating || isPreview ? "transition-all duration-300" : ""} ${isPreview ? "bg-blue-100 dark:bg-blue-900/30 border-blue-300 dark:border-blue-600 border-2 opacity-80 z-50" : `bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 shadow-lg hover:shadow-xl dark:shadow-gray-900/50 z-10 ${isActive && !isPreview ? "opacity-50" : ""}`} ${isEditMode && !isPreview ? "cursor-grab active:cursor-grabbing" : "cursor-default"}`,
|
|
1406
|
+
style: { left: pos.x, top: pos.y, width: size.width, height: size.height },
|
|
1407
|
+
onMouseDown: handleMouseDownOnWidget,
|
|
1408
|
+
children: [
|
|
1409
|
+
!isPreview && isEditMode && /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1410
|
+
/* @__PURE__ */ jsx9("div", { className: "absolute top-0 left-0 w-5 h-5 cursor-nw-resize z-20" }),
|
|
1411
|
+
/* @__PURE__ */ jsx9("div", { className: "absolute top-0 right-0 w-5 h-5 cursor-ne-resize z-20" }),
|
|
1412
|
+
/* @__PURE__ */ jsx9("div", { className: "absolute bottom-0 left-0 w-5 h-5 cursor-sw-resize z-20" }),
|
|
1413
|
+
/* @__PURE__ */ jsx9("div", { className: "absolute bottom-0 right-0 w-5 h-5 cursor-se-resize z-20" })
|
|
1414
|
+
] }),
|
|
1415
|
+
/* @__PURE__ */ jsxs7(
|
|
1416
|
+
"div",
|
|
1417
|
+
{
|
|
1418
|
+
className: `flex items-center justify-between p-2 text-xs border-b select-none ${isPreview ? "bg-blue-50 dark:bg-blue-900/50 border-blue-200 dark:border-blue-700" : "bg-gradient-to-r from-blue-50 to-purple-50 dark:from-gray-700 dark:to-gray-700 border-gray-200 dark:border-gray-700 hover:from-blue-100 hover:to-purple-100 dark:hover:from-gray-600 dark:hover:to-gray-600"}`,
|
|
1419
|
+
children: [
|
|
1420
|
+
/* @__PURE__ */ jsx9("span", { className: `font-medium truncate ${isPreview ? "text-blue-700 dark:text-blue-300" : "text-gray-700 dark:text-gray-200"}`, children: item.title }),
|
|
1421
|
+
!isPreview && /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-0.5 flex-shrink-0", children: [
|
|
1422
|
+
isEditMode && item.onMenuClick && /* @__PURE__ */ jsx9(
|
|
1423
|
+
"button",
|
|
1424
|
+
{
|
|
1425
|
+
className: "text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300 p-0.5",
|
|
1426
|
+
onClick: (e) => {
|
|
1427
|
+
e.stopPropagation();
|
|
1428
|
+
item.onMenuClick?.(e);
|
|
1429
|
+
},
|
|
1430
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
1431
|
+
title: "Widget options",
|
|
1432
|
+
children: item.menuIcon || /* @__PURE__ */ jsx9(MoreHorizontal, { size: 14 })
|
|
1433
|
+
}
|
|
1434
|
+
),
|
|
1435
|
+
isEditMode && /* @__PURE__ */ jsx9(
|
|
1436
|
+
"button",
|
|
1437
|
+
{
|
|
1438
|
+
onClick: (e) => {
|
|
1439
|
+
e.stopPropagation();
|
|
1440
|
+
removeItem(item.id);
|
|
1441
|
+
},
|
|
1442
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
1443
|
+
className: "text-gray-400 dark:text-gray-500 hover:text-red-500 dark:hover:text-red-400 p-0.5 hover:bg-red-50 dark:hover:bg-red-900/20 rounded",
|
|
1444
|
+
title: "Remove widget",
|
|
1445
|
+
children: /* @__PURE__ */ jsx9(X2, { size: 14 })
|
|
1446
|
+
}
|
|
1447
|
+
)
|
|
1448
|
+
] })
|
|
1449
|
+
]
|
|
1450
|
+
}
|
|
1451
|
+
),
|
|
1452
|
+
/* @__PURE__ */ jsx9("div", { className: "p-1 flex items-center justify-center h-[calc(100%-30px)] overflow-auto", children: item.content ? item.content() : null })
|
|
1453
|
+
]
|
|
1454
|
+
},
|
|
1455
|
+
`${isPreview ? "preview-" : ""}${item.id}`
|
|
1456
|
+
);
|
|
1457
|
+
};
|
|
1458
|
+
const dashboardActions = {
|
|
1459
|
+
toggleEditMode,
|
|
1460
|
+
toggleAddWidgetMode,
|
|
1461
|
+
autoOrganize,
|
|
1462
|
+
toggleFixedHeight,
|
|
1463
|
+
addWidget: addWidgetAtPosition,
|
|
1464
|
+
removeItem
|
|
1465
|
+
};
|
|
1466
|
+
const dashboardState = {
|
|
1467
|
+
isEditMode,
|
|
1468
|
+
isAddWidgetMode,
|
|
1469
|
+
isFixedHeight: maxHeight !== null,
|
|
1470
|
+
gridDimensions,
|
|
1471
|
+
itemCount: items.length,
|
|
1472
|
+
items
|
|
1473
|
+
};
|
|
1474
|
+
const customToolbarProps = {
|
|
1475
|
+
state: dashboardState,
|
|
1476
|
+
actions: dashboardActions,
|
|
1477
|
+
availableWidgetTypes
|
|
1478
|
+
};
|
|
1479
|
+
const renderElegantGrid = () => /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1480
|
+
/* @__PURE__ */ jsx9(
|
|
1481
|
+
"div",
|
|
1482
|
+
{
|
|
1483
|
+
className: "absolute pointer-events-none opacity-20",
|
|
1484
|
+
style: {
|
|
1485
|
+
top: CONTAINER_PADDING,
|
|
1486
|
+
left: CONTAINER_PADDING,
|
|
1487
|
+
right: CONTAINER_PADDING,
|
|
1488
|
+
bottom: CONTAINER_PADDING,
|
|
1489
|
+
backgroundImage: `
|
|
1490
|
+
linear-gradient(to right, rgba(156, 163, 175, 0.25) 1px, transparent 1px),
|
|
1491
|
+
linear-gradient(to bottom, rgba(156, 163, 175, 0.25) 1px, transparent 1px)
|
|
1492
|
+
`,
|
|
1493
|
+
backgroundSize: `${CELL_SIZE}px ${CELL_SIZE}px`
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
),
|
|
1497
|
+
/* @__PURE__ */ jsx9(
|
|
1498
|
+
"div",
|
|
1499
|
+
{
|
|
1500
|
+
className: "absolute pointer-events-none dark:block hidden opacity-15",
|
|
1501
|
+
style: {
|
|
1502
|
+
top: CONTAINER_PADDING,
|
|
1503
|
+
left: CONTAINER_PADDING,
|
|
1504
|
+
right: CONTAINER_PADDING,
|
|
1505
|
+
bottom: CONTAINER_PADDING,
|
|
1506
|
+
backgroundImage: `
|
|
1507
|
+
linear-gradient(to right, rgba(75, 85, 99, 0.3) 1px, transparent 1px),
|
|
1508
|
+
linear-gradient(to bottom, rgba(75, 85, 99, 0.3) 1px, transparent 1px)
|
|
1509
|
+
`,
|
|
1510
|
+
backgroundSize: `${CELL_SIZE}px ${CELL_SIZE}px`
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
)
|
|
1514
|
+
] });
|
|
1515
|
+
const renderDotsGrid = () => /* @__PURE__ */ jsx9(
|
|
1516
|
+
"div",
|
|
1517
|
+
{
|
|
1518
|
+
className: "absolute pointer-events-none opacity-30 dark:opacity-20",
|
|
1519
|
+
style: {
|
|
1520
|
+
top: CONTAINER_PADDING,
|
|
1521
|
+
left: CONTAINER_PADDING,
|
|
1522
|
+
right: CONTAINER_PADDING,
|
|
1523
|
+
bottom: CONTAINER_PADDING,
|
|
1524
|
+
backgroundImage: `
|
|
1525
|
+
radial-gradient(circle at center, rgba(156, 163, 175, 0.4) 1px, transparent 1px),
|
|
1526
|
+
radial-gradient(circle at center, rgba(75, 85, 99, 0.5) 1px, transparent 1px)
|
|
1527
|
+
`,
|
|
1528
|
+
backgroundSize: `${CELL_SIZE}px ${CELL_SIZE}px`,
|
|
1529
|
+
backgroundPosition: `${CELL_SIZE / 2}px ${CELL_SIZE / 2}px`
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
);
|
|
1533
|
+
const renderHarshGrid = () => /* @__PURE__ */ jsx9(
|
|
1534
|
+
"div",
|
|
1535
|
+
{
|
|
1536
|
+
className: "absolute opacity-20 dark:opacity-10 pointer-events-none",
|
|
1537
|
+
style: {
|
|
1538
|
+
top: CONTAINER_PADDING,
|
|
1539
|
+
left: CONTAINER_PADDING,
|
|
1540
|
+
right: CONTAINER_PADDING,
|
|
1541
|
+
bottom: CONTAINER_PADDING,
|
|
1542
|
+
backgroundImage: `
|
|
1543
|
+
linear-gradient(to right, #ccc 1px, transparent 1px),
|
|
1544
|
+
linear-gradient(to bottom, #ccc 1px, transparent 1px)
|
|
1545
|
+
`,
|
|
1546
|
+
backgroundSize: `${CELL_SIZE}px ${CELL_SIZE}px`
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
);
|
|
1550
|
+
const renderGrid = () => {
|
|
1551
|
+
switch (gridMode) {
|
|
1552
|
+
case "dots":
|
|
1553
|
+
return renderDotsGrid();
|
|
1554
|
+
case "harsh":
|
|
1555
|
+
return renderHarshGrid();
|
|
1556
|
+
case "blank":
|
|
1557
|
+
return null;
|
|
1558
|
+
case "elegant":
|
|
1559
|
+
default:
|
|
1560
|
+
return renderElegantGrid();
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
return /* @__PURE__ */ jsxs7("div", { className: `p-4 bg-gray-50 dark:bg-gray-900 min-h-screen ${className}`, children: [
|
|
1564
|
+
showDefaultToolbar && !customToolbar && /* @__PURE__ */ jsx9(
|
|
1565
|
+
DashboardToolbar,
|
|
1566
|
+
{
|
|
1567
|
+
isEditMode,
|
|
1568
|
+
onToggleMode: toggleEditMode,
|
|
1569
|
+
onAutoOrganize: autoOrganize,
|
|
1570
|
+
onToggleFixedHeight: toggleFixedHeight,
|
|
1571
|
+
isFixedHeight: maxHeight !== null,
|
|
1572
|
+
gridDimensions,
|
|
1573
|
+
itemCount: items.length,
|
|
1574
|
+
isAddWidgetMode,
|
|
1575
|
+
onToggleAddWidgetMode: toggleAddWidgetMode,
|
|
1576
|
+
onAddWidget: addWidgetAtPosition,
|
|
1577
|
+
availableWidgetTypes
|
|
1578
|
+
}
|
|
1579
|
+
),
|
|
1580
|
+
customToolbar && /* @__PURE__ */ jsx9("div", { className: toolbarClassName, children: React2.isValidElement(customToolbar) ? customToolbar : React2.createElement(customToolbar, customToolbarProps) }),
|
|
1581
|
+
/* @__PURE__ */ jsx9("div", { className: "w-full", children: /* @__PURE__ */ jsxs7(
|
|
1582
|
+
"div",
|
|
1583
|
+
{
|
|
1584
|
+
ref: containerRef,
|
|
1585
|
+
className: `relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg w-full ${maxHeight ? "overflow-auto" : "overflow-hidden"}`,
|
|
1586
|
+
style: { height: gridDimensions.height, minHeight: MIN_CONTAINER_HEIGHT, padding: CONTAINER_PADDING },
|
|
1587
|
+
children: [
|
|
1588
|
+
renderGrid(),
|
|
1589
|
+
/* @__PURE__ */ jsxs7(
|
|
1590
|
+
"div",
|
|
1591
|
+
{
|
|
1592
|
+
className: "relative w-full",
|
|
1593
|
+
style: { height: Math.max(0, gridDimensions.rows * CELL_SIZE - MARGIN), minHeight: `calc(100% - ${CONTAINER_PADDING * 2}px)` },
|
|
1594
|
+
children: [
|
|
1595
|
+
items.map((item) => renderItem(item)),
|
|
1596
|
+
preview && renderItem(preview, true),
|
|
1597
|
+
items.length === 0 && !isAddWidgetMode && /* @__PURE__ */ jsx9("div", { className: "absolute inset-0 flex items-center justify-center text-gray-400 dark:text-gray-500", children: /* @__PURE__ */ jsxs7("div", { className: "text-center", children: [
|
|
1598
|
+
/* @__PURE__ */ jsx9("div", { className: "w-16 h-16 bg-gray-100 dark:bg-gray-700 rounded-full flex items-center justify-center mx-auto mb-3", children: /* @__PURE__ */ jsx9(Plus2, { size: 24 }) }),
|
|
1599
|
+
/* @__PURE__ */ jsx9("p", { children: isEditMode ? 'Click "Add Widget" to start' : "No widgets" })
|
|
1600
|
+
] }) })
|
|
1601
|
+
]
|
|
1602
|
+
}
|
|
1603
|
+
)
|
|
1604
|
+
]
|
|
1605
|
+
}
|
|
1606
|
+
) })
|
|
1607
|
+
] });
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
// #style-inject:#style-inject
|
|
1611
|
+
function styleInject(css, { insertAt } = {}) {
|
|
1612
|
+
if (!css || typeof document === "undefined") return;
|
|
1613
|
+
const head = document.head || document.getElementsByTagName("head")[0];
|
|
1614
|
+
const style = document.createElement("style");
|
|
1615
|
+
style.type = "text/css";
|
|
1616
|
+
if (insertAt === "top") {
|
|
1617
|
+
if (head.firstChild) {
|
|
1618
|
+
head.insertBefore(style, head.firstChild);
|
|
1619
|
+
} else {
|
|
1620
|
+
head.appendChild(style);
|
|
1621
|
+
}
|
|
1622
|
+
} else {
|
|
1623
|
+
head.appendChild(style);
|
|
1624
|
+
}
|
|
1625
|
+
if (style.styleSheet) {
|
|
1626
|
+
style.styleSheet.cssText = css;
|
|
1627
|
+
} else {
|
|
1628
|
+
style.appendChild(document.createTextNode(css));
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// src/styles.css
|
|
1633
|
+
styleInject('*,\n::before,\n::after {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n::backdrop {\n --tw-border-spacing-x: 0;\n --tw-border-spacing-y: 0;\n --tw-translate-x: 0;\n --tw-translate-y: 0;\n --tw-rotate: 0;\n --tw-skew-x: 0;\n --tw-skew-y: 0;\n --tw-scale-x: 1;\n --tw-scale-y: 1;\n --tw-pan-x: ;\n --tw-pan-y: ;\n --tw-pinch-zoom: ;\n --tw-scroll-snap-strictness: proximity;\n --tw-gradient-from-position: ;\n --tw-gradient-via-position: ;\n --tw-gradient-to-position: ;\n --tw-ordinal: ;\n --tw-slashed-zero: ;\n --tw-numeric-figure: ;\n --tw-numeric-spacing: ;\n --tw-numeric-fraction: ;\n --tw-ring-inset: ;\n --tw-ring-offset-width: 0px;\n --tw-ring-offset-color: #fff;\n --tw-ring-color: rgb(59 130 246 / 0.5);\n --tw-ring-offset-shadow: 0 0 #0000;\n --tw-ring-shadow: 0 0 #0000;\n --tw-shadow: 0 0 #0000;\n --tw-shadow-colored: 0 0 #0000;\n --tw-blur: ;\n --tw-brightness: ;\n --tw-contrast: ;\n --tw-grayscale: ;\n --tw-hue-rotate: ;\n --tw-invert: ;\n --tw-saturate: ;\n --tw-sepia: ;\n --tw-drop-shadow: ;\n --tw-backdrop-blur: ;\n --tw-backdrop-brightness: ;\n --tw-backdrop-contrast: ;\n --tw-backdrop-grayscale: ;\n --tw-backdrop-hue-rotate: ;\n --tw-backdrop-invert: ;\n --tw-backdrop-opacity: ;\n --tw-backdrop-saturate: ;\n --tw-backdrop-sepia: ;\n --tw-contain-size: ;\n --tw-contain-layout: ;\n --tw-contain-paint: ;\n --tw-contain-style: ;\n}\n*,\n::before,\n::after {\n box-sizing: border-box;\n border-width: 0;\n border-style: solid;\n border-color: #e5e7eb;\n}\n::before,\n::after {\n --tw-content: "";\n}\nhtml,\n:host {\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n -moz-tab-size: 4;\n -o-tab-size: 4;\n tab-size: 4;\n font-family:\n ui-sans-serif,\n system-ui,\n sans-serif,\n "Apple Color Emoji",\n "Segoe UI Emoji",\n "Segoe UI Symbol",\n "Noto Color Emoji";\n font-feature-settings: normal;\n font-variation-settings: normal;\n -webkit-tap-highlight-color: transparent;\n}\nbody {\n margin: 0;\n line-height: inherit;\n}\nhr {\n height: 0;\n color: inherit;\n border-top-width: 1px;\n}\nabbr:where([title]) {\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n}\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n font-size: inherit;\n font-weight: inherit;\n}\na {\n color: inherit;\n text-decoration: inherit;\n}\nb,\nstrong {\n font-weight: bolder;\n}\ncode,\nkbd,\nsamp,\npre {\n font-family:\n ui-monospace,\n SFMono-Regular,\n Menlo,\n Monaco,\n Consolas,\n "Liberation Mono",\n "Courier New",\n monospace;\n font-feature-settings: normal;\n font-variation-settings: normal;\n font-size: 1em;\n}\nsmall {\n font-size: 80%;\n}\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsub {\n bottom: -0.25em;\n}\nsup {\n top: -0.5em;\n}\ntable {\n text-indent: 0;\n border-color: inherit;\n border-collapse: collapse;\n}\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit;\n font-feature-settings: inherit;\n font-variation-settings: inherit;\n font-size: 100%;\n font-weight: inherit;\n line-height: inherit;\n letter-spacing: inherit;\n color: inherit;\n margin: 0;\n padding: 0;\n}\nbutton,\nselect {\n text-transform: none;\n}\nbutton,\ninput:where([type=button]),\ninput:where([type=reset]),\ninput:where([type=submit]) {\n -webkit-appearance: button;\n background-color: transparent;\n background-image: none;\n}\n:-moz-focusring {\n outline: auto;\n}\n:-moz-ui-invalid {\n box-shadow: none;\n}\nprogress {\n vertical-align: baseline;\n}\n::-webkit-inner-spin-button,\n::-webkit-outer-spin-button {\n height: auto;\n}\n[type=search] {\n -webkit-appearance: textfield;\n outline-offset: -2px;\n}\n::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n::-webkit-file-upload-button {\n -webkit-appearance: button;\n font: inherit;\n}\nsummary {\n display: list-item;\n}\nblockquote,\ndl,\ndd,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\nhr,\nfigure,\np,\npre {\n margin: 0;\n}\nfieldset {\n margin: 0;\n padding: 0;\n}\nlegend {\n padding: 0;\n}\nol,\nul,\nmenu {\n list-style: none;\n margin: 0;\n padding: 0;\n}\ndialog {\n padding: 0;\n}\ntextarea {\n resize: vertical;\n}\ninput::-moz-placeholder,\ntextarea::-moz-placeholder {\n opacity: 1;\n color: #9ca3af;\n}\ninput::placeholder,\ntextarea::placeholder {\n opacity: 1;\n color: #9ca3af;\n}\nbutton,\n[role=button] {\n cursor: pointer;\n}\n:disabled {\n cursor: default;\n}\nimg,\nsvg,\nvideo,\ncanvas,\naudio,\niframe,\nembed,\nobject {\n display: block;\n vertical-align: middle;\n}\nimg,\nvideo {\n max-width: 100%;\n height: auto;\n}\n[hidden]:where(:not([hidden=until-found])) {\n display: none;\n}\n:root {\n --background: 0 0% 100%;\n --foreground: 222.2 84% 4.9%;\n --card: 0 0% 100%;\n --card-foreground: 222.2 84% 4.9%;\n --popover: 0 0% 100%;\n --popover-foreground: 222.2 84% 4.9%;\n --primary: 222.2 47.4% 11.2%;\n --primary-foreground: 210 40% 98%;\n --secondary: 210 40% 96.1%;\n --secondary-foreground: 222.2 47.4% 11.2%;\n --muted: 210 40% 96.1%;\n --muted-foreground: 215.4 16.3% 46.9%;\n --accent: 210 40% 96.1%;\n --accent-foreground: 222.2 47.4% 11.2%;\n --destructive: 0 84.2% 60.2%;\n --destructive-foreground: 210 40% 98%;\n --border: 214.3 31.8% 91.4%;\n --input: 214.3 31.8% 91.4%;\n --ring: 222.2 84% 4.9%;\n --radius: 0.5rem;\n}\n* {\n border-color: hsl(var(--border));\n}\nbody {\n background-color: hsl(var(--background));\n color: hsl(var(--foreground));\n width: 100vw;\n max-width: 100vw;\n}\n#root {\n width: 100%;\n height: 100%;\n}\n.container {\n width: 100%;\n}\n@media (min-width: 640px) {\n .container {\n max-width: 640px;\n }\n}\n@media (min-width: 768px) {\n .container {\n max-width: 768px;\n }\n}\n@media (min-width: 1024px) {\n .container {\n max-width: 1024px;\n }\n}\n@media (min-width: 1280px) {\n .container {\n max-width: 1280px;\n }\n}\n@media (min-width: 1536px) {\n .container {\n max-width: 1536px;\n }\n}\n.pointer-events-none {\n pointer-events: none;\n}\n.invisible {\n visibility: hidden;\n}\n.fixed {\n position: fixed;\n}\n.absolute {\n position: absolute;\n}\n.relative {\n position: relative;\n}\n.inset-0 {\n inset: 0px;\n}\n.bottom-0 {\n bottom: 0px;\n}\n.left-0 {\n left: 0px;\n}\n.right-0 {\n right: 0px;\n}\n.right-4 {\n right: 1rem;\n}\n.top-0 {\n top: 0px;\n}\n.top-4 {\n top: 1rem;\n}\n.z-10 {\n z-index: 10;\n}\n.z-20 {\n z-index: 20;\n}\n.z-40 {\n z-index: 40;\n}\n.z-50 {\n z-index: 50;\n}\n.mx-auto {\n margin-left: auto;\n margin-right: auto;\n}\n.my-1 {\n margin-top: 0.25rem;\n margin-bottom: 0.25rem;\n}\n.mb-1 {\n margin-bottom: 0.25rem;\n}\n.mb-2 {\n margin-bottom: 0.5rem;\n}\n.mb-3 {\n margin-bottom: 0.75rem;\n}\n.mb-4 {\n margin-bottom: 1rem;\n}\n.mb-6 {\n margin-bottom: 1.5rem;\n}\n.mb-8 {\n margin-bottom: 2rem;\n}\n.ml-2 {\n margin-left: 0.5rem;\n}\n.mt-1 {\n margin-top: 0.25rem;\n}\n.mt-2 {\n margin-top: 0.5rem;\n}\n.mt-4 {\n margin-top: 1rem;\n}\n.mt-8 {\n margin-top: 2rem;\n}\n.inline-block {\n display: inline-block;\n}\n.flex {\n display: flex;\n}\n.inline-flex {\n display: inline-flex;\n}\n.grid {\n display: grid;\n}\n.hidden {\n display: none;\n}\n.h-1\\.5 {\n height: 0.375rem;\n}\n.h-10 {\n height: 2.5rem;\n}\n.h-11 {\n height: 2.75rem;\n}\n.h-12 {\n height: 3rem;\n}\n.h-16 {\n height: 4rem;\n}\n.h-2 {\n height: 0.5rem;\n}\n.h-20 {\n height: 5rem;\n}\n.h-24 {\n height: 6rem;\n}\n.h-3 {\n height: 0.75rem;\n}\n.h-5 {\n height: 1.25rem;\n}\n.h-8 {\n height: 2rem;\n}\n.h-9 {\n height: 2.25rem;\n}\n.h-\\[calc\\(100\\%-30px\\)\\] {\n height: calc(100% - 30px);\n}\n.h-auto {\n height: auto;\n}\n.h-full {\n height: 100%;\n}\n.min-h-0 {\n min-height: 0px;\n}\n.min-h-screen {\n min-height: 100vh;\n}\n.w-10 {\n width: 2.5rem;\n}\n.w-12 {\n width: 3rem;\n}\n.w-16 {\n width: 4rem;\n}\n.w-2 {\n width: 0.5rem;\n}\n.w-24 {\n width: 6rem;\n}\n.w-3 {\n width: 0.75rem;\n}\n.w-3\\/4 {\n width: 75%;\n}\n.w-48 {\n width: 12rem;\n}\n.w-5 {\n width: 1.25rem;\n}\n.w-8 {\n width: 2rem;\n}\n.w-full {\n width: 100%;\n}\n.min-w-0 {\n min-width: 0px;\n}\n.max-w-20 {\n max-width: 5rem;\n}\n.max-w-7xl {\n max-width: 80rem;\n}\n.max-w-full {\n max-width: 100%;\n}\n.flex-1 {\n flex: 1 1 0%;\n}\n.flex-shrink-0 {\n flex-shrink: 0;\n}\n@keyframes pulse {\n 50% {\n opacity: .5;\n }\n}\n.animate-pulse {\n animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;\n}\n.cursor-default {\n cursor: default;\n}\n.cursor-grab {\n cursor: grab;\n}\n.cursor-ne-resize {\n cursor: ne-resize;\n}\n.cursor-nw-resize {\n cursor: nw-resize;\n}\n.cursor-se-resize {\n cursor: se-resize;\n}\n.cursor-sw-resize {\n cursor: sw-resize;\n}\n.select-none {\n -webkit-user-select: none;\n -moz-user-select: none;\n user-select: none;\n}\n.resize {\n resize: both;\n}\n.grid-cols-2 {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n}\n.flex-col {\n flex-direction: column;\n}\n.items-start {\n align-items: flex-start;\n}\n.items-end {\n align-items: flex-end;\n}\n.items-center {\n align-items: center;\n}\n.justify-center {\n justify-content: center;\n}\n.justify-between {\n justify-content: space-between;\n}\n.gap-0\\.5 {\n gap: 0.125rem;\n}\n.gap-1 {\n gap: 0.25rem;\n}\n.gap-2 {\n gap: 0.5rem;\n}\n.gap-3 {\n gap: 0.75rem;\n}\n.gap-4 {\n gap: 1rem;\n}\n.overflow-auto {\n overflow: auto;\n}\n.overflow-hidden {\n overflow: hidden;\n}\n.overflow-visible {\n overflow: visible;\n}\n.overflow-x-auto {\n overflow-x: auto;\n}\n.truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.whitespace-nowrap {\n white-space: nowrap;\n}\n.rounded {\n border-radius: 0.25rem;\n}\n.rounded-full {\n border-radius: 9999px;\n}\n.rounded-lg {\n border-radius: var(--radius);\n}\n.rounded-md {\n border-radius: calc(var(--radius) - 2px);\n}\n.rounded-t {\n border-top-left-radius: 0.25rem;\n border-top-right-radius: 0.25rem;\n}\n.border {\n border-width: 1px;\n}\n.border-2 {\n border-width: 2px;\n}\n.border-b {\n border-bottom-width: 1px;\n}\n.border-t {\n border-top-width: 1px;\n}\n.border-blue-200 {\n --tw-border-opacity: 1;\n border-color: rgb(191 219 254 / var(--tw-border-opacity, 1));\n}\n.border-blue-300 {\n --tw-border-opacity: 1;\n border-color: rgb(147 197 253 / var(--tw-border-opacity, 1));\n}\n.border-gray-200 {\n --tw-border-opacity: 1;\n border-color: rgb(229 231 235 / var(--tw-border-opacity, 1));\n}\n.border-gray-300 {\n --tw-border-opacity: 1;\n border-color: rgb(209 213 219 / var(--tw-border-opacity, 1));\n}\n.border-green-200 {\n --tw-border-opacity: 1;\n border-color: rgb(187 247 208 / var(--tw-border-opacity, 1));\n}\n.border-input {\n border-color: hsl(var(--input));\n}\n.border-red-200 {\n --tw-border-opacity: 1;\n border-color: rgb(254 202 202 / var(--tw-border-opacity, 1));\n}\n.bg-background {\n background-color: hsl(var(--background));\n}\n.bg-blue-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(219 234 254 / var(--tw-bg-opacity, 1));\n}\n.bg-blue-400 {\n --tw-bg-opacity: 1;\n background-color: rgb(96 165 250 / var(--tw-bg-opacity, 1));\n}\n.bg-blue-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(239 246 255 / var(--tw-bg-opacity, 1));\n}\n.bg-blue-500 {\n --tw-bg-opacity: 1;\n background-color: rgb(59 130 246 / var(--tw-bg-opacity, 1));\n}\n.bg-destructive {\n background-color: hsl(var(--destructive));\n}\n.bg-gray-100 {\n --tw-bg-opacity: 1;\n background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-200 {\n --tw-bg-opacity: 1;\n background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));\n}\n.bg-gray-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(249 250 251 / var(--tw-bg-opacity, 1));\n}\n.bg-green-50 {\n --tw-bg-opacity: 1;\n background-color: rgb(240 253 244 / var(--tw-bg-opacity, 1));\n}\n.bg-primary {\n background-color: hsl(var(--primary));\n}\n.bg-secondary {\n background-color: hsl(var(--secondary));\n}\n.bg-white {\n --tw-bg-opacity: 1;\n background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1));\n}\n.bg-gradient-to-br {\n background-image: linear-gradient(to bottom right, var(--tw-gradient-stops));\n}\n.bg-gradient-to-r {\n background-image: linear-gradient(to right, var(--tw-gradient-stops));\n}\n.from-blue-100 {\n --tw-gradient-from: #dbeafe var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(219 234 254 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n}\n.from-blue-400 {\n --tw-gradient-from: #60a5fa var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(96 165 250 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n}\n.from-blue-50 {\n --tw-gradient-from: #eff6ff var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(239 246 255 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n}\n.via-red-400 {\n --tw-gradient-to: rgb(248 113 113 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops:\n var(--tw-gradient-from),\n #f87171 var(--tw-gradient-via-position),\n var(--tw-gradient-to);\n}\n.to-green-400 {\n --tw-gradient-to: #4ade80 var(--tw-gradient-to-position);\n}\n.to-purple-100 {\n --tw-gradient-to: #f3e8ff var(--tw-gradient-to-position);\n}\n.to-purple-50 {\n --tw-gradient-to: #faf5ff var(--tw-gradient-to-position);\n}\n.p-0\\.5 {\n padding: 0.125rem;\n}\n.p-1 {\n padding: 0.25rem;\n}\n.p-2 {\n padding: 0.5rem;\n}\n.p-3 {\n padding: 0.75rem;\n}\n.p-4 {\n padding: 1rem;\n}\n.p-6 {\n padding: 1.5rem;\n}\n.p-8 {\n padding: 2rem;\n}\n.px-2 {\n padding-left: 0.5rem;\n padding-right: 0.5rem;\n}\n.px-3 {\n padding-left: 0.75rem;\n padding-right: 0.75rem;\n}\n.px-4 {\n padding-left: 1rem;\n padding-right: 1rem;\n}\n.px-6 {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n}\n.px-8 {\n padding-left: 2rem;\n padding-right: 2rem;\n}\n.py-1 {\n padding-top: 0.25rem;\n padding-bottom: 0.25rem;\n}\n.py-1\\.5 {\n padding-top: 0.375rem;\n padding-bottom: 0.375rem;\n}\n.py-2 {\n padding-top: 0.5rem;\n padding-bottom: 0.5rem;\n}\n.py-3 {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n}\n.pt-4 {\n padding-top: 1rem;\n}\n.text-left {\n text-align: left;\n}\n.text-center {\n text-align: center;\n}\n.text-2xl {\n font-size: 1.5rem;\n line-height: 2rem;\n}\n.text-3xl {\n font-size: 1.875rem;\n line-height: 2.25rem;\n}\n.text-lg {\n font-size: 1.125rem;\n line-height: 1.75rem;\n}\n.text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem;\n}\n.text-xs {\n font-size: 0.75rem;\n line-height: 1rem;\n}\n.font-bold {\n font-weight: 700;\n}\n.font-medium {\n font-weight: 500;\n}\n.font-semibold {\n font-weight: 600;\n}\n.leading-tight {\n line-height: 1.25;\n}\n.text-blue-600 {\n --tw-text-opacity: 1;\n color: rgb(37 99 235 / var(--tw-text-opacity, 1));\n}\n.text-blue-700 {\n --tw-text-opacity: 1;\n color: rgb(29 78 216 / var(--tw-text-opacity, 1));\n}\n.text-destructive-foreground {\n color: hsl(var(--destructive-foreground));\n}\n.text-gray-400 {\n --tw-text-opacity: 1;\n color: rgb(156 163 175 / var(--tw-text-opacity, 1));\n}\n.text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(107 114 128 / var(--tw-text-opacity, 1));\n}\n.text-gray-600 {\n --tw-text-opacity: 1;\n color: rgb(75 85 99 / var(--tw-text-opacity, 1));\n}\n.text-gray-700 {\n --tw-text-opacity: 1;\n color: rgb(55 65 81 / var(--tw-text-opacity, 1));\n}\n.text-gray-800 {\n --tw-text-opacity: 1;\n color: rgb(31 41 55 / var(--tw-text-opacity, 1));\n}\n.text-gray-900 {\n --tw-text-opacity: 1;\n color: rgb(17 24 39 / var(--tw-text-opacity, 1));\n}\n.text-green-700 {\n --tw-text-opacity: 1;\n color: rgb(21 128 61 / var(--tw-text-opacity, 1));\n}\n.text-green-900 {\n --tw-text-opacity: 1;\n color: rgb(20 83 45 / var(--tw-text-opacity, 1));\n}\n.text-primary {\n color: hsl(var(--primary));\n}\n.text-primary-foreground {\n color: hsl(var(--primary-foreground));\n}\n.text-red-600 {\n --tw-text-opacity: 1;\n color: rgb(220 38 38 / var(--tw-text-opacity, 1));\n}\n.text-secondary-foreground {\n color: hsl(var(--secondary-foreground));\n}\n.text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1));\n}\n.underline-offset-4 {\n text-underline-offset: 4px;\n}\n.opacity-15 {\n opacity: 0.15;\n}\n.opacity-20 {\n opacity: 0.2;\n}\n.opacity-30 {\n opacity: 0.3;\n}\n.opacity-50 {\n opacity: 0.5;\n}\n.opacity-80 {\n opacity: 0.8;\n}\n.shadow-lg {\n --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.shadow-sm {\n --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.outline {\n outline-style: solid;\n}\n.ring-1 {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.ring-black {\n --tw-ring-opacity: 1;\n --tw-ring-color: rgb(0 0 0 / var(--tw-ring-opacity, 1));\n}\n.ring-opacity-5 {\n --tw-ring-opacity: 0.05;\n}\n.ring-offset-background {\n --tw-ring-offset-color: hsl(var(--background));\n}\n.filter {\n filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);\n}\n.transition-all {\n transition-property: all;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.transition-colors {\n transition-property:\n color,\n background-color,\n border-color,\n text-decoration-color,\n fill,\n stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms;\n}\n.duration-200 {\n transition-duration: 200ms;\n}\n.duration-300 {\n transition-duration: 300ms;\n}\n.duration-500 {\n transition-duration: 500ms;\n}\n@theme { --color-background: hsl(var(--background)); --color-foreground: hsl(var(--foreground)); --color-card: hsl(var(--card)); --color-card-foreground: hsl(var(--card-foreground)); --color-popover: hsl(var(--popover)); --color-popover-foreground: hsl(var(--popover-foreground)); --color-primary: hsl(var(--primary)); --color-primary-foreground: hsl(var(--primary-foreground)); --color-secondary: hsl(var(--secondary)); --color-secondary-foreground: hsl(var(--secondary-foreground)); --color-muted: hsl(var(--muted)); --color-muted-foreground: hsl(var(--muted-foreground)); --color-accent: hsl(var(--accent)); --color-accent-foreground: hsl(var(--accent-foreground)); --color-destructive: hsl(var(--destructive)); --color-destructive-foreground: hsl(var(--destructive-foreground)); --color-border: hsl(var(--border)); --color-input: hsl(var(--input)); --color-ring: hsl(var(--ring)); --radius-lg: var(--radius); --radius-md: calc(var(--radius) - 2px); --radius-sm: calc(var(--radius) - 4px); }\n.hover\\:border-blue-300:hover {\n --tw-border-opacity: 1;\n border-color: rgb(147 197 253 / var(--tw-border-opacity, 1));\n}\n.hover\\:border-red-300:hover {\n --tw-border-opacity: 1;\n border-color: rgb(252 165 165 / var(--tw-border-opacity, 1));\n}\n.hover\\:bg-accent:hover {\n background-color: hsl(var(--accent));\n}\n.hover\\:bg-blue-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(239 246 255 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-destructive\\/90:hover {\n background-color: hsl(var(--destructive) / 0.9);\n}\n.hover\\:bg-gray-100:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(243 244 246 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-gray-200:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(229 231 235 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-primary\\/90:hover {\n background-color: hsl(var(--primary) / 0.9);\n}\n.hover\\:bg-red-50:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(254 242 242 / var(--tw-bg-opacity, 1));\n}\n.hover\\:bg-secondary\\/80:hover {\n background-color: hsl(var(--secondary) / 0.8);\n}\n.hover\\:from-blue-100:hover {\n --tw-gradient-from: #dbeafe var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(219 234 254 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n}\n.hover\\:to-purple-100:hover {\n --tw-gradient-to: #f3e8ff var(--tw-gradient-to-position);\n}\n.hover\\:text-accent-foreground:hover {\n color: hsl(var(--accent-foreground));\n}\n.hover\\:text-gray-600:hover {\n --tw-text-opacity: 1;\n color: rgb(75 85 99 / var(--tw-text-opacity, 1));\n}\n.hover\\:text-red-500:hover {\n --tw-text-opacity: 1;\n color: rgb(239 68 68 / var(--tw-text-opacity, 1));\n}\n.hover\\:text-red-700:hover {\n --tw-text-opacity: 1;\n color: rgb(185 28 28 / var(--tw-text-opacity, 1));\n}\n.hover\\:underline:hover {\n text-decoration-line: underline;\n}\n.hover\\:opacity-80:hover {\n opacity: 0.8;\n}\n.hover\\:shadow-xl:hover {\n --tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);\n --tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);\n box-shadow:\n var(--tw-ring-offset-shadow, 0 0 #0000),\n var(--tw-ring-shadow, 0 0 #0000),\n var(--tw-shadow);\n}\n.focus-visible\\:outline-none:focus-visible {\n outline: 2px solid transparent;\n outline-offset: 2px;\n}\n.focus-visible\\:ring-2:focus-visible {\n --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);\n --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);\n box-shadow:\n var(--tw-ring-offset-shadow),\n var(--tw-ring-shadow),\n var(--tw-shadow, 0 0 #0000);\n}\n.focus-visible\\:ring-ring:focus-visible {\n --tw-ring-color: hsl(var(--ring));\n}\n.focus-visible\\:ring-offset-2:focus-visible {\n --tw-ring-offset-width: 2px;\n}\n.active\\:cursor-grabbing:active {\n cursor: grabbing;\n}\n.disabled\\:pointer-events-none:disabled {\n pointer-events: none;\n}\n.disabled\\:opacity-50:disabled {\n opacity: 0.5;\n}\n.group:hover .group-hover\\:text-blue-700 {\n --tw-text-opacity: 1;\n color: rgb(29 78 216 / var(--tw-text-opacity, 1));\n}\n@media (prefers-color-scheme: dark) {\n .dark\\:block {\n display: block;\n }\n .dark\\:border-blue-600 {\n --tw-border-opacity: 1;\n border-color: rgb(37 99 235 / var(--tw-border-opacity, 1));\n }\n .dark\\:border-blue-700 {\n --tw-border-opacity: 1;\n border-color: rgb(29 78 216 / var(--tw-border-opacity, 1));\n }\n .dark\\:border-gray-600 {\n --tw-border-opacity: 1;\n border-color: rgb(75 85 99 / var(--tw-border-opacity, 1));\n }\n .dark\\:border-gray-700 {\n --tw-border-opacity: 1;\n border-color: rgb(55 65 81 / var(--tw-border-opacity, 1));\n }\n .dark\\:bg-blue-900\\/30 {\n background-color: rgb(30 58 138 / 0.3);\n }\n .dark\\:bg-blue-900\\/50 {\n background-color: rgb(30 58 138 / 0.5);\n }\n .dark\\:bg-gray-600 {\n --tw-bg-opacity: 1;\n background-color: rgb(75 85 99 / var(--tw-bg-opacity, 1));\n }\n .dark\\:bg-gray-700 {\n --tw-bg-opacity: 1;\n background-color: rgb(55 65 81 / var(--tw-bg-opacity, 1));\n }\n .dark\\:bg-gray-800 {\n --tw-bg-opacity: 1;\n background-color: rgb(31 41 55 / var(--tw-bg-opacity, 1));\n }\n .dark\\:bg-gray-900 {\n --tw-bg-opacity: 1;\n background-color: rgb(17 24 39 / var(--tw-bg-opacity, 1));\n }\n .dark\\:from-gray-700 {\n --tw-gradient-from: #374151 var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(55 65 81 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n }\n .dark\\:to-gray-600 {\n --tw-gradient-to: #4b5563 var(--tw-gradient-to-position);\n }\n .dark\\:to-gray-700 {\n --tw-gradient-to: #374151 var(--tw-gradient-to-position);\n }\n .dark\\:text-blue-300 {\n --tw-text-opacity: 1;\n color: rgb(147 197 253 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-blue-400 {\n --tw-text-opacity: 1;\n color: rgb(96 165 250 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-100 {\n --tw-text-opacity: 1;\n color: rgb(243 244 246 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-200 {\n --tw-text-opacity: 1;\n color: rgb(229 231 235 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-300 {\n --tw-text-opacity: 1;\n color: rgb(209 213 219 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-400 {\n --tw-text-opacity: 1;\n color: rgb(156 163 175 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-gray-500 {\n --tw-text-opacity: 1;\n color: rgb(107 114 128 / var(--tw-text-opacity, 1));\n }\n .dark\\:text-red-400 {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n }\n .dark\\:opacity-10 {\n opacity: 0.1;\n }\n .dark\\:opacity-20 {\n opacity: 0.2;\n }\n .dark\\:shadow-gray-900\\/50 {\n --tw-shadow-color: rgb(17 24 39 / 0.5);\n --tw-shadow: var(--tw-shadow-colored);\n }\n .dark\\:hover\\:bg-blue-900\\/50:hover {\n background-color: rgb(30 58 138 / 0.5);\n }\n .dark\\:hover\\:bg-gray-600:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(75 85 99 / var(--tw-bg-opacity, 1));\n }\n .dark\\:hover\\:bg-gray-700:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(55 65 81 / var(--tw-bg-opacity, 1));\n }\n .dark\\:hover\\:bg-red-900\\/20:hover {\n background-color: rgb(127 29 29 / 0.2);\n }\n .dark\\:hover\\:from-gray-600:hover {\n --tw-gradient-from: #4b5563 var(--tw-gradient-from-position);\n --tw-gradient-to: rgb(75 85 99 / 0) var(--tw-gradient-to-position);\n --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to);\n }\n .dark\\:hover\\:to-gray-600:hover {\n --tw-gradient-to: #4b5563 var(--tw-gradient-to-position);\n }\n .dark\\:hover\\:text-gray-300:hover {\n --tw-text-opacity: 1;\n color: rgb(209 213 219 / var(--tw-text-opacity, 1));\n }\n .dark\\:hover\\:text-red-400:hover {\n --tw-text-opacity: 1;\n color: rgb(248 113 113 / var(--tw-text-opacity, 1));\n }\n .group:hover .dark\\:group-hover\\:text-blue-300 {\n --tw-text-opacity: 1;\n color: rgb(147 197 253 / var(--tw-text-opacity, 1));\n }\n}\n.\\[\\&_svg\\]\\:pointer-events-none svg {\n pointer-events: none;\n}\n.\\[\\&_svg\\]\\:size-4 svg {\n width: 1rem;\n height: 1rem;\n}\n.\\[\\&_svg\\]\\:shrink-0 svg {\n flex-shrink: 0;\n}\n');
|
|
1634
|
+
|
|
1635
|
+
// src/index.preact.ts
|
|
1636
|
+
var index_preact_default = Dashboard;
|
|
1637
|
+
export {
|
|
1638
|
+
ANIMATION_DURATION,
|
|
1639
|
+
Button,
|
|
1640
|
+
CELL_SIZE,
|
|
1641
|
+
CONTAINER_PADDING,
|
|
1642
|
+
DEBOUNCE_DELAY,
|
|
1643
|
+
Dashboard,
|
|
1644
|
+
DashboardToolbar,
|
|
1645
|
+
GRID_SIZE,
|
|
1646
|
+
MARGIN,
|
|
1647
|
+
MAX_SIZE,
|
|
1648
|
+
MIN_CONTAINER_HEIGHT,
|
|
1649
|
+
MIN_SIZE,
|
|
1650
|
+
Tooltip,
|
|
1651
|
+
TooltipContent,
|
|
1652
|
+
TooltipProvider,
|
|
1653
|
+
TooltipTrigger,
|
|
1654
|
+
index_preact_default as default
|
|
1655
|
+
};
|
|
1656
|
+
//# sourceMappingURL=index.preact.js.map
|