@signiphi/page-assembly 0.2.0-beta.1
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 +296 -0
- package/dist/index.d.mts +178 -0
- package/dist/index.d.ts +178 -0
- package/dist/index.js +1872 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1837 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles/index.css +1762 -0
- package/package.json +85 -0
- package/src/styles/index.css +109 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1872 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var React7 = require('react');
|
|
6
|
+
var reactDnd = require('react-dnd');
|
|
7
|
+
var reactDndHtml5Backend = require('react-dnd-html5-backend');
|
|
8
|
+
var lucideReact = require('lucide-react');
|
|
9
|
+
var reactSlot = require('@radix-ui/react-slot');
|
|
10
|
+
var classVarianceAuthority = require('class-variance-authority');
|
|
11
|
+
var clsx = require('clsx');
|
|
12
|
+
var tailwindMerge = require('tailwind-merge');
|
|
13
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
14
|
+
var LabelPrimitive = require('@radix-ui/react-label');
|
|
15
|
+
var SelectPrimitive = require('@radix-ui/react-select');
|
|
16
|
+
var AlertDialogPrimitive = require('@radix-ui/react-alert-dialog');
|
|
17
|
+
var DropdownMenuPrimitive = require('@radix-ui/react-dropdown-menu');
|
|
18
|
+
var TooltipPrimitive = require('@radix-ui/react-tooltip');
|
|
19
|
+
var pdfjsLib = require('pdfjs-dist');
|
|
20
|
+
var pdfLib = require('pdf-lib');
|
|
21
|
+
|
|
22
|
+
function _interopNamespace(e) {
|
|
23
|
+
if (e && e.__esModule) return e;
|
|
24
|
+
var n = Object.create(null);
|
|
25
|
+
if (e) {
|
|
26
|
+
Object.keys(e).forEach(function (k) {
|
|
27
|
+
if (k !== 'default') {
|
|
28
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
29
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
get: function () { return e[k]; }
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
n.default = e;
|
|
37
|
+
return Object.freeze(n);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
var React7__namespace = /*#__PURE__*/_interopNamespace(React7);
|
|
41
|
+
var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
|
|
42
|
+
var SelectPrimitive__namespace = /*#__PURE__*/_interopNamespace(SelectPrimitive);
|
|
43
|
+
var AlertDialogPrimitive__namespace = /*#__PURE__*/_interopNamespace(AlertDialogPrimitive);
|
|
44
|
+
var DropdownMenuPrimitive__namespace = /*#__PURE__*/_interopNamespace(DropdownMenuPrimitive);
|
|
45
|
+
var TooltipPrimitive__namespace = /*#__PURE__*/_interopNamespace(TooltipPrimitive);
|
|
46
|
+
var pdfjsLib__namespace = /*#__PURE__*/_interopNamespace(pdfjsLib);
|
|
47
|
+
|
|
48
|
+
// src/components/PageAssembler.tsx
|
|
49
|
+
function cn(...inputs) {
|
|
50
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
51
|
+
}
|
|
52
|
+
var buttonVariants = classVarianceAuthority.cva(
|
|
53
|
+
"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",
|
|
54
|
+
{
|
|
55
|
+
variants: {
|
|
56
|
+
variant: {
|
|
57
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
58
|
+
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
59
|
+
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
60
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
61
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
62
|
+
link: "text-primary underline-offset-4 hover:underline"
|
|
63
|
+
},
|
|
64
|
+
size: {
|
|
65
|
+
default: "h-10 px-4 py-2",
|
|
66
|
+
sm: "h-9 rounded-md px-3",
|
|
67
|
+
lg: "h-11 md:h-12 rounded-md px-8",
|
|
68
|
+
icon: "h-10 w-10"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
defaultVariants: {
|
|
72
|
+
variant: "default",
|
|
73
|
+
size: "default"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
var Button = React7__namespace.forwardRef(
|
|
78
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
79
|
+
const Comp = asChild ? reactSlot.Slot : "button";
|
|
80
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
81
|
+
Comp,
|
|
82
|
+
{
|
|
83
|
+
className: cn(buttonVariants({ variant, size }), className),
|
|
84
|
+
ref,
|
|
85
|
+
...props
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
Button.displayName = "Button";
|
|
91
|
+
var Card = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
92
|
+
"div",
|
|
93
|
+
{
|
|
94
|
+
ref,
|
|
95
|
+
className: cn(
|
|
96
|
+
"rounded-lg bg-card text-card-foreground shadow-sm",
|
|
97
|
+
className
|
|
98
|
+
),
|
|
99
|
+
style: {
|
|
100
|
+
border: "1px solid hsl(var(--xpa-border))",
|
|
101
|
+
...props.style || {}
|
|
102
|
+
},
|
|
103
|
+
...props
|
|
104
|
+
}
|
|
105
|
+
));
|
|
106
|
+
Card.displayName = "Card";
|
|
107
|
+
var CardHeader = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
108
|
+
"div",
|
|
109
|
+
{
|
|
110
|
+
ref,
|
|
111
|
+
className: cn("flex flex-col space-y-1.5 p-6", className),
|
|
112
|
+
...props
|
|
113
|
+
}
|
|
114
|
+
));
|
|
115
|
+
CardHeader.displayName = "CardHeader";
|
|
116
|
+
var CardTitle = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
117
|
+
"h3",
|
|
118
|
+
{
|
|
119
|
+
ref,
|
|
120
|
+
className: cn(
|
|
121
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
122
|
+
className
|
|
123
|
+
),
|
|
124
|
+
...props
|
|
125
|
+
}
|
|
126
|
+
));
|
|
127
|
+
CardTitle.displayName = "CardTitle";
|
|
128
|
+
var CardDescription = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
129
|
+
"p",
|
|
130
|
+
{
|
|
131
|
+
ref,
|
|
132
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
133
|
+
...props
|
|
134
|
+
}
|
|
135
|
+
));
|
|
136
|
+
CardDescription.displayName = "CardDescription";
|
|
137
|
+
var CardContent = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
|
|
138
|
+
CardContent.displayName = "CardContent";
|
|
139
|
+
var CardFooter = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
140
|
+
"div",
|
|
141
|
+
{
|
|
142
|
+
ref,
|
|
143
|
+
className: cn("flex items-center p-6 pt-0", className),
|
|
144
|
+
...props
|
|
145
|
+
}
|
|
146
|
+
));
|
|
147
|
+
CardFooter.displayName = "CardFooter";
|
|
148
|
+
var Input = React7__namespace.forwardRef(
|
|
149
|
+
({ className, type, ...props }, ref) => {
|
|
150
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
151
|
+
"input",
|
|
152
|
+
{
|
|
153
|
+
type,
|
|
154
|
+
className: cn(
|
|
155
|
+
"flex h-10 w-full rounded-md border-2 border-input bg-background px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground transition-all duration-200 focus:outline-none focus:border-primary focus:ring-0 focus:shadow-[0_0_0_3px_rgba(0,153,205,0.15)] focus-visible:outline-none focus-visible:border-primary focus-visible:ring-0 focus-visible:shadow-[0_0_0_3px_rgba(0,153,205,0.15)] disabled:cursor-not-allowed disabled:opacity-50",
|
|
156
|
+
className
|
|
157
|
+
),
|
|
158
|
+
ref,
|
|
159
|
+
...props
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
Input.displayName = "Input";
|
|
165
|
+
var labelVariants = classVarianceAuthority.cva(
|
|
166
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
|
167
|
+
);
|
|
168
|
+
var Label = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
169
|
+
LabelPrimitive__namespace.Root,
|
|
170
|
+
{
|
|
171
|
+
ref,
|
|
172
|
+
className: cn(labelVariants(), className),
|
|
173
|
+
...props
|
|
174
|
+
}
|
|
175
|
+
));
|
|
176
|
+
Label.displayName = LabelPrimitive__namespace.Root.displayName;
|
|
177
|
+
var badgeVariants = classVarianceAuthority.cva(
|
|
178
|
+
"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
179
|
+
{
|
|
180
|
+
variants: {
|
|
181
|
+
variant: {
|
|
182
|
+
default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
|
|
183
|
+
secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
184
|
+
destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
|
185
|
+
outline: "text-foreground"
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
defaultVariants: {
|
|
189
|
+
variant: "default"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
);
|
|
193
|
+
function Badge({ className, variant, ...props }) {
|
|
194
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
195
|
+
}
|
|
196
|
+
var Select = SelectPrimitive__namespace.Root;
|
|
197
|
+
var SelectValue = SelectPrimitive__namespace.Value;
|
|
198
|
+
var SelectTrigger = React7__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
199
|
+
SelectPrimitive__namespace.Trigger,
|
|
200
|
+
{
|
|
201
|
+
ref,
|
|
202
|
+
className: cn(
|
|
203
|
+
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
204
|
+
className
|
|
205
|
+
),
|
|
206
|
+
...props,
|
|
207
|
+
children: [
|
|
208
|
+
children,
|
|
209
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4 opacity-50" }) })
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
));
|
|
213
|
+
SelectTrigger.displayName = SelectPrimitive__namespace.Trigger.displayName;
|
|
214
|
+
var SelectScrollUpButton = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
215
|
+
SelectPrimitive__namespace.ScrollUpButton,
|
|
216
|
+
{
|
|
217
|
+
ref,
|
|
218
|
+
className: cn(
|
|
219
|
+
"flex cursor-default items-center justify-center py-1",
|
|
220
|
+
className
|
|
221
|
+
),
|
|
222
|
+
...props,
|
|
223
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "h-4 w-4" })
|
|
224
|
+
}
|
|
225
|
+
));
|
|
226
|
+
SelectScrollUpButton.displayName = SelectPrimitive__namespace.ScrollUpButton.displayName;
|
|
227
|
+
var SelectScrollDownButton = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
228
|
+
SelectPrimitive__namespace.ScrollDownButton,
|
|
229
|
+
{
|
|
230
|
+
ref,
|
|
231
|
+
className: cn(
|
|
232
|
+
"flex cursor-default items-center justify-center py-1",
|
|
233
|
+
className
|
|
234
|
+
),
|
|
235
|
+
...props,
|
|
236
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-4 w-4" })
|
|
237
|
+
}
|
|
238
|
+
));
|
|
239
|
+
SelectScrollDownButton.displayName = SelectPrimitive__namespace.ScrollDownButton.displayName;
|
|
240
|
+
var SelectContent = React7__namespace.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
241
|
+
SelectPrimitive__namespace.Content,
|
|
242
|
+
{
|
|
243
|
+
ref,
|
|
244
|
+
className: cn(
|
|
245
|
+
"relative max-h-96 min-w-[12rem] overflow-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
246
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
247
|
+
className
|
|
248
|
+
),
|
|
249
|
+
position,
|
|
250
|
+
style: {
|
|
251
|
+
zIndex: 99999,
|
|
252
|
+
backgroundColor: "#ffffff",
|
|
253
|
+
border: "1px solid #e2e8f0",
|
|
254
|
+
borderRadius: "0.5rem",
|
|
255
|
+
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
|
|
256
|
+
marginTop: "4px"
|
|
257
|
+
},
|
|
258
|
+
...props,
|
|
259
|
+
children: [
|
|
260
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
|
|
261
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
262
|
+
SelectPrimitive__namespace.Viewport,
|
|
263
|
+
{
|
|
264
|
+
className: cn(
|
|
265
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
266
|
+
),
|
|
267
|
+
style: { padding: "6px" },
|
|
268
|
+
children
|
|
269
|
+
}
|
|
270
|
+
),
|
|
271
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
|
|
272
|
+
]
|
|
273
|
+
}
|
|
274
|
+
) }));
|
|
275
|
+
SelectContent.displayName = SelectPrimitive__namespace.Content.displayName;
|
|
276
|
+
var SelectLabel = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
277
|
+
SelectPrimitive__namespace.Label,
|
|
278
|
+
{
|
|
279
|
+
ref,
|
|
280
|
+
className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
|
|
281
|
+
...props
|
|
282
|
+
}
|
|
283
|
+
));
|
|
284
|
+
SelectLabel.displayName = SelectPrimitive__namespace.Label.displayName;
|
|
285
|
+
var SelectItem = React7__namespace.forwardRef(({ className, children, ...props }, ref) => {
|
|
286
|
+
const [isHovered, setIsHovered] = React7__namespace.useState(false);
|
|
287
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
288
|
+
SelectPrimitive__namespace.Item,
|
|
289
|
+
{
|
|
290
|
+
ref,
|
|
291
|
+
className: cn(
|
|
292
|
+
"relative flex w-full cursor-pointer select-none items-center outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
293
|
+
className
|
|
294
|
+
),
|
|
295
|
+
style: {
|
|
296
|
+
backgroundColor: isHovered ? "#f1f5f9" : "transparent",
|
|
297
|
+
color: "#1e293b",
|
|
298
|
+
padding: "8px 12px",
|
|
299
|
+
borderRadius: "6px",
|
|
300
|
+
fontSize: "14px",
|
|
301
|
+
marginBottom: "2px"
|
|
302
|
+
},
|
|
303
|
+
onMouseEnter: () => setIsHovered(true),
|
|
304
|
+
onMouseLeave: () => setIsHovered(false),
|
|
305
|
+
...props,
|
|
306
|
+
children: [
|
|
307
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectPrimitive__namespace.ItemText, { children }),
|
|
308
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
309
|
+
SelectPrimitive__namespace.ItemIndicator,
|
|
310
|
+
{
|
|
311
|
+
style: {
|
|
312
|
+
marginLeft: "auto",
|
|
313
|
+
paddingLeft: "8px",
|
|
314
|
+
color: "#0099cd"
|
|
315
|
+
},
|
|
316
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" })
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
]
|
|
320
|
+
}
|
|
321
|
+
);
|
|
322
|
+
});
|
|
323
|
+
SelectItem.displayName = SelectPrimitive__namespace.Item.displayName;
|
|
324
|
+
var SelectSeparator = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
325
|
+
SelectPrimitive__namespace.Separator,
|
|
326
|
+
{
|
|
327
|
+
ref,
|
|
328
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
329
|
+
...props
|
|
330
|
+
}
|
|
331
|
+
));
|
|
332
|
+
SelectSeparator.displayName = SelectPrimitive__namespace.Separator.displayName;
|
|
333
|
+
var AlertDialog = AlertDialogPrimitive__namespace.Root;
|
|
334
|
+
var AlertDialogPortal = AlertDialogPrimitive__namespace.Portal;
|
|
335
|
+
var AlertDialogOverlay = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
336
|
+
AlertDialogPrimitive__namespace.Overlay,
|
|
337
|
+
{
|
|
338
|
+
className: cn(
|
|
339
|
+
"fixed inset-0 z-[9998] bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
340
|
+
className
|
|
341
|
+
),
|
|
342
|
+
...props,
|
|
343
|
+
ref
|
|
344
|
+
}
|
|
345
|
+
));
|
|
346
|
+
AlertDialogOverlay.displayName = AlertDialogPrimitive__namespace.Overlay.displayName;
|
|
347
|
+
var AlertDialogContent = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogPortal, { container: document.querySelector(".signiphi-page-assembly"), children: [
|
|
348
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogOverlay, {}),
|
|
349
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
350
|
+
AlertDialogPrimitive__namespace.Content,
|
|
351
|
+
{
|
|
352
|
+
ref,
|
|
353
|
+
className: cn(
|
|
354
|
+
"fixed left-[50%] top-[50%] z-[9999] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
|
355
|
+
className
|
|
356
|
+
),
|
|
357
|
+
...props
|
|
358
|
+
}
|
|
359
|
+
)
|
|
360
|
+
] }));
|
|
361
|
+
AlertDialogContent.displayName = AlertDialogPrimitive__namespace.Content.displayName;
|
|
362
|
+
var AlertDialogHeader = ({
|
|
363
|
+
className,
|
|
364
|
+
...props
|
|
365
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
366
|
+
"div",
|
|
367
|
+
{
|
|
368
|
+
className: cn(
|
|
369
|
+
"flex flex-col space-y-2 text-center sm:text-left",
|
|
370
|
+
className
|
|
371
|
+
),
|
|
372
|
+
...props
|
|
373
|
+
}
|
|
374
|
+
);
|
|
375
|
+
AlertDialogHeader.displayName = "AlertDialogHeader";
|
|
376
|
+
var AlertDialogFooter = ({
|
|
377
|
+
className,
|
|
378
|
+
...props
|
|
379
|
+
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
380
|
+
"div",
|
|
381
|
+
{
|
|
382
|
+
className: cn(
|
|
383
|
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
384
|
+
className
|
|
385
|
+
),
|
|
386
|
+
...props
|
|
387
|
+
}
|
|
388
|
+
);
|
|
389
|
+
AlertDialogFooter.displayName = "AlertDialogFooter";
|
|
390
|
+
var AlertDialogTitle = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
391
|
+
AlertDialogPrimitive__namespace.Title,
|
|
392
|
+
{
|
|
393
|
+
ref,
|
|
394
|
+
className: cn("text-lg font-semibold", className),
|
|
395
|
+
...props
|
|
396
|
+
}
|
|
397
|
+
));
|
|
398
|
+
AlertDialogTitle.displayName = AlertDialogPrimitive__namespace.Title.displayName;
|
|
399
|
+
var AlertDialogDescription = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
400
|
+
AlertDialogPrimitive__namespace.Description,
|
|
401
|
+
{
|
|
402
|
+
ref,
|
|
403
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
404
|
+
...props
|
|
405
|
+
}
|
|
406
|
+
));
|
|
407
|
+
AlertDialogDescription.displayName = AlertDialogPrimitive__namespace.Description.displayName;
|
|
408
|
+
var AlertDialogAction = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
409
|
+
AlertDialogPrimitive__namespace.Action,
|
|
410
|
+
{
|
|
411
|
+
ref,
|
|
412
|
+
className: cn(buttonVariants(), className),
|
|
413
|
+
...props
|
|
414
|
+
}
|
|
415
|
+
));
|
|
416
|
+
AlertDialogAction.displayName = AlertDialogPrimitive__namespace.Action.displayName;
|
|
417
|
+
var AlertDialogCancel = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
418
|
+
AlertDialogPrimitive__namespace.Cancel,
|
|
419
|
+
{
|
|
420
|
+
ref,
|
|
421
|
+
className: cn(
|
|
422
|
+
buttonVariants({ variant: "outline" }),
|
|
423
|
+
"mt-2 sm:mt-0",
|
|
424
|
+
className
|
|
425
|
+
),
|
|
426
|
+
...props
|
|
427
|
+
}
|
|
428
|
+
));
|
|
429
|
+
AlertDialogCancel.displayName = AlertDialogPrimitive__namespace.Cancel.displayName;
|
|
430
|
+
var DropdownMenu = DropdownMenuPrimitive__namespace.Root;
|
|
431
|
+
var DropdownMenuTrigger = DropdownMenuPrimitive__namespace.Trigger;
|
|
432
|
+
var DropdownMenuSubTrigger = React7__namespace.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
433
|
+
DropdownMenuPrimitive__namespace.SubTrigger,
|
|
434
|
+
{
|
|
435
|
+
ref,
|
|
436
|
+
className: cn(
|
|
437
|
+
"flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
438
|
+
inset && "pl-8",
|
|
439
|
+
className
|
|
440
|
+
),
|
|
441
|
+
...props,
|
|
442
|
+
children: [
|
|
443
|
+
children,
|
|
444
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "ml-auto" })
|
|
445
|
+
]
|
|
446
|
+
}
|
|
447
|
+
));
|
|
448
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive__namespace.SubTrigger.displayName;
|
|
449
|
+
var DropdownMenuSubContent = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
450
|
+
DropdownMenuPrimitive__namespace.SubContent,
|
|
451
|
+
{
|
|
452
|
+
ref,
|
|
453
|
+
className: cn(
|
|
454
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
455
|
+
className
|
|
456
|
+
),
|
|
457
|
+
...props
|
|
458
|
+
}
|
|
459
|
+
));
|
|
460
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive__namespace.SubContent.displayName;
|
|
461
|
+
var DropdownMenuContent = React7__namespace.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
462
|
+
DropdownMenuPrimitive__namespace.Content,
|
|
463
|
+
{
|
|
464
|
+
ref,
|
|
465
|
+
sideOffset,
|
|
466
|
+
className: cn(
|
|
467
|
+
"min-w-[12rem] overflow-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
468
|
+
className
|
|
469
|
+
),
|
|
470
|
+
style: {
|
|
471
|
+
zIndex: 99999,
|
|
472
|
+
backgroundColor: "#ffffff",
|
|
473
|
+
border: "1px solid #e2e8f0",
|
|
474
|
+
borderRadius: "0.5rem",
|
|
475
|
+
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
|
|
476
|
+
padding: "6px"
|
|
477
|
+
},
|
|
478
|
+
...props
|
|
479
|
+
}
|
|
480
|
+
) }));
|
|
481
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive__namespace.Content.displayName;
|
|
482
|
+
var DropdownMenuItem = React7__namespace.forwardRef(({ className, inset, ...props }, ref) => {
|
|
483
|
+
const [isHovered, setIsHovered] = React7__namespace.useState(false);
|
|
484
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
485
|
+
DropdownMenuPrimitive__namespace.Item,
|
|
486
|
+
{
|
|
487
|
+
ref,
|
|
488
|
+
className: cn(
|
|
489
|
+
"relative flex cursor-pointer select-none items-center gap-2 outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
490
|
+
inset && "pl-8",
|
|
491
|
+
className
|
|
492
|
+
),
|
|
493
|
+
style: {
|
|
494
|
+
backgroundColor: isHovered ? "#f1f5f9" : "transparent",
|
|
495
|
+
color: "#1e293b",
|
|
496
|
+
padding: "8px 12px",
|
|
497
|
+
borderRadius: "6px",
|
|
498
|
+
fontSize: "14px",
|
|
499
|
+
marginBottom: "2px"
|
|
500
|
+
},
|
|
501
|
+
onMouseEnter: () => setIsHovered(true),
|
|
502
|
+
onMouseLeave: () => setIsHovered(false),
|
|
503
|
+
...props
|
|
504
|
+
}
|
|
505
|
+
);
|
|
506
|
+
});
|
|
507
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive__namespace.Item.displayName;
|
|
508
|
+
var DropdownMenuCheckboxItem = React7__namespace.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
509
|
+
DropdownMenuPrimitive__namespace.CheckboxItem,
|
|
510
|
+
{
|
|
511
|
+
ref,
|
|
512
|
+
className: cn(
|
|
513
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
514
|
+
className
|
|
515
|
+
),
|
|
516
|
+
checked,
|
|
517
|
+
...props,
|
|
518
|
+
children: [
|
|
519
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-4 w-4" }) }) }),
|
|
520
|
+
children
|
|
521
|
+
]
|
|
522
|
+
}
|
|
523
|
+
));
|
|
524
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive__namespace.CheckboxItem.displayName;
|
|
525
|
+
var DropdownMenuRadioItem = React7__namespace.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
526
|
+
DropdownMenuPrimitive__namespace.RadioItem,
|
|
527
|
+
{
|
|
528
|
+
ref,
|
|
529
|
+
className: cn(
|
|
530
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
531
|
+
className
|
|
532
|
+
),
|
|
533
|
+
...props,
|
|
534
|
+
children: [
|
|
535
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuPrimitive__namespace.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Circle, { className: "h-2 w-2 fill-current" }) }) }),
|
|
536
|
+
children
|
|
537
|
+
]
|
|
538
|
+
}
|
|
539
|
+
));
|
|
540
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive__namespace.RadioItem.displayName;
|
|
541
|
+
var DropdownMenuLabel = React7__namespace.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
542
|
+
DropdownMenuPrimitive__namespace.Label,
|
|
543
|
+
{
|
|
544
|
+
ref,
|
|
545
|
+
className: cn(
|
|
546
|
+
"px-2 py-1.5 text-sm font-semibold",
|
|
547
|
+
inset && "pl-8",
|
|
548
|
+
className
|
|
549
|
+
),
|
|
550
|
+
...props
|
|
551
|
+
}
|
|
552
|
+
));
|
|
553
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive__namespace.Label.displayName;
|
|
554
|
+
var DropdownMenuSeparator = React7__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
555
|
+
DropdownMenuPrimitive__namespace.Separator,
|
|
556
|
+
{
|
|
557
|
+
ref,
|
|
558
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
559
|
+
...props
|
|
560
|
+
}
|
|
561
|
+
));
|
|
562
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive__namespace.Separator.displayName;
|
|
563
|
+
var TooltipProvider = TooltipPrimitive__namespace.Provider;
|
|
564
|
+
var Tooltip = TooltipPrimitive__namespace.Root;
|
|
565
|
+
var TooltipTrigger = TooltipPrimitive__namespace.Trigger;
|
|
566
|
+
var TooltipContent = React7__namespace.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
567
|
+
TooltipPrimitive__namespace.Content,
|
|
568
|
+
{
|
|
569
|
+
ref,
|
|
570
|
+
sideOffset,
|
|
571
|
+
className: cn(
|
|
572
|
+
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
573
|
+
className
|
|
574
|
+
),
|
|
575
|
+
...props
|
|
576
|
+
}
|
|
577
|
+
));
|
|
578
|
+
TooltipContent.displayName = TooltipPrimitive__namespace.Content.displayName;
|
|
579
|
+
if (typeof window !== "undefined") {
|
|
580
|
+
pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = "/pdfjs/build/pdf.worker.mjs";
|
|
581
|
+
}
|
|
582
|
+
async function pdfToImages(pdfBytes, options) {
|
|
583
|
+
try {
|
|
584
|
+
const header = new TextDecoder().decode(pdfBytes.slice(0, 10));
|
|
585
|
+
if (!header.startsWith("%PDF-")) {
|
|
586
|
+
console.error("Invalid PDF: Does not start with %PDF- header");
|
|
587
|
+
throw new Error("Invalid PDF format: Missing PDF header");
|
|
588
|
+
}
|
|
589
|
+
if (typeof window !== "undefined") {
|
|
590
|
+
pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = "/pdfjs/build/pdf.worker.mjs";
|
|
591
|
+
}
|
|
592
|
+
const loadingTask = pdfjsLib__namespace.getDocument({
|
|
593
|
+
data: pdfBytes,
|
|
594
|
+
verbosity: 0
|
|
595
|
+
});
|
|
596
|
+
const pdfDoc = await loadingTask.promise;
|
|
597
|
+
const pages = [];
|
|
598
|
+
let pdfLibDoc = null;
|
|
599
|
+
try {
|
|
600
|
+
pdfLibDoc = await pdfLib.PDFDocument.load(pdfBytes);
|
|
601
|
+
} catch {
|
|
602
|
+
}
|
|
603
|
+
for (let pageNum = 1; pageNum <= pdfDoc.numPages; pageNum++) {
|
|
604
|
+
const page = await pdfDoc.getPage(pageNum);
|
|
605
|
+
const viewport = page.getViewport({ scale: 2 });
|
|
606
|
+
let pdfWidth = viewport.width / 2;
|
|
607
|
+
let pdfHeight = viewport.height / 2;
|
|
608
|
+
if (pdfLibDoc) {
|
|
609
|
+
try {
|
|
610
|
+
const pdfLibPage = pdfLibDoc.getPage(pageNum - 1);
|
|
611
|
+
const pdfLibWidth = pdfLibPage.getWidth();
|
|
612
|
+
const pdfLibHeight = pdfLibPage.getHeight();
|
|
613
|
+
pdfWidth = pdfLibWidth;
|
|
614
|
+
pdfHeight = pdfLibHeight;
|
|
615
|
+
} catch {
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
const canvas = document.createElement("canvas");
|
|
619
|
+
const context = canvas.getContext("2d");
|
|
620
|
+
canvas.height = viewport.height;
|
|
621
|
+
canvas.width = viewport.width;
|
|
622
|
+
const hideFormFields = options?.hideFormFields ?? false;
|
|
623
|
+
const renderContext = {
|
|
624
|
+
canvasContext: context,
|
|
625
|
+
viewport,
|
|
626
|
+
// Hide annotations/form fields when specified
|
|
627
|
+
renderInteractiveForms: !hideFormFields,
|
|
628
|
+
// Additional options to ensure form fields are hidden
|
|
629
|
+
...hideFormFields && {
|
|
630
|
+
annotationMode: 0,
|
|
631
|
+
// Disable annotations
|
|
632
|
+
renderAnnotations: false,
|
|
633
|
+
renderForms: false
|
|
634
|
+
}
|
|
635
|
+
};
|
|
636
|
+
await page.render(renderContext).promise;
|
|
637
|
+
const imageUrl = canvas.toDataURL("image/png");
|
|
638
|
+
pages.push({
|
|
639
|
+
imageUrl,
|
|
640
|
+
width: pdfWidth,
|
|
641
|
+
height: pdfHeight
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
return pages;
|
|
645
|
+
} catch (error) {
|
|
646
|
+
console.error("Error converting PDF to images with PDF.js:", error);
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
async function imageToPdf(imageFile) {
|
|
651
|
+
return new Promise((resolve, reject) => {
|
|
652
|
+
const img = new Image();
|
|
653
|
+
const canvas = document.createElement("canvas");
|
|
654
|
+
const ctx = canvas.getContext("2d");
|
|
655
|
+
img.onload = async () => {
|
|
656
|
+
try {
|
|
657
|
+
canvas.width = img.width;
|
|
658
|
+
canvas.height = img.height;
|
|
659
|
+
ctx.drawImage(img, 0, 0);
|
|
660
|
+
canvas.toBlob(
|
|
661
|
+
async (blob) => {
|
|
662
|
+
if (!blob) {
|
|
663
|
+
reject(new Error("Failed to convert image to blob"));
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
const imageBytes = new Uint8Array(await blob.arrayBuffer());
|
|
667
|
+
const pdfDoc = await pdfLib.PDFDocument.create();
|
|
668
|
+
const embeddedImage = await pdfDoc.embedPng(imageBytes);
|
|
669
|
+
const { width, height } = embeddedImage.scale(1);
|
|
670
|
+
const maxWidth = 595;
|
|
671
|
+
const maxHeight = 842;
|
|
672
|
+
let pageWidth = width;
|
|
673
|
+
let pageHeight = height;
|
|
674
|
+
if (width > maxWidth || height > maxHeight) {
|
|
675
|
+
const scale = Math.min(maxWidth / width, maxHeight / height);
|
|
676
|
+
pageWidth = width * scale;
|
|
677
|
+
pageHeight = height * scale;
|
|
678
|
+
}
|
|
679
|
+
const page = pdfDoc.addPage([
|
|
680
|
+
Math.max(pageWidth, 100),
|
|
681
|
+
Math.max(pageHeight, 100)
|
|
682
|
+
]);
|
|
683
|
+
const x = (page.getWidth() - pageWidth) / 2;
|
|
684
|
+
const y = (page.getHeight() - pageHeight) / 2;
|
|
685
|
+
page.drawImage(embeddedImage, {
|
|
686
|
+
x: Math.max(x, 0),
|
|
687
|
+
y: Math.max(y, 0),
|
|
688
|
+
width: pageWidth,
|
|
689
|
+
height: pageHeight
|
|
690
|
+
});
|
|
691
|
+
const pdfBytes = await pdfDoc.save();
|
|
692
|
+
resolve(pdfBytes);
|
|
693
|
+
},
|
|
694
|
+
"image/png",
|
|
695
|
+
0.95
|
|
696
|
+
);
|
|
697
|
+
} catch (error) {
|
|
698
|
+
reject(error);
|
|
699
|
+
}
|
|
700
|
+
};
|
|
701
|
+
img.onerror = () => {
|
|
702
|
+
reject(new Error("Failed to load image"));
|
|
703
|
+
};
|
|
704
|
+
const objectUrl = URL.createObjectURL(imageFile);
|
|
705
|
+
img.src = objectUrl;
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
function createPdfBlobUrl(pdfBytes) {
|
|
709
|
+
const blob = new Blob([pdfBytes], {
|
|
710
|
+
type: "application/pdf"
|
|
711
|
+
});
|
|
712
|
+
return URL.createObjectURL(blob);
|
|
713
|
+
}
|
|
714
|
+
function downloadPdf(pdfBytes, filename) {
|
|
715
|
+
const blob = new Blob([pdfBytes], {
|
|
716
|
+
type: "application/pdf"
|
|
717
|
+
});
|
|
718
|
+
const url = URL.createObjectURL(blob);
|
|
719
|
+
const a = document.createElement("a");
|
|
720
|
+
a.href = url;
|
|
721
|
+
a.download = filename;
|
|
722
|
+
a.click();
|
|
723
|
+
URL.revokeObjectURL(url);
|
|
724
|
+
}
|
|
725
|
+
var isMultiSelectModifier = (event) => {
|
|
726
|
+
return event.ctrlKey || event.metaKey;
|
|
727
|
+
};
|
|
728
|
+
var isRangeSelectModifier = (event) => {
|
|
729
|
+
return event.shiftKey;
|
|
730
|
+
};
|
|
731
|
+
var handleContextMenu = (event) => {
|
|
732
|
+
if (event.ctrlKey && /Mac|iPod|iPhone|iPad/.test(navigator.userAgent)) {
|
|
733
|
+
event.preventDefault();
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
var getPageRange = (files, startPageId, endPageId) => {
|
|
737
|
+
const allPages = [];
|
|
738
|
+
files.forEach((file) => {
|
|
739
|
+
file.pages.forEach((page) => {
|
|
740
|
+
allPages.push({ id: page.id, fileId: file.id });
|
|
741
|
+
});
|
|
742
|
+
});
|
|
743
|
+
const startIndex = allPages.findIndex((p) => p.id === startPageId);
|
|
744
|
+
const endIndex = allPages.findIndex((p) => p.id === endPageId);
|
|
745
|
+
if (startIndex === -1 || endIndex === -1) return [];
|
|
746
|
+
const start = Math.min(startIndex, endIndex);
|
|
747
|
+
const end = Math.max(startIndex, endIndex);
|
|
748
|
+
return allPages.slice(start, end + 1).map((p) => p.id);
|
|
749
|
+
};
|
|
750
|
+
function assemblerReducer(state, action) {
|
|
751
|
+
switch (action.type) {
|
|
752
|
+
case "ADD_FILE": {
|
|
753
|
+
const existingFileById = state.files.find((f) => f.id === action.payload.id);
|
|
754
|
+
if (existingFileById) return state;
|
|
755
|
+
return {
|
|
756
|
+
...state,
|
|
757
|
+
files: [...state.files, action.payload]
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
case "REMOVE_FILE":
|
|
761
|
+
return {
|
|
762
|
+
...state,
|
|
763
|
+
files: state.files.filter((f) => f.id !== action.payload),
|
|
764
|
+
selection: state.selection.filter(
|
|
765
|
+
(pageId) => !state.files.find((f) => f.id === action.payload)?.pages.some((p) => p.id === pageId)
|
|
766
|
+
)
|
|
767
|
+
};
|
|
768
|
+
case "UPDATE_FILE":
|
|
769
|
+
return {
|
|
770
|
+
...state,
|
|
771
|
+
files: state.files.map(
|
|
772
|
+
(f) => f.id === action.payload.fileId ? { ...f, ...action.payload.updates } : f
|
|
773
|
+
)
|
|
774
|
+
};
|
|
775
|
+
case "REORDER_PAGES":
|
|
776
|
+
return {
|
|
777
|
+
...state,
|
|
778
|
+
files: state.files.map(
|
|
779
|
+
(f) => f.id === action.payload.fileId ? { ...f, pages: action.payload.newOrder } : f
|
|
780
|
+
)
|
|
781
|
+
};
|
|
782
|
+
case "REORDER_FILES":
|
|
783
|
+
return {
|
|
784
|
+
...state,
|
|
785
|
+
files: action.payload.newOrder
|
|
786
|
+
};
|
|
787
|
+
case "MOVE_PAGES": {
|
|
788
|
+
const { pageIds, targetFileId } = action.payload;
|
|
789
|
+
const existingPageIds = state.files.flatMap((f) => f.pages.map((p) => p.id));
|
|
790
|
+
const pagesToActuallyMove = pageIds.filter((id) => existingPageIds.includes(id));
|
|
791
|
+
if (pagesToActuallyMove.length === 0) return state;
|
|
792
|
+
const pagesToMove = [];
|
|
793
|
+
for (const file of state.files) {
|
|
794
|
+
for (const page of file.pages) {
|
|
795
|
+
if (pagesToActuallyMove.includes(page.id)) {
|
|
796
|
+
pagesToMove.push(page);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const updatedFiles = state.files.map((file) => {
|
|
801
|
+
if (file.id === targetFileId) {
|
|
802
|
+
const existingPageIds2 = file.pages.map((p) => p.id);
|
|
803
|
+
const newPages = pagesToMove.filter((p) => !existingPageIds2.includes(p.id));
|
|
804
|
+
return {
|
|
805
|
+
...file,
|
|
806
|
+
pages: [...file.pages, ...newPages]
|
|
807
|
+
};
|
|
808
|
+
} else {
|
|
809
|
+
return {
|
|
810
|
+
...file,
|
|
811
|
+
pages: file.pages.filter((page) => !pagesToActuallyMove.includes(page.id))
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
return {
|
|
816
|
+
...state,
|
|
817
|
+
files: updatedFiles,
|
|
818
|
+
selection: []
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
case "DELETE_PAGES": {
|
|
822
|
+
const pageIdsToDelete = action.payload;
|
|
823
|
+
const existingPageIds = state.files.flatMap((f) => f.pages.map((p) => p.id));
|
|
824
|
+
const pagesToActuallyDelete = pageIdsToDelete.filter((id) => existingPageIds.includes(id));
|
|
825
|
+
if (pagesToActuallyDelete.length === 0) return state;
|
|
826
|
+
const updatedFiles = state.files.map((file) => {
|
|
827
|
+
const remainingPages = file.pages.filter((page) => !pagesToActuallyDelete.includes(page.id));
|
|
828
|
+
return {
|
|
829
|
+
...file,
|
|
830
|
+
pages: remainingPages
|
|
831
|
+
};
|
|
832
|
+
}).filter((file) => file.pages.length > 0);
|
|
833
|
+
return {
|
|
834
|
+
...state,
|
|
835
|
+
files: updatedFiles,
|
|
836
|
+
selection: state.selection.filter((id) => !pagesToActuallyDelete.includes(id))
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
case "SET_SELECTION":
|
|
840
|
+
return {
|
|
841
|
+
...state,
|
|
842
|
+
selection: action.payload
|
|
843
|
+
};
|
|
844
|
+
case "TOGGLE_PAGE_SELECTION": {
|
|
845
|
+
const { pageId } = action.payload;
|
|
846
|
+
const isSelected = state.selection.includes(pageId);
|
|
847
|
+
if (isSelected) {
|
|
848
|
+
return {
|
|
849
|
+
...state,
|
|
850
|
+
selection: state.selection.filter((id) => id !== pageId),
|
|
851
|
+
lastSelectedPageId: pageId
|
|
852
|
+
};
|
|
853
|
+
} else {
|
|
854
|
+
return {
|
|
855
|
+
...state,
|
|
856
|
+
selection: [...state.selection, pageId],
|
|
857
|
+
lastSelectedPageId: pageId
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
case "RANGE_SELECT_PAGES": {
|
|
862
|
+
const { pageId, files } = action.payload;
|
|
863
|
+
if (!state.lastSelectedPageId) {
|
|
864
|
+
return {
|
|
865
|
+
...state,
|
|
866
|
+
selection: [pageId],
|
|
867
|
+
lastSelectedPageId: pageId
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
const rangePageIds = getPageRange(files, state.lastSelectedPageId, pageId);
|
|
871
|
+
const newSelection = [.../* @__PURE__ */ new Set([...state.selection, ...rangePageIds])];
|
|
872
|
+
return {
|
|
873
|
+
...state,
|
|
874
|
+
selection: newSelection,
|
|
875
|
+
lastSelectedPageId: pageId
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
case "SINGLE_SELECT_PAGE": {
|
|
879
|
+
return {
|
|
880
|
+
...state,
|
|
881
|
+
selection: [action.payload],
|
|
882
|
+
lastSelectedPageId: action.payload
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
case "CLEAR_SELECTION":
|
|
886
|
+
return {
|
|
887
|
+
...state,
|
|
888
|
+
selection: [],
|
|
889
|
+
lastSelectedPageId: void 0
|
|
890
|
+
};
|
|
891
|
+
case "RENAME_FILE":
|
|
892
|
+
return {
|
|
893
|
+
...state,
|
|
894
|
+
files: state.files.map(
|
|
895
|
+
(f) => f.id === action.payload.fileId ? { ...f, name: action.payload.name } : f
|
|
896
|
+
)
|
|
897
|
+
};
|
|
898
|
+
case "RESET":
|
|
899
|
+
return action.payload || { files: [], selection: [], lastSelectedPageId: void 0 };
|
|
900
|
+
default:
|
|
901
|
+
return state;
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
var DND_TYPES = {
|
|
905
|
+
PAGE: "page",
|
|
906
|
+
FILE: "file"
|
|
907
|
+
};
|
|
908
|
+
var DraggablePageThumbnail = ({
|
|
909
|
+
page,
|
|
910
|
+
fileId,
|
|
911
|
+
index,
|
|
912
|
+
isSelected,
|
|
913
|
+
onSingleSelect,
|
|
914
|
+
onToggleSelect,
|
|
915
|
+
onRangeSelect,
|
|
916
|
+
movePageInFile,
|
|
917
|
+
isDraggingPageRef
|
|
918
|
+
}) => {
|
|
919
|
+
const ref = React7.useRef(null);
|
|
920
|
+
const [{ isDragging }, drag] = reactDnd.useDrag({
|
|
921
|
+
type: DND_TYPES.PAGE,
|
|
922
|
+
item: () => {
|
|
923
|
+
if (isDraggingPageRef?.current !== void 0) {
|
|
924
|
+
isDraggingPageRef.current = true;
|
|
925
|
+
}
|
|
926
|
+
return { pageId: page.id, fileId, index };
|
|
927
|
+
},
|
|
928
|
+
collect: (monitor) => ({
|
|
929
|
+
isDragging: monitor.isDragging()
|
|
930
|
+
}),
|
|
931
|
+
end: () => {
|
|
932
|
+
if (isDraggingPageRef?.current !== void 0) {
|
|
933
|
+
isDraggingPageRef.current = false;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
const [{ handlerId }, drop] = reactDnd.useDrop({
|
|
938
|
+
accept: DND_TYPES.PAGE,
|
|
939
|
+
collect(monitor) {
|
|
940
|
+
return {
|
|
941
|
+
handlerId: monitor.getHandlerId()
|
|
942
|
+
};
|
|
943
|
+
},
|
|
944
|
+
hover(item, monitor) {
|
|
945
|
+
if (!ref.current) return;
|
|
946
|
+
if (item.fileId !== fileId) return;
|
|
947
|
+
const dragIndex = item.index;
|
|
948
|
+
const hoverIndex = index;
|
|
949
|
+
if (dragIndex === hoverIndex) return;
|
|
950
|
+
const hoverBoundingRect = ref.current?.getBoundingClientRect();
|
|
951
|
+
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
|
|
952
|
+
const clientOffset = monitor.getClientOffset();
|
|
953
|
+
const hoverClientY = clientOffset.y - hoverBoundingRect.top;
|
|
954
|
+
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) return;
|
|
955
|
+
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) return;
|
|
956
|
+
movePageInFile(dragIndex, hoverIndex);
|
|
957
|
+
item.index = hoverIndex;
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
React7.useEffect(() => {
|
|
961
|
+
drag(drop(ref));
|
|
962
|
+
}, [drag, drop]);
|
|
963
|
+
const handleClick = (e) => {
|
|
964
|
+
e.preventDefault();
|
|
965
|
+
handleContextMenu(e);
|
|
966
|
+
if (isRangeSelectModifier(e)) {
|
|
967
|
+
onRangeSelect(page.id);
|
|
968
|
+
} else if (isMultiSelectModifier(e)) {
|
|
969
|
+
onToggleSelect(page.id);
|
|
970
|
+
} else {
|
|
971
|
+
onSingleSelect(page.id);
|
|
972
|
+
}
|
|
973
|
+
};
|
|
974
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
975
|
+
"div",
|
|
976
|
+
{
|
|
977
|
+
ref,
|
|
978
|
+
"data-handler-id": handlerId,
|
|
979
|
+
className: cn(
|
|
980
|
+
"relative group cursor-pointer border-2 rounded-lg overflow-hidden bg-white transition-all",
|
|
981
|
+
isDragging && "opacity-50 scale-95",
|
|
982
|
+
isSelected ? "border-primary shadow-md" : "border-gray-200 hover:border-gray-300"
|
|
983
|
+
),
|
|
984
|
+
onClick: handleClick,
|
|
985
|
+
children: [
|
|
986
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity bg-black/50 rounded p-1 z-10", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVertical, { className: "h-3 w-3 text-white" }) }),
|
|
987
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
988
|
+
"div",
|
|
989
|
+
{
|
|
990
|
+
className: "absolute top-2 left-2 z-10",
|
|
991
|
+
onClick: (e) => e.stopPropagation(),
|
|
992
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
993
|
+
"input",
|
|
994
|
+
{
|
|
995
|
+
type: "checkbox",
|
|
996
|
+
checked: isSelected,
|
|
997
|
+
onChange: (e) => {
|
|
998
|
+
const nativeEvent = e.nativeEvent;
|
|
999
|
+
if (nativeEvent && "shiftKey" in nativeEvent && nativeEvent.shiftKey) {
|
|
1000
|
+
onRangeSelect(page.id);
|
|
1001
|
+
} else if (nativeEvent && ("ctrlKey" in nativeEvent && nativeEvent.ctrlKey || "metaKey" in nativeEvent && nativeEvent.metaKey)) {
|
|
1002
|
+
onToggleSelect(page.id);
|
|
1003
|
+
} else {
|
|
1004
|
+
onSingleSelect(page.id);
|
|
1005
|
+
}
|
|
1006
|
+
},
|
|
1007
|
+
onContextMenu: handleContextMenu,
|
|
1008
|
+
className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary focus:ring-2 bg-white/90 cursor-pointer"
|
|
1009
|
+
}
|
|
1010
|
+
)
|
|
1011
|
+
}
|
|
1012
|
+
),
|
|
1013
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aspect-[3/4] relative", children: [
|
|
1014
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1015
|
+
"img",
|
|
1016
|
+
{
|
|
1017
|
+
src: page.thumbUrl,
|
|
1018
|
+
alt: `Page ${index + 1}`,
|
|
1019
|
+
className: "w-full h-full object-cover"
|
|
1020
|
+
}
|
|
1021
|
+
),
|
|
1022
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1023
|
+
Badge,
|
|
1024
|
+
{
|
|
1025
|
+
variant: "secondary",
|
|
1026
|
+
className: "absolute bottom-1 right-1 text-xs px-1 py-0",
|
|
1027
|
+
children: index + 1
|
|
1028
|
+
}
|
|
1029
|
+
)
|
|
1030
|
+
] })
|
|
1031
|
+
]
|
|
1032
|
+
}
|
|
1033
|
+
);
|
|
1034
|
+
};
|
|
1035
|
+
var ReorderablePageGrid = ({
|
|
1036
|
+
file,
|
|
1037
|
+
selectedPageIds,
|
|
1038
|
+
onSingleSelectPage,
|
|
1039
|
+
onTogglePageSelect,
|
|
1040
|
+
onRangeSelectPage,
|
|
1041
|
+
onReorderPages,
|
|
1042
|
+
isDraggingPageRef
|
|
1043
|
+
}) => {
|
|
1044
|
+
const movePageInFile = React7.useCallback((dragIndex, hoverIndex) => {
|
|
1045
|
+
const newPages = [...file.pages];
|
|
1046
|
+
const draggedPage = newPages[dragIndex];
|
|
1047
|
+
newPages.splice(dragIndex, 1);
|
|
1048
|
+
newPages.splice(hoverIndex, 0, draggedPage);
|
|
1049
|
+
onReorderPages(file.id, newPages);
|
|
1050
|
+
}, [file.id, file.pages, onReorderPages]);
|
|
1051
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4", children: file.pages.map((page, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1052
|
+
DraggablePageThumbnail,
|
|
1053
|
+
{
|
|
1054
|
+
page,
|
|
1055
|
+
fileId: file.id,
|
|
1056
|
+
index,
|
|
1057
|
+
isSelected: selectedPageIds.includes(page.id),
|
|
1058
|
+
onSingleSelect: onSingleSelectPage,
|
|
1059
|
+
onToggleSelect: onTogglePageSelect,
|
|
1060
|
+
onRangeSelect: onRangeSelectPage,
|
|
1061
|
+
movePageInFile,
|
|
1062
|
+
isDraggingPageRef
|
|
1063
|
+
},
|
|
1064
|
+
page.id
|
|
1065
|
+
)) });
|
|
1066
|
+
};
|
|
1067
|
+
var FileDropZone = ({
|
|
1068
|
+
targetIndex,
|
|
1069
|
+
onReorderFiles,
|
|
1070
|
+
files
|
|
1071
|
+
}) => {
|
|
1072
|
+
const [{ isOver, canDrop }, drop] = reactDnd.useDrop({
|
|
1073
|
+
accept: DND_TYPES.FILE,
|
|
1074
|
+
drop: (item) => {
|
|
1075
|
+
const currentIndex = files.findIndex((f) => f.id === item.fileId);
|
|
1076
|
+
if (currentIndex === -1) return;
|
|
1077
|
+
let newIndex = targetIndex;
|
|
1078
|
+
if (currentIndex < targetIndex) {
|
|
1079
|
+
newIndex = targetIndex - 1;
|
|
1080
|
+
}
|
|
1081
|
+
if (currentIndex === newIndex) return;
|
|
1082
|
+
const newFiles = [...files];
|
|
1083
|
+
const [movedFile] = newFiles.splice(currentIndex, 1);
|
|
1084
|
+
newFiles.splice(newIndex, 0, movedFile);
|
|
1085
|
+
onReorderFiles(newFiles);
|
|
1086
|
+
},
|
|
1087
|
+
collect: (monitor) => ({
|
|
1088
|
+
isOver: monitor.isOver(),
|
|
1089
|
+
canDrop: monitor.canDrop()
|
|
1090
|
+
})
|
|
1091
|
+
});
|
|
1092
|
+
const isActive = isOver && canDrop;
|
|
1093
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1094
|
+
"div",
|
|
1095
|
+
{
|
|
1096
|
+
ref: drop,
|
|
1097
|
+
className: cn(
|
|
1098
|
+
"h-2 transition-all duration-200 mx-4",
|
|
1099
|
+
isActive ? "h-8 bg-primary/20 border-2 border-dashed border-primary rounded-lg" : ""
|
|
1100
|
+
),
|
|
1101
|
+
children: isActive && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full text-primary text-sm font-medium", children: "Drop here to reorder" })
|
|
1102
|
+
}
|
|
1103
|
+
);
|
|
1104
|
+
};
|
|
1105
|
+
var FileCard = ({
|
|
1106
|
+
file,
|
|
1107
|
+
selectedPageIds,
|
|
1108
|
+
onSingleSelectPage,
|
|
1109
|
+
onTogglePageSelect,
|
|
1110
|
+
onRangeSelectPage,
|
|
1111
|
+
onSelectAllInFile,
|
|
1112
|
+
onRemoveFile,
|
|
1113
|
+
onRenameFile,
|
|
1114
|
+
onReorderPages,
|
|
1115
|
+
isDraggingPageRef
|
|
1116
|
+
}) => {
|
|
1117
|
+
const [isRenaming, setIsRenaming] = React7.useState(false);
|
|
1118
|
+
const [newName, setNewName] = React7.useState(file.name);
|
|
1119
|
+
const fileSelectedPages = file.pages.filter((p) => selectedPageIds.includes(p.id));
|
|
1120
|
+
const allPagesSelected = file.pages.length > 0 && fileSelectedPages.length === file.pages.length;
|
|
1121
|
+
const [{ isDraggingFile }, dragFile] = reactDnd.useDrag({
|
|
1122
|
+
type: DND_TYPES.FILE,
|
|
1123
|
+
item: () => {
|
|
1124
|
+
if (isDraggingPageRef?.current !== void 0) {
|
|
1125
|
+
isDraggingPageRef.current = true;
|
|
1126
|
+
}
|
|
1127
|
+
return { fileId: file.id, file };
|
|
1128
|
+
},
|
|
1129
|
+
collect: (monitor) => ({
|
|
1130
|
+
isDraggingFile: monitor.isDragging()
|
|
1131
|
+
}),
|
|
1132
|
+
end: () => {
|
|
1133
|
+
if (isDraggingPageRef?.current !== void 0) {
|
|
1134
|
+
isDraggingPageRef.current = false;
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
});
|
|
1138
|
+
const [, drop] = reactDnd.useDrop({
|
|
1139
|
+
accept: DND_TYPES.PAGE,
|
|
1140
|
+
drop: (_item) => {
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
const handleRename = () => {
|
|
1144
|
+
if (newName.trim() && newName !== file.name) {
|
|
1145
|
+
onRenameFile(file.id, newName.trim());
|
|
1146
|
+
}
|
|
1147
|
+
setIsRenaming(false);
|
|
1148
|
+
};
|
|
1149
|
+
const combineRefs = React7.useCallback((el) => {
|
|
1150
|
+
drop(el);
|
|
1151
|
+
dragFile(el);
|
|
1152
|
+
}, [drop, dragFile]);
|
|
1153
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1154
|
+
Card,
|
|
1155
|
+
{
|
|
1156
|
+
className: cn(
|
|
1157
|
+
"mb-6 transition-all cursor-move",
|
|
1158
|
+
isDraggingFile ? "opacity-50 rotate-2 scale-105 shadow-2xl z-50" : "opacity-100"
|
|
1159
|
+
),
|
|
1160
|
+
ref: combineRefs,
|
|
1161
|
+
children: [
|
|
1162
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardHeader, { className: "pb-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1163
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1164
|
+
file.type === "pdf" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "h-5 w-5 text-red-500" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Image, { className: "h-5 w-5 text-blue-500" }),
|
|
1165
|
+
isRenaming ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1166
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1167
|
+
Input,
|
|
1168
|
+
{
|
|
1169
|
+
value: newName,
|
|
1170
|
+
onChange: (e) => setNewName(e.target.value),
|
|
1171
|
+
onKeyDown: (e) => {
|
|
1172
|
+
if (e.key === "Enter") handleRename();
|
|
1173
|
+
if (e.key === "Escape") {
|
|
1174
|
+
setIsRenaming(false);
|
|
1175
|
+
setNewName(file.name);
|
|
1176
|
+
}
|
|
1177
|
+
},
|
|
1178
|
+
className: "h-8 text-sm",
|
|
1179
|
+
autoFocus: true
|
|
1180
|
+
}
|
|
1181
|
+
),
|
|
1182
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", size: "sm", variant: "ghost", onClick: handleRename, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3 w-3" }) }),
|
|
1183
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1184
|
+
Button,
|
|
1185
|
+
{
|
|
1186
|
+
type: "button",
|
|
1187
|
+
size: "sm",
|
|
1188
|
+
variant: "ghost",
|
|
1189
|
+
onClick: () => {
|
|
1190
|
+
setIsRenaming(false);
|
|
1191
|
+
setNewName(file.name);
|
|
1192
|
+
},
|
|
1193
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
|
|
1194
|
+
}
|
|
1195
|
+
)
|
|
1196
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1197
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "text-base", children: file.name }),
|
|
1198
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground", children: [
|
|
1199
|
+
file.pages.length,
|
|
1200
|
+
" page",
|
|
1201
|
+
file.pages.length !== 1 ? "s" : ""
|
|
1202
|
+
] })
|
|
1203
|
+
] })
|
|
1204
|
+
] }),
|
|
1205
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1206
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1207
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1208
|
+
Button,
|
|
1209
|
+
{
|
|
1210
|
+
type: "button",
|
|
1211
|
+
variant: "outline",
|
|
1212
|
+
size: "sm",
|
|
1213
|
+
onClick: () => onSelectAllInFile(file.id),
|
|
1214
|
+
disabled: file.pages.length === 0,
|
|
1215
|
+
children: [
|
|
1216
|
+
allPagesSelected && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-3 w-3 mr-2" }),
|
|
1217
|
+
"Select All"
|
|
1218
|
+
]
|
|
1219
|
+
}
|
|
1220
|
+
) }),
|
|
1221
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: "Select all pages in this file" })
|
|
1222
|
+
] }) }),
|
|
1223
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
|
|
1224
|
+
/* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "hover:bg-gray-100 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, { className: "h-4 w-4" }) }) }),
|
|
1225
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuContent, { align: "end", className: "w-52", children: [
|
|
1226
|
+
/* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onClick: () => setIsRenaming(true), children: [
|
|
1227
|
+
/* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-4 w-4 mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) }),
|
|
1228
|
+
"Rename File"
|
|
1229
|
+
] }),
|
|
1230
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1231
|
+
DropdownMenuItem,
|
|
1232
|
+
{
|
|
1233
|
+
onClick: () => onRemoveFile(file.id),
|
|
1234
|
+
className: "text-red-600 hover:text-red-700 hover:bg-red-50 focus:text-red-700 focus:bg-red-50",
|
|
1235
|
+
children: [
|
|
1236
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-4 w-4 mr-2" }),
|
|
1237
|
+
"Remove File"
|
|
1238
|
+
]
|
|
1239
|
+
}
|
|
1240
|
+
)
|
|
1241
|
+
] })
|
|
1242
|
+
] })
|
|
1243
|
+
] })
|
|
1244
|
+
] }) }),
|
|
1245
|
+
/* @__PURE__ */ jsxRuntime.jsx(CardContent, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1246
|
+
ReorderablePageGrid,
|
|
1247
|
+
{
|
|
1248
|
+
file,
|
|
1249
|
+
selectedPageIds,
|
|
1250
|
+
onSingleSelectPage,
|
|
1251
|
+
onTogglePageSelect,
|
|
1252
|
+
onRangeSelectPage,
|
|
1253
|
+
onReorderPages,
|
|
1254
|
+
isDraggingPageRef
|
|
1255
|
+
}
|
|
1256
|
+
) })
|
|
1257
|
+
]
|
|
1258
|
+
}
|
|
1259
|
+
);
|
|
1260
|
+
};
|
|
1261
|
+
var BulkActionBar = ({
|
|
1262
|
+
selectedCount,
|
|
1263
|
+
files,
|
|
1264
|
+
onMovePages,
|
|
1265
|
+
onDeletePages,
|
|
1266
|
+
onClearSelection
|
|
1267
|
+
}) => {
|
|
1268
|
+
if (selectedCount === 0) return null;
|
|
1269
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-4 z-10 mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4 shadow-lg border-primary/20 bg-blue-50", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between flex-wrap gap-4", children: [
|
|
1270
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 flex-wrap", children: [
|
|
1271
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1272
|
+
"span",
|
|
1273
|
+
{
|
|
1274
|
+
className: "inline-flex items-center text-sm font-medium text-white rounded-full",
|
|
1275
|
+
style: {
|
|
1276
|
+
backgroundColor: "#0099cd",
|
|
1277
|
+
padding: "6px 14px"
|
|
1278
|
+
},
|
|
1279
|
+
children: [
|
|
1280
|
+
selectedCount,
|
|
1281
|
+
" page",
|
|
1282
|
+
selectedCount !== 1 ? "s" : "",
|
|
1283
|
+
" selected"
|
|
1284
|
+
]
|
|
1285
|
+
}
|
|
1286
|
+
),
|
|
1287
|
+
files.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1288
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
|
|
1289
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "move-select", className: "text-sm font-medium text-gray-700", children: "Move to file:" }) }),
|
|
1290
|
+
/* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: "Move selected pages to another file" }) })
|
|
1291
|
+
] }) }),
|
|
1292
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Select, { onValueChange: onMovePages, children: [
|
|
1293
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { id: "move-select", className: "w-48 bg-white", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, { placeholder: "Choose file..." }) }),
|
|
1294
|
+
/* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: files.map((file) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: file.id, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1295
|
+
file.type === "pdf" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "h-4 w-4" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Image, { className: "h-4 w-4" }),
|
|
1296
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: file.name })
|
|
1297
|
+
] }) }, file.id)) })
|
|
1298
|
+
] })
|
|
1299
|
+
] })
|
|
1300
|
+
] }),
|
|
1301
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1302
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1303
|
+
Button,
|
|
1304
|
+
{
|
|
1305
|
+
type: "button",
|
|
1306
|
+
variant: "destructive",
|
|
1307
|
+
size: "sm",
|
|
1308
|
+
onClick: onDeletePages,
|
|
1309
|
+
children: [
|
|
1310
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-4 w-4 mr-2" }),
|
|
1311
|
+
"Delete Selected"
|
|
1312
|
+
]
|
|
1313
|
+
}
|
|
1314
|
+
),
|
|
1315
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1316
|
+
Button,
|
|
1317
|
+
{
|
|
1318
|
+
type: "button",
|
|
1319
|
+
variant: "outline",
|
|
1320
|
+
size: "sm",
|
|
1321
|
+
onClick: onClearSelection,
|
|
1322
|
+
className: "bg-white",
|
|
1323
|
+
children: "Clear Selection"
|
|
1324
|
+
}
|
|
1325
|
+
)
|
|
1326
|
+
] })
|
|
1327
|
+
] }) }) });
|
|
1328
|
+
};
|
|
1329
|
+
var PageAssembler = React7.forwardRef(
|
|
1330
|
+
(props, ref) => {
|
|
1331
|
+
const {
|
|
1332
|
+
initialPdf,
|
|
1333
|
+
initialState,
|
|
1334
|
+
onChange,
|
|
1335
|
+
onValidityChange,
|
|
1336
|
+
limits,
|
|
1337
|
+
disableFileUpload = false,
|
|
1338
|
+
className
|
|
1339
|
+
} = props;
|
|
1340
|
+
const [state, dispatch] = React7.useReducer(
|
|
1341
|
+
assemblerReducer,
|
|
1342
|
+
initialState || {
|
|
1343
|
+
files: [],
|
|
1344
|
+
selection: [],
|
|
1345
|
+
lastSelectedPageId: void 0
|
|
1346
|
+
}
|
|
1347
|
+
);
|
|
1348
|
+
const [isLoading, setIsLoading] = React7.useState(false);
|
|
1349
|
+
const [processingStatus, setProcessingStatus] = React7.useState("");
|
|
1350
|
+
const [showDeleteConfirm, setShowDeleteConfirm] = React7.useState(false);
|
|
1351
|
+
const [showRemoveFileConfirm, setShowRemoveFileConfirm] = React7.useState(false);
|
|
1352
|
+
const [fileToRemove, setFileToRemove] = React7.useState(null);
|
|
1353
|
+
const fileInputRef = React7.useRef(null);
|
|
1354
|
+
const processedInitialPdfRef = React7.useRef(null);
|
|
1355
|
+
const isDraggingPage = React7.useRef(false);
|
|
1356
|
+
const onChangeRef = React7.useRef(onChange);
|
|
1357
|
+
const summary = React7.useMemo(() => ({
|
|
1358
|
+
totalFiles: state.files.length,
|
|
1359
|
+
totalPages: state.files.reduce((sum, file) => sum + file.pages.length, 0)
|
|
1360
|
+
}), [state.files]);
|
|
1361
|
+
const isValid = summary.totalPages > 0;
|
|
1362
|
+
React7.useEffect(() => {
|
|
1363
|
+
onChangeRef.current = onChange;
|
|
1364
|
+
}, [onChange]);
|
|
1365
|
+
const debouncedOnChange = React7.useMemo(
|
|
1366
|
+
() => debounce((newState) => {
|
|
1367
|
+
onChangeRef.current?.(newState);
|
|
1368
|
+
}, 200),
|
|
1369
|
+
[]
|
|
1370
|
+
);
|
|
1371
|
+
React7.useEffect(() => {
|
|
1372
|
+
debouncedOnChange(state);
|
|
1373
|
+
}, [state, debouncedOnChange]);
|
|
1374
|
+
React7.useEffect(() => {
|
|
1375
|
+
onValidityChange?.(isValid);
|
|
1376
|
+
}, [isValid, onValidityChange]);
|
|
1377
|
+
const processInitialPdf = React7.useCallback(async (pdf) => {
|
|
1378
|
+
setIsLoading(true);
|
|
1379
|
+
setProcessingStatus("Processing initial PDF...");
|
|
1380
|
+
try {
|
|
1381
|
+
let pdfBytes;
|
|
1382
|
+
if (pdf instanceof Blob) {
|
|
1383
|
+
const arrayBuffer = await pdf.arrayBuffer();
|
|
1384
|
+
pdfBytes = new Uint8Array(arrayBuffer);
|
|
1385
|
+
} else if (pdf instanceof ArrayBuffer) {
|
|
1386
|
+
pdfBytes = new Uint8Array(pdf);
|
|
1387
|
+
} else {
|
|
1388
|
+
pdfBytes = new Uint8Array(pdf);
|
|
1389
|
+
}
|
|
1390
|
+
const defensiveCopy = new Uint8Array(pdfBytes);
|
|
1391
|
+
const pages = await pdfToImages(defensiveCopy, { hideFormFields: true });
|
|
1392
|
+
if (pages.length === 0) {
|
|
1393
|
+
throw new Error("Failed to process PDF pages");
|
|
1394
|
+
}
|
|
1395
|
+
let originalBytes;
|
|
1396
|
+
let originalFile;
|
|
1397
|
+
try {
|
|
1398
|
+
new Uint8Array(pdfBytes.buffer || pdfBytes, 0, 1);
|
|
1399
|
+
originalBytes = new Uint8Array(pdfBytes);
|
|
1400
|
+
originalFile = new File([pdfBytes.buffer], "Initial Document.pdf", { type: "application/pdf" });
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1403
|
+
if (errorMessage.includes("detached") || errorMessage.includes("ArrayBuffer")) {
|
|
1404
|
+
throw new Error("Initial PDF data is no longer available. Please try refreshing the page.");
|
|
1405
|
+
}
|
|
1406
|
+
throw error;
|
|
1407
|
+
}
|
|
1408
|
+
const fileName = pdf instanceof File ? pdf.name : "Initial Document.pdf";
|
|
1409
|
+
const fileId = generateId();
|
|
1410
|
+
const file = {
|
|
1411
|
+
id: fileId,
|
|
1412
|
+
name: fileName,
|
|
1413
|
+
type: "pdf",
|
|
1414
|
+
originalFile,
|
|
1415
|
+
originalBytes,
|
|
1416
|
+
pages: pages.map((page, index) => ({
|
|
1417
|
+
id: generateId(),
|
|
1418
|
+
srcIndex: index,
|
|
1419
|
+
thumbUrl: page.imageUrl,
|
|
1420
|
+
width: page.width,
|
|
1421
|
+
height: page.height,
|
|
1422
|
+
mime: "application/pdf",
|
|
1423
|
+
originalFileId: fileId
|
|
1424
|
+
}))
|
|
1425
|
+
};
|
|
1426
|
+
dispatch({ type: "ADD_FILE", payload: file });
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
console.error("Error processing initial PDF:", error);
|
|
1429
|
+
} finally {
|
|
1430
|
+
setIsLoading(false);
|
|
1431
|
+
setProcessingStatus("");
|
|
1432
|
+
}
|
|
1433
|
+
}, []);
|
|
1434
|
+
React7.useEffect(() => {
|
|
1435
|
+
if (initialPdf && !initialState) {
|
|
1436
|
+
const pdfIdentifier = initialPdf instanceof Uint8Array ? `uint8array-${initialPdf.length}-${initialPdf[0]}-${initialPdf[initialPdf.length - 1]}` : `${initialPdf.constructor.name}-${initialPdf.size || initialPdf.byteLength}`;
|
|
1437
|
+
if (processedInitialPdfRef.current === pdfIdentifier) {
|
|
1438
|
+
return;
|
|
1439
|
+
}
|
|
1440
|
+
processedInitialPdfRef.current = pdfIdentifier;
|
|
1441
|
+
processInitialPdf(initialPdf);
|
|
1442
|
+
}
|
|
1443
|
+
}, [initialPdf, initialState, processInitialPdf]);
|
|
1444
|
+
React7.useEffect(() => {
|
|
1445
|
+
if (initialState) {
|
|
1446
|
+
dispatch({ type: "RESET", payload: initialState });
|
|
1447
|
+
}
|
|
1448
|
+
}, [initialState]);
|
|
1449
|
+
const processFiles = React7.useCallback(async (files) => {
|
|
1450
|
+
const fileArray = Array.from(files);
|
|
1451
|
+
const maxFileSize = (limits?.maxFileSizeMB || 10) * 1024 * 1024;
|
|
1452
|
+
setIsLoading(true);
|
|
1453
|
+
setProcessingStatus(`Processing ${fileArray.length} file${fileArray.length !== 1 ? "s" : ""}...`);
|
|
1454
|
+
for (const file of fileArray) {
|
|
1455
|
+
try {
|
|
1456
|
+
if (file.size > maxFileSize) {
|
|
1457
|
+
console.error(`File ${file.name} is too large`);
|
|
1458
|
+
continue;
|
|
1459
|
+
}
|
|
1460
|
+
const validTypes = ["application/pdf", "image/png", "image/jpeg", "image/jpg", "image/webp"];
|
|
1461
|
+
if (!validTypes.includes(file.type)) {
|
|
1462
|
+
console.error(`Unsupported file type: ${file.name}`);
|
|
1463
|
+
continue;
|
|
1464
|
+
}
|
|
1465
|
+
const fileInfo = {
|
|
1466
|
+
id: generateId(),
|
|
1467
|
+
name: file.name,
|
|
1468
|
+
type: file.type === "application/pdf" ? "pdf" : "image",
|
|
1469
|
+
originalFile: file,
|
|
1470
|
+
originalBytes: new Uint8Array(),
|
|
1471
|
+
pages: []
|
|
1472
|
+
};
|
|
1473
|
+
if (file.type === "application/pdf") {
|
|
1474
|
+
setProcessingStatus(`Processing PDF: ${file.name}`);
|
|
1475
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
1476
|
+
const pdfBytes = new Uint8Array(arrayBuffer);
|
|
1477
|
+
fileInfo.originalBytes = new Uint8Array(pdfBytes);
|
|
1478
|
+
const defensiveCopy = new Uint8Array(pdfBytes);
|
|
1479
|
+
const pages = await pdfToImages(defensiveCopy, { hideFormFields: true });
|
|
1480
|
+
fileInfo.pages = pages.map((page, index) => ({
|
|
1481
|
+
id: generateId(),
|
|
1482
|
+
srcIndex: index,
|
|
1483
|
+
thumbUrl: page.imageUrl,
|
|
1484
|
+
width: page.width,
|
|
1485
|
+
height: page.height,
|
|
1486
|
+
mime: file.type,
|
|
1487
|
+
originalFileId: fileInfo.id
|
|
1488
|
+
}));
|
|
1489
|
+
} else {
|
|
1490
|
+
setProcessingStatus(`Processing image: ${file.name}`);
|
|
1491
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
1492
|
+
const imageBytes = new Uint8Array(arrayBuffer);
|
|
1493
|
+
fileInfo.originalBytes = new Uint8Array(imageBytes);
|
|
1494
|
+
const thumbUrl = URL.createObjectURL(file);
|
|
1495
|
+
const img = new Image();
|
|
1496
|
+
const dimensions = await new Promise((resolve) => {
|
|
1497
|
+
img.onload = () => resolve({ width: img.width, height: img.height });
|
|
1498
|
+
img.src = thumbUrl;
|
|
1499
|
+
});
|
|
1500
|
+
const maxWidth = 595;
|
|
1501
|
+
const maxHeight = 842;
|
|
1502
|
+
let pdfWidth = dimensions.width;
|
|
1503
|
+
let pdfHeight = dimensions.height;
|
|
1504
|
+
if (dimensions.width > maxWidth || dimensions.height > maxHeight) {
|
|
1505
|
+
const scaleX = maxWidth / dimensions.width;
|
|
1506
|
+
const scaleY = maxHeight / dimensions.height;
|
|
1507
|
+
const scale = Math.min(scaleX, scaleY);
|
|
1508
|
+
pdfWidth = dimensions.width * scale;
|
|
1509
|
+
pdfHeight = dimensions.height * scale;
|
|
1510
|
+
}
|
|
1511
|
+
fileInfo.pages = [{
|
|
1512
|
+
id: generateId(),
|
|
1513
|
+
srcIndex: 0,
|
|
1514
|
+
thumbUrl,
|
|
1515
|
+
width: Math.max(pdfWidth, 100),
|
|
1516
|
+
height: Math.max(pdfHeight, 100),
|
|
1517
|
+
mime: file.type,
|
|
1518
|
+
originalFileId: fileInfo.id
|
|
1519
|
+
}];
|
|
1520
|
+
}
|
|
1521
|
+
dispatch({ type: "ADD_FILE", payload: fileInfo });
|
|
1522
|
+
} catch (error) {
|
|
1523
|
+
console.error(`Error processing file ${file.name}:`, error);
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
setIsLoading(false);
|
|
1527
|
+
setProcessingStatus("");
|
|
1528
|
+
}, [limits]);
|
|
1529
|
+
const handleFileUpload = React7.useCallback(() => {
|
|
1530
|
+
fileInputRef.current?.click();
|
|
1531
|
+
}, []);
|
|
1532
|
+
const handleFileChange = React7.useCallback((e) => {
|
|
1533
|
+
const files = e.target.files;
|
|
1534
|
+
if (files) {
|
|
1535
|
+
processFiles(files);
|
|
1536
|
+
}
|
|
1537
|
+
if (fileInputRef.current) {
|
|
1538
|
+
fileInputRef.current.value = "";
|
|
1539
|
+
}
|
|
1540
|
+
}, [processFiles]);
|
|
1541
|
+
const handleDrop = React7.useCallback((e) => {
|
|
1542
|
+
e.preventDefault();
|
|
1543
|
+
const files = e.dataTransfer.files;
|
|
1544
|
+
if (files && files.length > 0) {
|
|
1545
|
+
if (isDraggingPage.current) {
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
processFiles(files);
|
|
1549
|
+
}
|
|
1550
|
+
}, [processFiles]);
|
|
1551
|
+
const handleDragOver = React7.useCallback((e) => {
|
|
1552
|
+
e.preventDefault();
|
|
1553
|
+
}, []);
|
|
1554
|
+
const handleSingleSelectPage = React7.useCallback((pageId) => {
|
|
1555
|
+
dispatch({ type: "SINGLE_SELECT_PAGE", payload: pageId });
|
|
1556
|
+
}, []);
|
|
1557
|
+
const handleTogglePageSelect = React7.useCallback((pageId) => {
|
|
1558
|
+
dispatch({ type: "TOGGLE_PAGE_SELECTION", payload: { pageId, files: state.files } });
|
|
1559
|
+
}, [state.files]);
|
|
1560
|
+
const handleRangeSelectPage = React7.useCallback((pageId) => {
|
|
1561
|
+
dispatch({ type: "RANGE_SELECT_PAGES", payload: { pageId, files: state.files } });
|
|
1562
|
+
}, [state.files]);
|
|
1563
|
+
const handleSelectAllInFile = React7.useCallback((fileId) => {
|
|
1564
|
+
const file = state.files.find((f) => f.id === fileId);
|
|
1565
|
+
if (!file) return;
|
|
1566
|
+
const filePageIds = file.pages.map((p) => p.id);
|
|
1567
|
+
const allSelected = filePageIds.every((id) => state.selection.includes(id));
|
|
1568
|
+
if (allSelected) {
|
|
1569
|
+
dispatch({
|
|
1570
|
+
type: "SET_SELECTION",
|
|
1571
|
+
payload: state.selection.filter((id) => !filePageIds.includes(id))
|
|
1572
|
+
});
|
|
1573
|
+
} else {
|
|
1574
|
+
const newSelection = [.../* @__PURE__ */ new Set([...state.selection, ...filePageIds])];
|
|
1575
|
+
dispatch({ type: "SET_SELECTION", payload: newSelection });
|
|
1576
|
+
}
|
|
1577
|
+
}, [state.files, state.selection]);
|
|
1578
|
+
const handleMovePages = React7.useCallback((targetFileId) => {
|
|
1579
|
+
if (state.selection.length === 0) return;
|
|
1580
|
+
dispatch({ type: "MOVE_PAGES", payload: { pageIds: state.selection, targetFileId } });
|
|
1581
|
+
}, [state.selection]);
|
|
1582
|
+
const handleDeletePages = React7.useCallback(() => {
|
|
1583
|
+
setShowDeleteConfirm(true);
|
|
1584
|
+
}, []);
|
|
1585
|
+
const confirmDeletePages = React7.useCallback(() => {
|
|
1586
|
+
dispatch({ type: "DELETE_PAGES", payload: state.selection });
|
|
1587
|
+
setShowDeleteConfirm(false);
|
|
1588
|
+
}, [state.selection]);
|
|
1589
|
+
const handleRemoveFile = React7.useCallback((fileId) => {
|
|
1590
|
+
const file = state.files.find((f) => f.id === fileId);
|
|
1591
|
+
if (!file) return;
|
|
1592
|
+
if (file.pages.length > 0) {
|
|
1593
|
+
setFileToRemove({
|
|
1594
|
+
id: file.id,
|
|
1595
|
+
name: file.name,
|
|
1596
|
+
pageCount: file.pages.length
|
|
1597
|
+
});
|
|
1598
|
+
setShowRemoveFileConfirm(true);
|
|
1599
|
+
} else {
|
|
1600
|
+
dispatch({ type: "REMOVE_FILE", payload: fileId });
|
|
1601
|
+
}
|
|
1602
|
+
}, [state.files]);
|
|
1603
|
+
const confirmRemoveFile = React7.useCallback(() => {
|
|
1604
|
+
if (fileToRemove) {
|
|
1605
|
+
dispatch({ type: "REMOVE_FILE", payload: fileToRemove.id });
|
|
1606
|
+
setFileToRemove(null);
|
|
1607
|
+
}
|
|
1608
|
+
setShowRemoveFileConfirm(false);
|
|
1609
|
+
}, [fileToRemove]);
|
|
1610
|
+
const handleRenameFile = React7.useCallback((fileId, name) => {
|
|
1611
|
+
dispatch({ type: "RENAME_FILE", payload: { fileId, name } });
|
|
1612
|
+
}, []);
|
|
1613
|
+
const handleReorderFiles = React7.useCallback((newOrder) => {
|
|
1614
|
+
dispatch({ type: "REORDER_FILES", payload: { newOrder } });
|
|
1615
|
+
}, []);
|
|
1616
|
+
React7.useImperativeHandle(ref, () => ({
|
|
1617
|
+
assemble: async () => {
|
|
1618
|
+
setIsLoading(true);
|
|
1619
|
+
setProcessingStatus("Assembling PDF...");
|
|
1620
|
+
try {
|
|
1621
|
+
const { PDFDocument: PDFDocument2 } = await import('pdf-lib');
|
|
1622
|
+
const combinedDoc = await PDFDocument2.create();
|
|
1623
|
+
const loadedPdfs = /* @__PURE__ */ new Map();
|
|
1624
|
+
const fileMap = /* @__PURE__ */ new Map();
|
|
1625
|
+
for (const file of state.files) {
|
|
1626
|
+
fileMap.set(file.id, file);
|
|
1627
|
+
if (file.type === "pdf" && file.originalBytes) {
|
|
1628
|
+
try {
|
|
1629
|
+
const sourcePdf = await PDFDocument2.load(file.originalBytes);
|
|
1630
|
+
loadedPdfs.set(file.id, sourcePdf);
|
|
1631
|
+
} catch (error) {
|
|
1632
|
+
console.error(`Error loading PDF for file ${file.id}:`, error);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
for (const file of state.files) {
|
|
1637
|
+
if (file.pages.length === 0) continue;
|
|
1638
|
+
for (const page of file.pages) {
|
|
1639
|
+
const originalFileId = page.originalFileId || file.id;
|
|
1640
|
+
const sourceFile = fileMap.get(originalFileId);
|
|
1641
|
+
if (!sourceFile) {
|
|
1642
|
+
console.warn(`Source file not found for page ${page.id}`);
|
|
1643
|
+
continue;
|
|
1644
|
+
}
|
|
1645
|
+
if (sourceFile.type === "pdf") {
|
|
1646
|
+
const sourcePdf = loadedPdfs.get(originalFileId);
|
|
1647
|
+
if (!sourcePdf) {
|
|
1648
|
+
console.warn(`Source PDF not loaded for page ${page.id}`);
|
|
1649
|
+
continue;
|
|
1650
|
+
}
|
|
1651
|
+
const sourcePageCount = sourcePdf.getPageCount();
|
|
1652
|
+
if (page.srcIndex >= 0 && page.srcIndex < sourcePageCount) {
|
|
1653
|
+
try {
|
|
1654
|
+
const copiedPages = await combinedDoc.copyPages(sourcePdf, [page.srcIndex]);
|
|
1655
|
+
copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
|
|
1656
|
+
} catch (copyError) {
|
|
1657
|
+
console.error(`Error copying page ${page.srcIndex}:`, copyError);
|
|
1658
|
+
throw new Error(`Failed to copy page: ${copyError instanceof Error ? copyError.message : String(copyError)}`);
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
} else if (sourceFile.type === "image" && sourceFile.originalFile) {
|
|
1662
|
+
try {
|
|
1663
|
+
const imagePdfBytes = await imageToPdf(sourceFile.originalFile);
|
|
1664
|
+
const imagePdf = await PDFDocument2.load(imagePdfBytes);
|
|
1665
|
+
const copiedPages = await combinedDoc.copyPages(imagePdf, [0]);
|
|
1666
|
+
copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
|
|
1667
|
+
} catch (imageError) {
|
|
1668
|
+
console.error(`Error converting image page:`, imageError);
|
|
1669
|
+
throw new Error(`Failed to convert image: ${imageError instanceof Error ? imageError.message : String(imageError)}`);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
const pdfBytes = await combinedDoc.save();
|
|
1675
|
+
const blob = new Blob([pdfBytes.buffer], { type: "application/pdf" });
|
|
1676
|
+
return blob;
|
|
1677
|
+
} catch (error) {
|
|
1678
|
+
console.error("Error assembling PDF:", error);
|
|
1679
|
+
throw error;
|
|
1680
|
+
} finally {
|
|
1681
|
+
setIsLoading(false);
|
|
1682
|
+
setProcessingStatus("");
|
|
1683
|
+
}
|
|
1684
|
+
},
|
|
1685
|
+
getSummary: () => summary,
|
|
1686
|
+
isBusy: () => isLoading,
|
|
1687
|
+
getState: () => state
|
|
1688
|
+
}), [state, summary, isLoading]);
|
|
1689
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(reactDnd.DndProvider, { backend: reactDndHtml5Backend.HTML5Backend, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("space-y-6 pb-6", className), children: [
|
|
1690
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1691
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1692
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-medium", children: "Page Assembly" }),
|
|
1693
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Upload, arrange, and combine pages into a single PDF document" })
|
|
1694
|
+
] }),
|
|
1695
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
|
|
1696
|
+
summary.totalPages > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-500", children: [
|
|
1697
|
+
summary.totalFiles,
|
|
1698
|
+
" file",
|
|
1699
|
+
summary.totalFiles !== 1 ? "s" : "",
|
|
1700
|
+
", ",
|
|
1701
|
+
summary.totalPages,
|
|
1702
|
+
" page",
|
|
1703
|
+
summary.totalPages !== 1 ? "s" : ""
|
|
1704
|
+
] }),
|
|
1705
|
+
!disableFileUpload && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1706
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", onClick: handleFileUpload, disabled: isLoading, children: [
|
|
1707
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "h-4 w-4 mr-2" }),
|
|
1708
|
+
"Add Files"
|
|
1709
|
+
] }),
|
|
1710
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1711
|
+
"input",
|
|
1712
|
+
{
|
|
1713
|
+
ref: fileInputRef,
|
|
1714
|
+
type: "file",
|
|
1715
|
+
multiple: true,
|
|
1716
|
+
accept: ".pdf,image/*",
|
|
1717
|
+
onChange: handleFileChange,
|
|
1718
|
+
className: "hidden"
|
|
1719
|
+
}
|
|
1720
|
+
)
|
|
1721
|
+
] })
|
|
1722
|
+
] })
|
|
1723
|
+
] }),
|
|
1724
|
+
isLoading && /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1725
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-primary" }),
|
|
1726
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: processingStatus })
|
|
1727
|
+
] }) }),
|
|
1728
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1729
|
+
BulkActionBar,
|
|
1730
|
+
{
|
|
1731
|
+
selectedCount: state.selection.length,
|
|
1732
|
+
files: state.files,
|
|
1733
|
+
onMovePages: handleMovePages,
|
|
1734
|
+
onDeletePages: handleDeletePages,
|
|
1735
|
+
onClearSelection: () => dispatch({ type: "CLEAR_SELECTION" })
|
|
1736
|
+
}
|
|
1737
|
+
),
|
|
1738
|
+
state.files.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1739
|
+
"div",
|
|
1740
|
+
{
|
|
1741
|
+
onDrop: handleDrop,
|
|
1742
|
+
onDragOver: handleDragOver,
|
|
1743
|
+
children: [
|
|
1744
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1745
|
+
FileDropZone,
|
|
1746
|
+
{
|
|
1747
|
+
targetIndex: 0,
|
|
1748
|
+
onReorderFiles: handleReorderFiles,
|
|
1749
|
+
files: state.files
|
|
1750
|
+
}
|
|
1751
|
+
),
|
|
1752
|
+
state.files.map((file, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1753
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1754
|
+
FileCard,
|
|
1755
|
+
{
|
|
1756
|
+
file,
|
|
1757
|
+
selectedPageIds: state.selection,
|
|
1758
|
+
onSingleSelectPage: handleSingleSelectPage,
|
|
1759
|
+
onTogglePageSelect: handleTogglePageSelect,
|
|
1760
|
+
onRangeSelectPage: handleRangeSelectPage,
|
|
1761
|
+
onSelectAllInFile: handleSelectAllInFile,
|
|
1762
|
+
onRemoveFile: handleRemoveFile,
|
|
1763
|
+
onRenameFile: handleRenameFile,
|
|
1764
|
+
onReorderPages: (fileId, newOrder) => dispatch({ type: "REORDER_PAGES", payload: { fileId, newOrder } }),
|
|
1765
|
+
isDraggingPageRef: isDraggingPage
|
|
1766
|
+
}
|
|
1767
|
+
),
|
|
1768
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1769
|
+
FileDropZone,
|
|
1770
|
+
{
|
|
1771
|
+
targetIndex: index + 1,
|
|
1772
|
+
onReorderFiles: handleReorderFiles,
|
|
1773
|
+
files: state.files
|
|
1774
|
+
}
|
|
1775
|
+
)
|
|
1776
|
+
] }, file.id))
|
|
1777
|
+
]
|
|
1778
|
+
}
|
|
1779
|
+
) : !disableFileUpload && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1780
|
+
Card,
|
|
1781
|
+
{
|
|
1782
|
+
className: "p-12 border-dashed border-2 border-gray-300 hover:border-gray-400 transition-colors cursor-pointer",
|
|
1783
|
+
onClick: handleFileUpload,
|
|
1784
|
+
onDrop: handleDrop,
|
|
1785
|
+
onDragOver: handleDragOver,
|
|
1786
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
1787
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderOpen, { className: "h-12 w-12 mx-auto text-gray-400 mb-4" }),
|
|
1788
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-lg font-medium text-gray-900 mb-2", children: "No files added" }),
|
|
1789
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-500 mb-4", children: "Upload PDF files and images to start building your document" }),
|
|
1790
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Button, { type: "button", variant: "outline", children: [
|
|
1791
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "h-4 w-4 mr-2" }),
|
|
1792
|
+
"Choose Files"
|
|
1793
|
+
] })
|
|
1794
|
+
] })
|
|
1795
|
+
}
|
|
1796
|
+
),
|
|
1797
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialog, { open: showDeleteConfirm, onOpenChange: setShowDeleteConfirm, children: /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogContent, { children: [
|
|
1798
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AlertDialogHeader, { children: [
|
|
1799
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogTitle, { children: "Delete Pages" }),
|
|
1800
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AlertDialogDescription, { children: [
|
|
1801
|
+
"Are you sure you want to delete ",
|
|
1802
|
+
state.selection.length,
|
|
1803
|
+
" selected page",
|
|
1804
|
+
state.selection.length !== 1 ? "s" : "",
|
|
1805
|
+
"? This action cannot be undone."
|
|
1806
|
+
] })
|
|
1807
|
+
] }),
|
|
1808
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { children: [
|
|
1809
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { children: "Cancel" }),
|
|
1810
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1811
|
+
AlertDialogAction,
|
|
1812
|
+
{
|
|
1813
|
+
className: "bg-destructive text-white hover:bg-destructive/90",
|
|
1814
|
+
onClick: confirmDeletePages,
|
|
1815
|
+
children: "Delete"
|
|
1816
|
+
}
|
|
1817
|
+
)
|
|
1818
|
+
] })
|
|
1819
|
+
] }) }),
|
|
1820
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialog, { open: showRemoveFileConfirm, onOpenChange: setShowRemoveFileConfirm, children: /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogContent, { children: [
|
|
1821
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AlertDialogHeader, { children: [
|
|
1822
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogTitle, { children: "Remove File" }),
|
|
1823
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AlertDialogDescription, { children: [
|
|
1824
|
+
'Are you sure you want to remove "',
|
|
1825
|
+
fileToRemove?.name,
|
|
1826
|
+
'" and all its ',
|
|
1827
|
+
fileToRemove?.pageCount,
|
|
1828
|
+
" page",
|
|
1829
|
+
fileToRemove?.pageCount !== 1 ? "s" : "",
|
|
1830
|
+
"? This action cannot be undone."
|
|
1831
|
+
] })
|
|
1832
|
+
] }),
|
|
1833
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AlertDialogFooter, { children: [
|
|
1834
|
+
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogCancel, { onClick: () => {
|
|
1835
|
+
setFileToRemove(null);
|
|
1836
|
+
setShowRemoveFileConfirm(false);
|
|
1837
|
+
}, children: "Cancel" }),
|
|
1838
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1839
|
+
AlertDialogAction,
|
|
1840
|
+
{
|
|
1841
|
+
className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
1842
|
+
onClick: confirmRemoveFile,
|
|
1843
|
+
children: "Remove File"
|
|
1844
|
+
}
|
|
1845
|
+
)
|
|
1846
|
+
] })
|
|
1847
|
+
] }) })
|
|
1848
|
+
] }) }) });
|
|
1849
|
+
}
|
|
1850
|
+
);
|
|
1851
|
+
PageAssembler.displayName = "PageAssembler";
|
|
1852
|
+
function generateId() {
|
|
1853
|
+
return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1854
|
+
}
|
|
1855
|
+
function debounce(func, wait) {
|
|
1856
|
+
let timeout;
|
|
1857
|
+
return ((...args) => {
|
|
1858
|
+
clearTimeout(timeout);
|
|
1859
|
+
timeout = setTimeout(() => func(...args), wait);
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
var PageAssembler_default = PageAssembler;
|
|
1863
|
+
|
|
1864
|
+
exports.Button = Button;
|
|
1865
|
+
exports.PageAssembler = PageAssembler;
|
|
1866
|
+
exports.createPdfBlobUrl = createPdfBlobUrl;
|
|
1867
|
+
exports.default = PageAssembler_default;
|
|
1868
|
+
exports.downloadPdf = downloadPdf;
|
|
1869
|
+
exports.imageToPdf = imageToPdf;
|
|
1870
|
+
exports.pdfToImages = pdfToImages;
|
|
1871
|
+
//# sourceMappingURL=index.js.map
|
|
1872
|
+
//# sourceMappingURL=index.js.map
|