@ttt-productions/upload-ui 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/react/deferred-upload-form-shell.d.ts +39 -0
- package/dist/react/deferred-upload-form-shell.d.ts.map +1 -0
- package/dist/react/deferred-upload-form-shell.js +56 -0
- package/dist/react/deferred-upload-form-shell.js.map +1 -0
- package/dist/react/guarded-link.d.ts +38 -0
- package/dist/react/guarded-link.d.ts.map +1 -0
- package/dist/react/guarded-link.js +14 -0
- package/dist/react/guarded-link.js.map +1 -0
- package/dist/react/in-flight-uploads-provider.d.ts +214 -0
- package/dist/react/in-flight-uploads-provider.d.ts.map +1 -0
- package/dist/react/in-flight-uploads-provider.js +286 -0
- package/dist/react/in-flight-uploads-provider.js.map +1 -0
- package/dist/react/index.d.ts +11 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +10 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/local-upload-guard-provider.d.ts +76 -0
- package/dist/react/local-upload-guard-provider.d.ts.map +1 -0
- package/dist/react/local-upload-guard-provider.js +110 -0
- package/dist/react/local-upload-guard-provider.js.map +1 -0
- package/dist/react/upload-activity-tray.d.ts +54 -0
- package/dist/react/upload-activity-tray.d.ts.map +1 -0
- package/dist/react/upload-activity-tray.js +87 -0
- package/dist/react/upload-activity-tray.js.map +1 -0
- package/dist/react/use-clear-upload-activity.d.ts +8 -0
- package/dist/react/use-clear-upload-activity.d.ts.map +1 -0
- package/dist/react/use-clear-upload-activity.js +24 -0
- package/dist/react/use-clear-upload-activity.js.map +1 -0
- package/dist/react/use-guarded-navigation.d.ts +8 -0
- package/dist/react/use-guarded-navigation.d.ts.map +1 -0
- package/dist/react/use-guarded-navigation.js +34 -0
- package/dist/react/use-guarded-navigation.js.map +1 -0
- package/dist/react/use-guarded-upload.d.ts +37 -0
- package/dist/react/use-guarded-upload.d.ts.map +1 -0
- package/dist/react/use-guarded-upload.js +39 -0
- package/dist/react/use-guarded-upload.js.map +1 -0
- package/dist/react/use-upload-processing.d.ts +27 -0
- package/dist/react/use-upload-processing.d.ts.map +1 -0
- package/dist/react/use-upload-processing.js +112 -0
- package/dist/react/use-upload-processing.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Global upload activity tray.
|
|
5
|
+
*
|
|
6
|
+
* Renders a fixed bottom-left floating action button when there is any
|
|
7
|
+
* active or non-cleared terminal upload. Opens a popover listing each
|
|
8
|
+
* item with backend status. Terminal items (completed / failed / rejected)
|
|
9
|
+
* get a Clear (×) button that calls the consumer-supplied clear mutation.
|
|
10
|
+
*
|
|
11
|
+
* Framework-agnostic via render props:
|
|
12
|
+
* - `renderViewAllLink` lets the consumer plug in their app's Link
|
|
13
|
+
* component (Next.js Link, React Router Link, anchor, etc.) routed at
|
|
14
|
+
* their canonical "view all uploads" destination.
|
|
15
|
+
* - `renderFooter` lets the consumer fully replace the footer slot.
|
|
16
|
+
* - Both omitted — footer is not rendered.
|
|
17
|
+
*/
|
|
18
|
+
import { useMemo } from 'react';
|
|
19
|
+
import { Cloud, Loader2, CheckCircle2, AlertCircle, XCircle, X } from 'lucide-react';
|
|
20
|
+
import { Button, Popover, PopoverTrigger, PopoverContent, ScrollArea, Separator, } from '@ttt-productions/ui-core/react';
|
|
21
|
+
import { cn } from '@ttt-productions/ui-core';
|
|
22
|
+
import { isTerminalUpload, useUploadActivityState, } from './in-flight-uploads-provider.js';
|
|
23
|
+
function getRowMeta(item) {
|
|
24
|
+
switch (item.status) {
|
|
25
|
+
case 'pending':
|
|
26
|
+
return {
|
|
27
|
+
label: 'Waiting to process…',
|
|
28
|
+
icon: _jsx(Loader2, { className: "icon-sm animate-spin" }),
|
|
29
|
+
iconClassName: 'text-muted-foreground',
|
|
30
|
+
};
|
|
31
|
+
case 'processing':
|
|
32
|
+
return {
|
|
33
|
+
label: 'Processing…',
|
|
34
|
+
icon: _jsx(Loader2, { className: "icon-sm animate-spin" }),
|
|
35
|
+
iconClassName: 'text-primary',
|
|
36
|
+
};
|
|
37
|
+
case 'completed':
|
|
38
|
+
return {
|
|
39
|
+
label: 'Completed',
|
|
40
|
+
icon: _jsx(CheckCircle2, { className: "icon-sm" }),
|
|
41
|
+
iconClassName: 'text-green-600 dark:text-green-500',
|
|
42
|
+
};
|
|
43
|
+
case 'failed':
|
|
44
|
+
return {
|
|
45
|
+
label: 'Failed',
|
|
46
|
+
detail: item.errorMessage,
|
|
47
|
+
icon: _jsx(AlertCircle, { className: "icon-sm" }),
|
|
48
|
+
iconClassName: 'text-destructive',
|
|
49
|
+
};
|
|
50
|
+
case 'rejected':
|
|
51
|
+
return {
|
|
52
|
+
label: 'Rejected',
|
|
53
|
+
detail: item.errorMessage,
|
|
54
|
+
icon: _jsx(XCircle, { className: "icon-sm" }),
|
|
55
|
+
iconClassName: 'text-destructive',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function UploadActivityTrayRow({ item, onClear, isClearPending, }) {
|
|
60
|
+
const meta = getRowMeta(item);
|
|
61
|
+
return (_jsxs("div", { className: "flex items-start gap-3 p-3", children: [_jsx("div", { className: cn('mt-0.5 shrink-0', meta.iconClassName), children: meta.icon }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsx("p", { className: "text-sm font-medium truncate", children: meta.label }), _jsx("p", { className: "text-xs text-muted-foreground truncate", children: item.surface }), meta.detail && (_jsx("p", { className: "text-xs text-muted-foreground mt-1 line-clamp-2", children: meta.detail }))] }), onClear && (_jsx(Button, { size: "icon", variant: "ghost", className: "h-7 w-7 shrink-0", onClick: onClear, disabled: isClearPending, "aria-label": `Clear ${meta.label.toLowerCase()} upload`, children: _jsx(X, { className: "icon-sm" }) }))] }));
|
|
62
|
+
}
|
|
63
|
+
export function UploadActivityTray(props) {
|
|
64
|
+
const { clearMutation, viewAllHref, renderViewAllLink, renderFooter, viewAllLabel = 'View all uploads →', } = props;
|
|
65
|
+
const items = useUploadActivityState();
|
|
66
|
+
const activeCount = useMemo(() => items.filter((item) => item.status === 'pending' || item.status === 'processing').length, [items]);
|
|
67
|
+
if (items.length === 0) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const footerContent = (() => {
|
|
71
|
+
if (renderFooter)
|
|
72
|
+
return renderFooter();
|
|
73
|
+
if (viewAllHref && renderViewAllLink) {
|
|
74
|
+
return (_jsx("div", { className: "p-3", children: renderViewAllLink({
|
|
75
|
+
href: viewAllHref,
|
|
76
|
+
children: (_jsx("span", { className: "text-sm text-primary hover:underline", children: viewAllLabel })),
|
|
77
|
+
}) }));
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
})();
|
|
81
|
+
return (_jsx("div", { className: "fixed bottom-6 left-6 z-50", children: _jsxs(Popover, { children: [_jsx(PopoverTrigger, { asChild: true, children: _jsxs(Button, { size: "icon", variant: "inverted", className: "relative h-14 w-14 rounded-full shadow-lg", "aria-label": activeCount > 0
|
|
82
|
+
? `Upload activity: ${activeCount} in progress`
|
|
83
|
+
: `Upload activity: ${items.length} item${items.length === 1 ? '' : 's'}`, children: [activeCount > 0 ? (_jsx(Loader2, { className: "icon-md animate-spin" })) : (_jsx(Cloud, { className: "icon-md" })), _jsx("span", { className: "absolute -top-1 -right-1 min-w-[1.25rem] h-5 px-1 rounded-full bg-destructive text-destructive-foreground text-xs font-medium flex items-center justify-center", "aria-hidden": "true", children: items.length })] }) }), _jsxs(PopoverContent, { align: "start", side: "top", className: "app-popover p-0 w-80", children: [_jsxs("div", { className: "flex items-center justify-between p-3", children: [_jsx("h2", { className: "text-sm font-semibold", children: "Upload Activity" }), _jsx("span", { className: "text-xs text-muted-foreground", children: activeCount > 0
|
|
84
|
+
? `${activeCount} in progress`
|
|
85
|
+
: `${items.length} item${items.length === 1 ? '' : 's'}` })] }), _jsx(Separator, {}), _jsx(ScrollArea, { className: "max-h-80", children: _jsx("ul", { className: "divide-y", children: items.map((item) => (_jsx("li", { children: _jsx(UploadActivityTrayRow, { item: item, onClear: isTerminalUpload(item) ? () => clearMutation.mutate(item.id) : undefined, isClearPending: clearMutation.isPending && clearMutation.variables === item.id }) }, item.id))) }) }), footerContent !== null && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), footerContent] }))] })] }) }));
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=upload-activity-tray.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload-activity-tray.js","sourceRoot":"","sources":["../../src/react/upload-activity-tray.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,OAAO,EAAkB,MAAM,OAAO,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EACL,MAAM,EACN,OAAO,EACP,cAAc,EACd,cAAc,EACd,UAAU,EACV,SAAS,GACV,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,EAAE,EAAE,MAAM,0BAA0B,CAAC;AAC9C,OAAO,EACL,gBAAgB,EAChB,sBAAsB,GAEvB,MAAM,iCAAiC,CAAC;AASzC,SAAS,UAAU,CAA6B,IAAiC;IAC/E,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,SAAS;YACZ,OAAO;gBACL,KAAK,EAAE,qBAAqB;gBAC5B,IAAI,EAAE,KAAC,OAAO,IAAC,SAAS,EAAC,sBAAsB,GAAG;gBAClD,aAAa,EAAE,uBAAuB;aACvC,CAAC;QACJ,KAAK,YAAY;YACf,OAAO;gBACL,KAAK,EAAE,aAAa;gBACpB,IAAI,EAAE,KAAC,OAAO,IAAC,SAAS,EAAC,sBAAsB,GAAG;gBAClD,aAAa,EAAE,cAAc;aAC9B,CAAC;QACJ,KAAK,WAAW;YACd,OAAO;gBACL,KAAK,EAAE,WAAW;gBAClB,IAAI,EAAE,KAAC,YAAY,IAAC,SAAS,EAAC,SAAS,GAAG;gBAC1C,aAAa,EAAE,oCAAoC;aACpD,CAAC;QACJ,KAAK,QAAQ;YACX,OAAO;gBACL,KAAK,EAAE,QAAQ;gBACf,MAAM,EAAE,IAAI,CAAC,YAAY;gBACzB,IAAI,EAAE,KAAC,WAAW,IAAC,SAAS,EAAC,SAAS,GAAG;gBACzC,aAAa,EAAE,kBAAkB;aAClC,CAAC;QACJ,KAAK,UAAU;YACb,OAAO;gBACL,KAAK,EAAE,UAAU;gBACjB,MAAM,EAAE,IAAI,CAAC,YAAY;gBACzB,IAAI,EAAE,KAAC,OAAO,IAAC,SAAS,EAAC,SAAS,GAAG;gBACrC,aAAa,EAAE,kBAAkB;aAClC,CAAC;IACN,CAAC;AACH,CAAC;AAQD,SAAS,qBAAqB,CAA6B,EACzD,IAAI,EACJ,OAAO,EACP,cAAc,GAC0B;IACxC,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC9B,OAAO,CACL,eAAK,SAAS,EAAC,4BAA4B,aACzC,cAAK,SAAS,EAAE,EAAE,CAAC,iBAAiB,EAAE,IAAI,CAAC,aAAa,CAAC,YAAG,IAAI,CAAC,IAAI,GAAO,EAC5E,eAAK,SAAS,EAAC,gBAAgB,aAC7B,YAAG,SAAS,EAAC,8BAA8B,YAAE,IAAI,CAAC,KAAK,GAAK,EAC5D,YAAG,SAAS,EAAC,wCAAwC,YAAE,IAAI,CAAC,OAAO,GAAK,EACvE,IAAI,CAAC,MAAM,IAAI,CACd,YAAG,SAAS,EAAC,iDAAiD,YAAE,IAAI,CAAC,MAAM,GAAK,CACjF,IACG,EACL,OAAO,IAAI,CACV,KAAC,MAAM,IACL,IAAI,EAAC,MAAM,EACX,OAAO,EAAC,OAAO,EACf,SAAS,EAAC,kBAAkB,EAC5B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,cAAc,gBACZ,SAAS,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,SAAS,YAEtD,KAAC,CAAC,IAAC,SAAS,EAAC,SAAS,GAAG,GAClB,CACV,IACG,CACP,CAAC;AACJ,CAAC;AAwCD,MAAM,UAAU,kBAAkB,CAChC,KAA8B;IAE9B,MAAM,EACJ,aAAa,EACb,WAAW,EACX,iBAAiB,EACjB,YAAY,EACZ,YAAY,GAAG,oBAAoB,GACpC,GAAG,KAAK,CAAC;IACV,MAAM,KAAK,GAAG,sBAAsB,EAAe,CAAC;IAEpD,MAAM,WAAW,GAAG,OAAO,CACzB,GAAG,EAAE,CACH,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC,MAAM,EAC1F,CAAC,KAAK,CAAC,CACR,CAAC;IAEF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,aAAa,GAAc,CAAC,GAAG,EAAE;QACrC,IAAI,YAAY;YAAE,OAAO,YAAY,EAAE,CAAC;QACxC,IAAI,WAAW,IAAI,iBAAiB,EAAE,CAAC;YACrC,OAAO,CACL,cAAK,SAAS,EAAC,KAAK,YACjB,iBAAiB,CAAC;oBACjB,IAAI,EAAE,WAAW;oBACjB,QAAQ,EAAE,CACR,eAAM,SAAS,EAAC,sCAAsC,YAAE,YAAY,GAAQ,CAC7E;iBACF,CAAC,GACE,CACP,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,EAAE,CAAC;IAEL,OAAO,CACL,cAAK,SAAS,EAAC,4BAA4B,YACzC,MAAC,OAAO,eACN,KAAC,cAAc,IAAC,OAAO,kBACrB,MAAC,MAAM,IACL,IAAI,EAAC,MAAM,EACX,OAAO,EAAC,UAAU,EAClB,SAAS,EAAC,2CAA2C,gBAEnD,WAAW,GAAG,CAAC;4BACb,CAAC,CAAC,oBAAoB,WAAW,cAAc;4BAC/C,CAAC,CAAC,oBAAoB,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,aAG5E,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,CACjB,KAAC,OAAO,IAAC,SAAS,EAAC,sBAAsB,GAAG,CAC7C,CAAC,CAAC,CAAC,CACF,KAAC,KAAK,IAAC,SAAS,EAAC,SAAS,GAAG,CAC9B,EACD,eACE,SAAS,EAAC,gKAAgK,iBAC9J,MAAM,YAEjB,KAAK,CAAC,MAAM,GACR,IACA,GACM,EACjB,MAAC,cAAc,IAAC,KAAK,EAAC,OAAO,EAAC,IAAI,EAAC,KAAK,EAAC,SAAS,EAAC,sBAAsB,aACvE,eAAK,SAAS,EAAC,uCAAuC,aACpD,aAAI,SAAS,EAAC,uBAAuB,gCAAqB,EAC1D,eAAM,SAAS,EAAC,+BAA+B,YAC5C,WAAW,GAAG,CAAC;wCACd,CAAC,CAAC,GAAG,WAAW,cAAc;wCAC9B,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,QAAQ,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,GACrD,IACH,EACN,KAAC,SAAS,KAAG,EACb,KAAC,UAAU,IAAC,SAAS,EAAC,UAAU,YAC9B,aAAI,SAAS,EAAC,UAAU,YACrB,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACnB,uBACE,KAAC,qBAAqB,IACpB,IAAI,EAAE,IAAI,EACV,OAAO,EACL,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAE1E,cAAc,EAAE,aAAa,CAAC,SAAS,IAAI,aAAa,CAAC,SAAS,KAAK,IAAI,CAAC,EAAE,GAC9E,IAPK,IAAI,CAAC,EAAE,CAQX,CACN,CAAC,GACC,GACM,EACZ,aAAa,KAAK,IAAI,IAAI,CACzB,8BACE,KAAC,SAAS,KAAG,EACZ,aAAa,IACb,CACJ,IACc,IACT,GACN,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ClearUploadActivityOptions {
|
|
2
|
+
/** Network call performing the clear. Receives the pendingMediaId. */
|
|
3
|
+
clearFn: (pendingMediaId: string) => Promise<void>;
|
|
4
|
+
/** Optional error callback. Receives the error and the pendingMediaId that failed. */
|
|
5
|
+
onError?: (error: Error, pendingMediaId: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function useClearUploadActivity(options: ClearUploadActivityOptions): import("@tanstack/react-query").UseMutationResult<void, Error, string, unknown>;
|
|
8
|
+
//# sourceMappingURL=use-clear-upload-activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-clear-upload-activity.d.ts","sourceRoot":"","sources":["../../src/react/use-clear-upload-activity.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,0BAA0B;IACzC,sEAAsE;IACtE,OAAO,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,sFAAsF;IACtF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1D;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,0BAA0B,mFAQzE"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* Generic mutation hook to clear a terminal pendingMedia item from the tray.
|
|
4
|
+
*
|
|
5
|
+
* Pure adapter: the consumer supplies `clearFn` (typically a wrapped Firebase
|
|
6
|
+
* callable in ttt-prod, or a fetch call elsewhere). The package owns the
|
|
7
|
+
* useMutation wiring and the optional `onError` toast hook; the consumer owns
|
|
8
|
+
* the network call.
|
|
9
|
+
*
|
|
10
|
+
* The provider's Firestore listener observes the `uploadTrayClearedAt` write
|
|
11
|
+
* and removes the item from the tray selector automatically — no optimistic
|
|
12
|
+
* update is needed in this hook.
|
|
13
|
+
*/
|
|
14
|
+
import { useMutation } from '@tanstack/react-query';
|
|
15
|
+
export function useClearUploadActivity(options) {
|
|
16
|
+
const { clearFn, onError } = options;
|
|
17
|
+
return useMutation({
|
|
18
|
+
mutationFn: clearFn,
|
|
19
|
+
onError: (error, pendingMediaId) => {
|
|
20
|
+
onError?.(error, pendingMediaId);
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=use-clear-upload-activity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-clear-upload-activity.js","sourceRoot":"","sources":["../../src/react/use-clear-upload-activity.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AASpD,MAAM,UAAU,sBAAsB,CAAC,OAAmC;IACxE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IACrC,OAAO,WAAW,CAAsB;QACtC,UAAU,EAAE,OAAO;QACnB,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE;YACjC,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;QACnC,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a callable that wraps a navigation action. Calling the returned
|
|
3
|
+
* function with `performNavigation` either invokes it immediately (when no
|
|
4
|
+
* active uploads) or shows a confirmation prompt; if the user confirms,
|
|
5
|
+
* `performNavigation` is invoked.
|
|
6
|
+
*/
|
|
7
|
+
export declare function useGuardedNavigation(): (performNavigation: () => void) => void;
|
|
8
|
+
//# sourceMappingURL=use-guarded-navigation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-guarded-navigation.d.ts","sourceRoot":"","sources":["../../src/react/use-guarded-navigation.ts"],"names":[],"mappings":"AAsBA;;;;;GAKG;AACH,wBAAgB,oBAAoB,IAAI,CAAC,iBAAiB,EAAE,MAAM,IAAI,KAAK,IAAI,CAU9E"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a navigation action so it prompts the user when an upload is in
|
|
4
|
+
* progress. Returns a function that takes the consumer's actual navigation
|
|
5
|
+
* call and either runs it immediately (no active uploads) or shows a confirm()
|
|
6
|
+
* dialog first.
|
|
7
|
+
*
|
|
8
|
+
* Designed to be framework-agnostic — the consumer supplies the navigator
|
|
9
|
+
* (Next.js router.push, React Router navigate, plain location.assign, etc.).
|
|
10
|
+
*
|
|
11
|
+
* Typical use inside a button:
|
|
12
|
+
*
|
|
13
|
+
* const guardedNav = useGuardedNavigation();
|
|
14
|
+
* <Button onClick={() => guardedNav(() => router.push('/somewhere'))}>Go</Button>
|
|
15
|
+
*
|
|
16
|
+
* For links, use the `<GuardedLink>` render-prop wrapper.
|
|
17
|
+
*/
|
|
18
|
+
import { useCallback } from 'react';
|
|
19
|
+
import { useLocalUploadGuard } from './local-upload-guard-provider.js';
|
|
20
|
+
/**
|
|
21
|
+
* Returns a callable that wraps a navigation action. Calling the returned
|
|
22
|
+
* function with `performNavigation` either invokes it immediately (when no
|
|
23
|
+
* active uploads) or shows a confirmation prompt; if the user confirms,
|
|
24
|
+
* `performNavigation` is invoked.
|
|
25
|
+
*/
|
|
26
|
+
export function useGuardedNavigation() {
|
|
27
|
+
const { confirmNavigation } = useLocalUploadGuard();
|
|
28
|
+
return useCallback((performNavigation) => {
|
|
29
|
+
if (confirmNavigation()) {
|
|
30
|
+
performNavigation();
|
|
31
|
+
}
|
|
32
|
+
}, [confirmNavigation]);
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=use-guarded-navigation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-guarded-navigation.js","sourceRoot":"","sources":["../../src/react/use-guarded-navigation.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAEvE;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,EAAE,iBAAiB,EAAE,GAAG,mBAAmB,EAAE,CAAC;IACpD,OAAO,WAAW,CAChB,CAAC,iBAA6B,EAAE,EAAE;QAChC,IAAI,iBAAiB,EAAE,EAAE,CAAC;YACxB,iBAAiB,EAAE,CAAC;QACtB,CAAC;IACH,CAAC,EACD,CAAC,iBAAiB,CAAC,CACpB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { FirebaseStorage } from 'firebase/storage';
|
|
2
|
+
import type { UploadState } from '@ttt-productions/media-schemas';
|
|
3
|
+
export interface GuardedUploadArgs {
|
|
4
|
+
storage: FirebaseStorage;
|
|
5
|
+
path: string;
|
|
6
|
+
file: File;
|
|
7
|
+
metadata: {
|
|
8
|
+
contentType: string;
|
|
9
|
+
};
|
|
10
|
+
/** Stable identifier for the in-flight upload (used to register/unregister the local-upload guard). */
|
|
11
|
+
uploadId: string;
|
|
12
|
+
/** Optional progress callback. Fires with preparing → uploading (with percent) → finalizing → null on completion. */
|
|
13
|
+
onProgress?: (state: UploadState | null) => void;
|
|
14
|
+
/**
|
|
15
|
+
* Optional cancellation signal. When aborted, the in-flight Storage upload
|
|
16
|
+
* is canceled and `guardedUpload` rejects with the underlying AbortError.
|
|
17
|
+
* Callers should treat AbortError as a user-initiated cancel (not a real
|
|
18
|
+
* failure) — typically by checking `error.name === 'AbortError'` in the
|
|
19
|
+
* mutation's onError and swallowing toasts/error UI for that case.
|
|
20
|
+
*/
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Wraps `uploadFileResumable` with the canonical local-upload guard + phase reporting.
|
|
25
|
+
*
|
|
26
|
+
* Every upload-capable mutation hook MUST use this helper instead of calling
|
|
27
|
+
* `uploadFileResumable` directly. The helper owns:
|
|
28
|
+
* - phase reporting: preparing → uploading (with percent) → finalizing
|
|
29
|
+
* - beforeunload guard registration during the upload window only
|
|
30
|
+
* - error safety: the guard is unregistered in a `finally` block
|
|
31
|
+
*
|
|
32
|
+
* The helper does NOT call `onProgress?.(null)` — callers do that from the
|
|
33
|
+
* mutation's `onSuccess` and `onError` callbacks (because the upload window
|
|
34
|
+
* ends when the upload completes, but the mutation may have more work after).
|
|
35
|
+
*/
|
|
36
|
+
export declare function useGuardedUpload(): (args: GuardedUploadArgs) => Promise<void>;
|
|
37
|
+
//# sourceMappingURL=use-guarded-upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-guarded-upload.d.ts","sourceRoot":"","sources":["../../src/react/use-guarded-upload.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAGlE,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,eAAe,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,uGAAuG;IACvG,QAAQ,EAAE,MAAM,CAAC;IACjB,qHAAqH;IACrH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,WAGI,iBAAiB,KAAG,OAAO,CAAC,IAAI,CAAC,CAmBpE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import { uploadFileResumable } from '@ttt-productions/upload-core/browser';
|
|
3
|
+
import { useLocalUploadGuard } from './local-upload-guard-provider.js';
|
|
4
|
+
/**
|
|
5
|
+
* Wraps `uploadFileResumable` with the canonical local-upload guard + phase reporting.
|
|
6
|
+
*
|
|
7
|
+
* Every upload-capable mutation hook MUST use this helper instead of calling
|
|
8
|
+
* `uploadFileResumable` directly. The helper owns:
|
|
9
|
+
* - phase reporting: preparing → uploading (with percent) → finalizing
|
|
10
|
+
* - beforeunload guard registration during the upload window only
|
|
11
|
+
* - error safety: the guard is unregistered in a `finally` block
|
|
12
|
+
*
|
|
13
|
+
* The helper does NOT call `onProgress?.(null)` — callers do that from the
|
|
14
|
+
* mutation's `onSuccess` and `onError` callbacks (because the upload window
|
|
15
|
+
* ends when the upload completes, but the mutation may have more work after).
|
|
16
|
+
*/
|
|
17
|
+
export function useGuardedUpload() {
|
|
18
|
+
const { registerUpload, unregisterUpload } = useLocalUploadGuard();
|
|
19
|
+
return useCallback(async (args) => {
|
|
20
|
+
const { uploadId, onProgress, storage, path, file, metadata, signal } = args;
|
|
21
|
+
onProgress?.({ phase: 'preparing', percent: null });
|
|
22
|
+
registerUpload(uploadId);
|
|
23
|
+
try {
|
|
24
|
+
await uploadFileResumable({
|
|
25
|
+
storage,
|
|
26
|
+
path,
|
|
27
|
+
file,
|
|
28
|
+
metadata,
|
|
29
|
+
signal,
|
|
30
|
+
onProgress: ({ percent }) => onProgress?.({ phase: 'uploading', percent }),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
unregisterUpload(uploadId);
|
|
35
|
+
}
|
|
36
|
+
onProgress?.({ phase: 'finalizing', percent: null });
|
|
37
|
+
}, [registerUpload, unregisterUpload]);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=use-guarded-upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-guarded-upload.js","sourceRoot":"","sources":["../../src/react/use-guarded-upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAG3E,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAqBvE;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB;IAC5B,MAAM,EAAE,cAAc,EAAE,gBAAgB,EAAE,GAAG,mBAAmB,EAAE,CAAC;IAEnE,OAAO,WAAW,CAAC,KAAK,EAAE,IAAuB,EAAiB,EAAE;QAChE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAE7E,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzB,IAAI,CAAC;YACD,MAAM,mBAAmB,CAAC;gBACtB,OAAO;gBACP,IAAI;gBACJ,IAAI;gBACJ,QAAQ;gBACR,MAAM;gBACN,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;aAC7E,CAAC,CAAC;QACP,CAAC;gBAAS,CAAC;YACP,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;QACD,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC,EAAE,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface UploadProcessingState {
|
|
2
|
+
status: 'idle' | 'pending' | 'processing' | 'success' | 'failed' | 'rejected-text' | 'rejected-media';
|
|
3
|
+
message: string;
|
|
4
|
+
errorMessage?: string;
|
|
5
|
+
isProcessing: boolean;
|
|
6
|
+
isComplete: boolean;
|
|
7
|
+
isFailed: boolean;
|
|
8
|
+
isRejected: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface UseUploadProcessingOptions {
|
|
11
|
+
/** Override the processing-phase message. */
|
|
12
|
+
processingMessage?: string;
|
|
13
|
+
/** Override the success-phase message. */
|
|
14
|
+
successMessage?: string;
|
|
15
|
+
/** Override the failed-phase message. */
|
|
16
|
+
failedMessage?: string;
|
|
17
|
+
/** Override the rejected-phase message. */
|
|
18
|
+
rejectedMessage?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Returns local UI processing state for the given pendingMediaId. If the
|
|
22
|
+
* `onTerminal` callback is provided, it fires once when the doc reaches a
|
|
23
|
+
* terminal status (completed / failed / rejected). Use `onTerminal` for
|
|
24
|
+
* local UI cleanup — global toast/dialog routing belongs in the provider.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useUploadProcessing<TFileOrigin extends string = string>(pendingMediaId: string | null | undefined, onTerminal?: (state: UploadProcessingState) => void, options?: UseUploadProcessingOptions): UploadProcessingState;
|
|
27
|
+
//# sourceMappingURL=use-upload-processing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-upload-processing.d.ts","sourceRoot":"","sources":["../../src/react/use-upload-processing.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,eAAe,GAAG,gBAAgB,CAAC;IACtG,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;CACrB;AAOD,MAAM,WAAW,0BAA0B;IACzC,6CAA6C;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,0CAA0C;IAC1C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2CAA2C;IAC3C,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAyED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,SAAS,MAAM,GAAG,MAAM,EACrE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACzC,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,EACnD,OAAO,GAAE,0BAA+B,GACvC,qBAAqB,CAqBvB"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* Adapter that maps `useInFlightUpload` state to the legacy
|
|
4
|
+
* `{ isProcessing, message, isComplete, isRejected, isFailed }` shape used
|
|
5
|
+
* by per-component spinners and overlays.
|
|
6
|
+
*
|
|
7
|
+
* Routing of toasts and rejection dialogs lives in `InFlightUploadsProvider`
|
|
8
|
+
* via the consumer-supplied callbacks — `useUploadProcessing` only surfaces
|
|
9
|
+
* local UI state. The `onTerminal` callback fires once per terminal status
|
|
10
|
+
* (completed / failed / rejected) and is intended for local UI cleanup
|
|
11
|
+
* (clearing form state, closing modals, etc.).
|
|
12
|
+
*/
|
|
13
|
+
import { useEffect, useRef } from 'react';
|
|
14
|
+
import { useInFlightUpload } from './in-flight-uploads-provider.js';
|
|
15
|
+
const DEFAULT_PROCESSING_MESSAGE = 'Processing your content...';
|
|
16
|
+
const DEFAULT_SUCCESS_MESSAGE = 'Complete!';
|
|
17
|
+
const DEFAULT_FAILED_MESSAGE = 'Processing failed';
|
|
18
|
+
const DEFAULT_REJECTED_MESSAGE = 'Content rejected';
|
|
19
|
+
function buildState(upload, opts) {
|
|
20
|
+
const processingMessage = opts.processingMessage ?? DEFAULT_PROCESSING_MESSAGE;
|
|
21
|
+
const successMessage = opts.successMessage ?? DEFAULT_SUCCESS_MESSAGE;
|
|
22
|
+
const failedMessage = opts.failedMessage ?? DEFAULT_FAILED_MESSAGE;
|
|
23
|
+
const rejectedMessage = opts.rejectedMessage ?? DEFAULT_REJECTED_MESSAGE;
|
|
24
|
+
if (!upload) {
|
|
25
|
+
return {
|
|
26
|
+
status: 'idle',
|
|
27
|
+
message: '',
|
|
28
|
+
isProcessing: false,
|
|
29
|
+
isComplete: false,
|
|
30
|
+
isFailed: false,
|
|
31
|
+
isRejected: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (upload.status === 'pending' || upload.status === 'processing') {
|
|
35
|
+
return {
|
|
36
|
+
status: upload.status,
|
|
37
|
+
message: processingMessage,
|
|
38
|
+
isProcessing: true,
|
|
39
|
+
isComplete: false,
|
|
40
|
+
isFailed: false,
|
|
41
|
+
isRejected: false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (upload.status === 'completed') {
|
|
45
|
+
return {
|
|
46
|
+
status: 'success',
|
|
47
|
+
message: successMessage,
|
|
48
|
+
isProcessing: false,
|
|
49
|
+
isComplete: true,
|
|
50
|
+
isFailed: false,
|
|
51
|
+
isRejected: false,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
if (upload.status === 'failed') {
|
|
55
|
+
return {
|
|
56
|
+
status: 'failed',
|
|
57
|
+
message: failedMessage,
|
|
58
|
+
errorMessage: upload.errorMessage,
|
|
59
|
+
isProcessing: false,
|
|
60
|
+
isComplete: false,
|
|
61
|
+
isFailed: true,
|
|
62
|
+
isRejected: false,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (upload.status === 'rejected') {
|
|
66
|
+
return {
|
|
67
|
+
status: upload.rejectionType === 'text' ? 'rejected-text' : 'rejected-media',
|
|
68
|
+
message: rejectedMessage,
|
|
69
|
+
errorMessage: upload.errorMessage,
|
|
70
|
+
isProcessing: false,
|
|
71
|
+
isComplete: false,
|
|
72
|
+
isFailed: false,
|
|
73
|
+
isRejected: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
status: 'processing',
|
|
78
|
+
message: processingMessage,
|
|
79
|
+
isProcessing: true,
|
|
80
|
+
isComplete: false,
|
|
81
|
+
isFailed: false,
|
|
82
|
+
isRejected: false,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Returns local UI processing state for the given pendingMediaId. If the
|
|
87
|
+
* `onTerminal` callback is provided, it fires once when the doc reaches a
|
|
88
|
+
* terminal status (completed / failed / rejected). Use `onTerminal` for
|
|
89
|
+
* local UI cleanup — global toast/dialog routing belongs in the provider.
|
|
90
|
+
*/
|
|
91
|
+
export function useUploadProcessing(pendingMediaId, onTerminal, options = {}) {
|
|
92
|
+
const upload = useInFlightUpload(pendingMediaId);
|
|
93
|
+
const state = buildState(upload, options);
|
|
94
|
+
const lastFiredRef = useRef(null);
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!onTerminal)
|
|
97
|
+
return;
|
|
98
|
+
if (!pendingMediaId) {
|
|
99
|
+
lastFiredRef.current = null;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (state.isComplete || state.isFailed || state.isRejected) {
|
|
103
|
+
const sig = `${pendingMediaId}:${state.status}`;
|
|
104
|
+
if (lastFiredRef.current !== sig) {
|
|
105
|
+
lastFiredRef.current = sig;
|
|
106
|
+
onTerminal(state);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}, [pendingMediaId, state.status, state.isComplete, state.isFailed, state.isRejected, state, onTerminal]);
|
|
110
|
+
return state;
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=use-upload-processing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-upload-processing.js","sourceRoot":"","sources":["../../src/react/use-upload-processing.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ttt-productions/upload-ui",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Upload UI primitives: local upload guard provider, useGuardedUpload helper, DeferredUploadFormShell for forms that defer Firebase Storage upload until form submit.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/ttt-productions/ttt-packages.git",
|
|
8
|
+
"directory": "packages/upload-ui"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"main": "dist/index.js",
|
|
12
|
+
"module": "dist/index.js",
|
|
13
|
+
"types": "dist/index.d.ts",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"exports": {
|
|
19
|
+
".": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./react": {
|
|
24
|
+
"types": "./dist/react/index.d.ts",
|
|
25
|
+
"default": "./dist/react/index.js"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"clean": "rm -rf dist *.tsbuildinfo",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@ttt-productions/file-input": "*",
|
|
36
|
+
"@ttt-productions/media-schemas": "*",
|
|
37
|
+
"@ttt-productions/query-core": "*",
|
|
38
|
+
"@ttt-productions/ui-core": "*",
|
|
39
|
+
"@ttt-productions/upload-core": "*"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@tanstack/react-query": ">=5.0.0",
|
|
43
|
+
"firebase": ">=10.0.0",
|
|
44
|
+
"lucide-react": ">=0.300.0",
|
|
45
|
+
"react": ">=18.0.0",
|
|
46
|
+
"react-dom": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"typescript": "^5.8.3"
|
|
50
|
+
},
|
|
51
|
+
"author": "DJ (TTT Productions)",
|
|
52
|
+
"license": "MIT"
|
|
53
|
+
}
|