@px-ui/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,39 @@
1
+ @font-face {
2
+ font-family: "Ubuntu L";
3
+ src: url("./Ubuntu-Light.ttf");
4
+ }
5
+
6
+ @font-face {
7
+ font-family: "Ubuntu R";
8
+ src: url("./Ubuntu-Regular.ttf");
9
+ }
10
+
11
+ @font-face {
12
+ font-family: "Ubuntu B";
13
+ src: url("./Ubuntu-Bold.ttf");
14
+ }
15
+
16
+ @font-face {
17
+ font-family: "Lato L";
18
+ src: url("./Lato-Light.ttf");
19
+ }
20
+
21
+ @font-face {
22
+ font-family: "Lato R";
23
+ src: url("./Lato-Regular.ttf");
24
+ }
25
+
26
+ @font-face {
27
+ font-family: "Lato B";
28
+ src: url("./Lato-Bold.ttf");
29
+ }
30
+
31
+ @font-face {
32
+ font-family: "Lato SB";
33
+ src: url("./Lato-SemiBold.ttf");
34
+ }
35
+
36
+ @font-face {
37
+ font-family: "Cedarville Cursive";
38
+ src: url("./CedarvilleCursive-Regular.ttf");
39
+ }
package/dist/index.css ADDED
@@ -0,0 +1,133 @@
1
+ @import "tailwindcss" prefix(ppx);
2
+ @import "tw-animate-css";
3
+ @import "shadcn/tailwind.css";
4
+
5
+ @custom-variant dark (&:is(.dark *));
6
+ @source "../**/*.{js,jsx,ts,tsx}";
7
+
8
+ @theme inline {
9
+ --font-heading: var(--font-sans);
10
+ --color-sidebar-ring: var(--sidebar-ring);
11
+ --color-sidebar-border: var(--sidebar-border);
12
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
13
+ --color-sidebar-accent: var(--sidebar-accent);
14
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
15
+ --color-sidebar-primary: var(--sidebar-primary);
16
+ --color-sidebar-foreground: var(--sidebar-foreground);
17
+ --color-sidebar: var(--sidebar);
18
+ --color-chart-5: var(--chart-5);
19
+ --color-chart-4: var(--chart-4);
20
+ --color-chart-3: var(--chart-3);
21
+ --color-chart-2: var(--chart-2);
22
+ --color-chart-1: var(--chart-1);
23
+ --color-ring: var(--ring);
24
+ --color-input: var(--input);
25
+ --color-border: var(--border);
26
+ --color-destructive: var(--destructive);
27
+ --color-accent-foreground: var(--accent-foreground);
28
+ --color-accent: var(--accent);
29
+ --color-muted-foreground: var(--muted-foreground);
30
+ --color-muted: var(--muted);
31
+ --color-secondary-foreground: var(--secondary-foreground);
32
+ --color-secondary: var(--secondary);
33
+ --color-primary-foreground: var(--primary-foreground);
34
+ --color-primary: var(--primary);
35
+ --color-popover-foreground: var(--popover-foreground);
36
+ --color-popover: var(--popover);
37
+ --color-card-foreground: var(--card-foreground);
38
+ --color-card: var(--card);
39
+ --color-foreground: var(--foreground);
40
+ --color-background: var(--background);
41
+ --radius-sm: calc(var(--radius) * 0.6);
42
+ --radius-md: calc(var(--radius) * 0.8);
43
+ --radius-lg: var(--radius);
44
+ --radius-xl: calc(var(--radius) * 1.4);
45
+ --radius-2xl: calc(var(--radius) * 1.8);
46
+ --radius-3xl: calc(var(--radius) * 2.2);
47
+ --radius-4xl: calc(var(--radius) * 2.6);
48
+ --font-sans-light: "Lato L", system-ui, sans-serif;
49
+ --font-sans: "Lato R", system-ui, sans-serif;
50
+ --font-sans-sb: "Lato SB", system-ui, sans-serif;
51
+ --font-sans-b: "Lato B", system-ui, sans-serif;
52
+ }
53
+
54
+ :root {
55
+ --background: oklch(1 0 0);
56
+ --foreground: oklch(0.141 0.005 285.823);
57
+ --card: oklch(1 0 0);
58
+ --card-foreground: oklch(0.141 0.005 285.823);
59
+ --popover: oklch(1 0 0);
60
+ --popover-foreground: oklch(0.141 0.005 285.823);
61
+ --primary: oklch(0.527 0.154 150.069);
62
+ --primary-foreground: oklch(0.982 0.018 155.826);
63
+ --secondary: oklch(0.967 0.001 286.375);
64
+ --secondary-foreground: oklch(0.21 0.006 285.885);
65
+ --muted: oklch(0.967 0.001 286.375);
66
+ --muted-foreground: oklch(0.552 0.016 285.938);
67
+ --accent: oklch(0.527 0.154 150.069);
68
+ --accent-foreground: oklch(0.982 0.018 155.826);
69
+ --destructive: oklch(0.577 0.245 27.325);
70
+ --border: oklch(0.92 0.004 286.32);
71
+ --input: oklch(0.92 0.004 286.32);
72
+ --ring: oklch(0.705 0.015 286.067);
73
+ --chart-1: oklch(0.871 0.006 286.286);
74
+ --chart-2: oklch(0.552 0.016 285.938);
75
+ --chart-3: oklch(0.442 0.017 285.786);
76
+ --chart-4: oklch(0.37 0.013 285.805);
77
+ --chart-5: oklch(0.274 0.006 286.033);
78
+ --radius: 0.625rem;
79
+ --sidebar: oklch(0.985 0 0);
80
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
81
+ --sidebar-primary: oklch(0.627 0.194 149.214);
82
+ --sidebar-primary-foreground: oklch(0.982 0.018 155.826);
83
+ --sidebar-accent: oklch(0.967 0.001 286.375);
84
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
85
+ --sidebar-border: oklch(0.92 0.004 286.32);
86
+ --sidebar-ring: oklch(0.705 0.015 286.067);
87
+ }
88
+
89
+ .dark {
90
+ --background: oklch(0.141 0.005 285.823);
91
+ --foreground: oklch(0.985 0 0);
92
+ --card: oklch(0.21 0.006 285.885);
93
+ --card-foreground: oklch(0.985 0 0);
94
+ --popover: oklch(0.21 0.006 285.885);
95
+ --popover-foreground: oklch(0.985 0 0);
96
+ --primary: oklch(0.448 0.119 151.328);
97
+ --primary-foreground: oklch(0.982 0.018 155.826);
98
+ --secondary: oklch(0.274 0.006 286.033);
99
+ --secondary-foreground: oklch(0.985 0 0);
100
+ --muted: oklch(0.274 0.006 286.033);
101
+ --muted-foreground: oklch(0.705 0.015 286.067);
102
+ --accent: oklch(0.448 0.119 151.328);
103
+ --accent-foreground: oklch(0.982 0.018 155.826);
104
+ --destructive: oklch(0.704 0.191 22.216);
105
+ --border: oklch(1 0 0 / 10%);
106
+ --input: oklch(1 0 0 / 15%);
107
+ --ring: oklch(0.552 0.016 285.938);
108
+ --chart-1: oklch(0.871 0.006 286.286);
109
+ --chart-2: oklch(0.552 0.016 285.938);
110
+ --chart-3: oklch(0.442 0.017 285.786);
111
+ --chart-4: oklch(0.37 0.013 285.805);
112
+ --chart-5: oklch(0.274 0.006 286.033);
113
+ --sidebar: oklch(0.21 0.006 285.885);
114
+ --sidebar-foreground: oklch(0.985 0 0);
115
+ --sidebar-primary: oklch(0.723 0.219 149.579);
116
+ --sidebar-primary-foreground: oklch(0.982 0.018 155.826);
117
+ --sidebar-accent: oklch(0.274 0.006 286.033);
118
+ --sidebar-accent-foreground: oklch(0.985 0 0);
119
+ --sidebar-border: oklch(1 0 0 / 10%);
120
+ --sidebar-ring: oklch(0.552 0.016 285.938);
121
+ }
122
+
123
+ @layer base {
124
+ * {
125
+ @apply border-border outline-ring/50;
126
+ }
127
+ body {
128
+ @apply bg-background text-foreground;
129
+ }
130
+ button:not(:disabled), [role="button"]:not(:disabled) {
131
+ cursor: pointer;
132
+ }
133
+ }
@@ -0,0 +1,229 @@
1
+ import { Button as Button$1 } from "@base-ui/react/button";
2
+ import { VariantProps } from "class-variance-authority";
3
+ import { ClassValue } from "clsx";
4
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
5
+ import { ChangeEvent, DragEvent, InputHTMLAttributes } from "react";
6
+ import * as _tanstack_react_query0 from "@tanstack/react-query";
7
+ import { QueryKey } from "@tanstack/react-query";
8
+ import * as class_variance_authority_types0 from "class-variance-authority/types";
9
+
10
+ //#region src/components/button.d.ts
11
+ declare const buttonVariants: (props?: ({
12
+ variant?: "default" | "outline" | "secondary" | "ghost" | "destructive" | "link" | null | undefined;
13
+ size?: "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | undefined;
14
+ } & class_variance_authority_types0.ClassProp) | undefined) => string;
15
+ declare function Button({
16
+ className,
17
+ variant,
18
+ size,
19
+ ...props
20
+ }: Button$1.Props & VariantProps<typeof buttonVariants>): react_jsx_runtime0.JSX.Element;
21
+ //#endregion
22
+ //#region src/lib/utils.d.ts
23
+ declare function cn(...inputs: ClassValue[]): string;
24
+ //#endregion
25
+ //#region src/providers/px-ui-provider.d.ts
26
+ declare function PXUIProvider(props: React.PropsWithChildren): react_jsx_runtime0.JSX.Element;
27
+ //#endregion
28
+ //#region src/hooks/use-debounce.d.ts
29
+ declare function useDebounce<T>(callback: () => void, delay: number, deps: React.DependencyList): void;
30
+ //#endregion
31
+ //#region src/hooks/use-intersection-observer.d.ts
32
+ interface UseIntersectionObserverOptions {
33
+ threshold?: number | number[];
34
+ root?: Element | Document | null;
35
+ rootMargin?: string;
36
+ }
37
+ type UseIntersectionObserverReturn = [(node: Element | null) => void, IntersectionObserverEntry | null];
38
+ declare function useIntersectionObserver(options?: UseIntersectionObserverOptions): UseIntersectionObserverReturn;
39
+ //#endregion
40
+ //#region src/hooks/use-infinite-scroll.d.ts
41
+ type UseInfiniteScrollHookArgs = Pick<UseIntersectionObserverOptions, "rootMargin" | "threshold"> & {
42
+ isLoadingMore: boolean;
43
+ hasMore: boolean;
44
+ onLoadMore: () => unknown;
45
+ disabled?: boolean;
46
+ };
47
+ declare function useInfiniteScroll({
48
+ isLoadingMore,
49
+ hasMore,
50
+ onLoadMore,
51
+ rootMargin,
52
+ disabled,
53
+ threshold
54
+ }: UseInfiniteScrollHookArgs): readonly [(node: Element | null) => void];
55
+ //#endregion
56
+ //#region src/hooks/use-async-options.d.ts
57
+ type LoadOptionsData<TData$1> = {
58
+ options: TData$1[];
59
+ hasMore: boolean;
60
+ };
61
+ type LoadOptionsReturn<TData$1> = {
62
+ data: LoadOptionsData<TData$1>;
63
+ error: any;
64
+ };
65
+ type LoadOptionsFn<TData$1> = (params: {
66
+ search: string;
67
+ page: number;
68
+ signal: AbortSignal;
69
+ }) => Promise<LoadOptionsReturn<TData$1>>;
70
+ declare function useAsyncOptions<TData$1 = any>(params: {
71
+ cacheKey: QueryKey;
72
+ loader: LoadOptionsFn<TData$1>;
73
+ }, {
74
+ isOpen,
75
+ inputValue
76
+ }: {
77
+ isOpen: boolean;
78
+ inputValue: string;
79
+ }): {
80
+ items: TData$1[] | undefined;
81
+ isLoading: boolean;
82
+ isLoadingMore: boolean;
83
+ hasMore: boolean;
84
+ isError: boolean;
85
+ onLoadMore: () => void;
86
+ _query: _tanstack_react_query0.UseInfiniteQueryResult<_tanstack_react_query0.InfiniteData<LoadOptionsData<TData$1>, unknown>, Error>;
87
+ };
88
+ type LoadOptionsConfig<TData$1> = {
89
+ cacheKey: QueryKey;
90
+ loader: LoadOptionsFn<TData$1>;
91
+ };
92
+ declare function defineLoadOptions<TData$1 = any>(loadOptions: LoadOptionsConfig<TData$1>): LoadOptionsConfig<TData$1>;
93
+ type InferOption<T> = T extends LoadOptionsConfig<infer TData> ? TData : never;
94
+ type InferOptions<T> = T extends LoadOptionsConfig<infer TData> ? TData[] : never;
95
+ //#endregion
96
+ //#region src/hooks/use-file-upload.d.ts
97
+ type FileMetadata = {
98
+ name: string;
99
+ size: number;
100
+ type: string;
101
+ url: string;
102
+ id: string;
103
+ };
104
+ type UploadStatus = "idle" | "uploading" | "complete" | "error";
105
+ type FileUploadItem = {
106
+ file: File | FileMetadata;
107
+ id: string;
108
+ preview?: string;
109
+ progress: number;
110
+ status: UploadStatus;
111
+ error?: string;
112
+ uploadedUrl?: string;
113
+ };
114
+ type PresignedUrlResponse = {
115
+ /** The S3 bucket URL to POST to */
116
+ url: string;
117
+ /** The full accessible path after upload (may include signed query params) */
118
+ full_path: string;
119
+ /** The S3 object key (e.g., "uuid/filename.pdf") */
120
+ key?: string;
121
+ /** Access control level */
122
+ acl?: string;
123
+ /** Expected response status from S3 */
124
+ success_action_status?: string;
125
+ /** Content-Type for the upload */
126
+ "Content-Type"?: string;
127
+ /** Base64-encoded policy document */
128
+ policy?: string;
129
+ /** AWS credential string */
130
+ "x-amz-credential"?: string;
131
+ /** AWS signing algorithm */
132
+ "x-amz-algorithm"?: string;
133
+ /** AWS request date */
134
+ "x-amz-date"?: string;
135
+ /** AWS request signature */
136
+ "x-amz-signature"?: string;
137
+ /** Additional fields */
138
+ [key: string]: unknown;
139
+ };
140
+ type UploadConfig = {
141
+ /**
142
+ * Function to get a presigned URL for uploading
143
+ */
144
+ getPresignedUrl: (params: {
145
+ filename: string;
146
+ contentType: string;
147
+ size: number;
148
+ signal?: AbortSignal;
149
+ }) => Promise<{
150
+ result?: PresignedUrlResponse | null;
151
+ error?: unknown;
152
+ }>;
153
+ /**
154
+ * Function to upload the file to the storage
155
+ */
156
+ uploadFile: (url: string, file: File, presignedData: PresignedUrlResponse, onProgress?: (progress: number) => void, signal?: AbortSignal) => Promise<{
157
+ result?: {
158
+ url: string;
159
+ [key: string]: unknown;
160
+ };
161
+ error?: unknown;
162
+ }>;
163
+ /**
164
+ * Whether to auto-upload files when they are added
165
+ * @default true
166
+ */
167
+ autoUpload?: boolean;
168
+ /**
169
+ * Called when a single file upload completes
170
+ */
171
+ onUploadComplete?: (file: FileUploadItem) => void;
172
+ /**
173
+ * Called when a single file upload fails
174
+ */
175
+ onUploadError?: (file: FileUploadItem, error: unknown) => void;
176
+ /**
177
+ * Called when all files finish uploading
178
+ */
179
+ onAllUploadsComplete?: (files: FileUploadItem[]) => void;
180
+ };
181
+ type UseFileUploadOptions = {
182
+ maxFiles?: number;
183
+ maxSize?: number;
184
+ minSize?: number;
185
+ accept?: string;
186
+ multiple?: boolean;
187
+ initialFiles?: FileMetadata[];
188
+ onFilesChange?: (files: FileUploadItem[]) => void;
189
+ onFileAdd?: (addedFile: FileUploadItem) => void;
190
+ onFileRemove?: (removedFile: FileUploadItem) => void;
191
+ upload?: UploadConfig;
192
+ };
193
+ type UseFileUploadReturn = {
194
+ files: FileUploadItem[];
195
+ isDragging: boolean;
196
+ isUploading: boolean;
197
+ errors: string[];
198
+ addFiles: (files: FileList | File[]) => void;
199
+ removeFile: (id: string) => void;
200
+ clearFiles: () => void;
201
+ clearErrors: () => void;
202
+ reset: (newFiles?: FileMetadata[]) => void;
203
+ setFiles: (files: FileUploadItem[] | ((prev: FileUploadItem[]) => FileUploadItem[])) => void;
204
+ instanceKey: number;
205
+ uploadFiles: (files?: FileUploadItem[]) => Promise<FileUploadItem[]>;
206
+ retryUpload: (id: string) => Promise<void>;
207
+ cancelUpload: (id: string) => void;
208
+ handleDragEnter: (e: DragEvent<HTMLElement>) => void;
209
+ handleDragLeave: (e: DragEvent<HTMLElement>) => void;
210
+ handleDragOver: (e: DragEvent<HTMLElement>) => void;
211
+ handleDrop: (e: DragEvent<HTMLElement>) => void;
212
+ handleFileChange: (e: ChangeEvent<HTMLInputElement>) => void;
213
+ openFileDialog: () => void;
214
+ getInputProps: (props?: InputHTMLAttributes<HTMLInputElement>) => InputHTMLAttributes<HTMLInputElement> & {
215
+ ref: any;
216
+ };
217
+ };
218
+ declare const formatBytes: (bytes: number, decimals?: number) => string;
219
+ declare const useFileUpload: (options?: UseFileUploadOptions) => UseFileUploadReturn;
220
+ type FileWithPreview = FileUploadItem;
221
+ type FileWithUploadStatus = FileUploadItem;
222
+ type FileUploadOptions = UseFileUploadOptions;
223
+ type FileUploadWithUploaderOptions = UseFileUploadOptions;
224
+ //#endregion
225
+ //#region src/hooks/use-mobile.d.ts
226
+ declare function useIsMobile(): boolean;
227
+ //#endregion
228
+ export { Button, FileMetadata, FileUploadItem, FileUploadOptions, FileUploadWithUploaderOptions, FileWithPreview, FileWithUploadStatus, InferOption, InferOptions, LoadOptionsConfig, PXUIProvider, PresignedUrlResponse, UploadConfig, UploadStatus, UseFileUploadOptions, UseFileUploadReturn, UseIntersectionObserverOptions, UseIntersectionObserverReturn, buttonVariants, cn, defineLoadOptions, formatBytes, useAsyncOptions, useDebounce, useFileUpload, useInfiniteScroll, useIntersectionObserver, useIsMobile };
229
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/components/button.tsx","../src/lib/utils.ts","../src/providers/px-ui-provider.tsx","../src/hooks/use-debounce.ts","../src/hooks/use-intersection-observer.ts","../src/hooks/use-infinite-scroll.ts","../src/hooks/use-async-options.ts","../src/hooks/use-file-upload.ts","../src/hooks/use-mobile.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,603 @@
1
+ import { Button as Button$1 } from "@base-ui/react/button";
2
+ import { cva } from "class-variance-authority";
3
+ import { clsx } from "clsx";
4
+ import { twMerge } from "tailwind-merge";
5
+ import { jsx } from "react/jsx-runtime";
6
+ import * as React$1 from "react";
7
+ import React, { createContext, useCallback, useEffect, useEffectEvent, useRef, useState } from "react";
8
+ import { useInfiniteQuery } from "@tanstack/react-query";
9
+
10
+ //#region src/lib/utils.ts
11
+ function cn(...inputs) {
12
+ return twMerge(clsx(inputs));
13
+ }
14
+
15
+ //#endregion
16
+ //#region src/components/button.tsx
17
+ const buttonVariants = cva("ppx:group/button ppx:inline-flex ppx:shrink-0 ppx:items-center ppx:justify-center ppx:rounded-md ppx:border ppx:border-transparent ppx:bg-clip-padding ppx:text-sm ppx:font-medium ppx:whitespace-nowrap ppx:transition-all ppx:outline-none ppx:select-none ppx:focus-visible:border-ring ppx:focus-visible:ring-3 ppx:focus-visible:ring-ring/50 ppx:active:not-aria-[haspopup]:translate-y-px ppx:disabled:pointer-events-none ppx:disabled:opacity-50 ppx:aria-invalid:border-destructive ppx:aria-invalid:ring-3 ppx:aria-invalid:ring-destructive/20 ppx:dark:aria-invalid:border-destructive/50 ppx:dark:aria-invalid:ring-destructive/40 ppx:[&_svg]:pointer-events-none ppx:[&_svg]:shrink-0 ppx:[&_svg:not([class*=size-])]:size-4", {
18
+ variants: {
19
+ variant: {
20
+ default: "ppx:bg-primary ppx:text-primary-foreground ppx:hover:bg-primary/80",
21
+ outline: "ppx:border-border ppx:bg-background ppx:shadow-xs ppx:hover:bg-muted ppx:hover:text-foreground ppx:aria-expanded:bg-muted ppx:aria-expanded:text-foreground ppx:dark:border-input ppx:dark:bg-input/30 ppx:dark:hover:bg-input/50",
22
+ secondary: "ppx:bg-secondary ppx:text-secondary-foreground ppx:hover:bg-secondary/80 ppx:aria-expanded:bg-secondary ppx:aria-expanded:text-secondary-foreground",
23
+ ghost: "ppx:hover:bg-muted ppx:hover:text-foreground ppx:aria-expanded:bg-muted ppx:aria-expanded:text-foreground ppx:dark:hover:bg-muted/50",
24
+ destructive: "ppx:bg-destructive/10 ppx:text-destructive ppx:hover:bg-destructive/20 ppx:focus-visible:border-destructive/40 ppx:focus-visible:ring-destructive/20 ppx:dark:bg-destructive/20 ppx:dark:hover:bg-destructive/30 ppx:dark:focus-visible:ring-destructive/40",
25
+ link: "ppx:text-primary ppx:underline-offset-4 ppx:hover:underline"
26
+ },
27
+ size: {
28
+ default: "ppx:h-9 ppx:gap-1.5 ppx:px-2.5 ppx:in-data-[slot=button-group]:rounded-md ppx:has-data-[icon=inline-end]:pe-2 ppx:has-data-[icon=inline-start]:ps-2",
29
+ xs: "ppx:h-6 ppx:gap-1 ppx:rounded-[min(var(--radius-md),8px)] ppx:px-2 ppx:text-xs ppx:in-data-[slot=button-group]:rounded-md ppx:has-data-[icon=inline-end]:pe-1.5 ppx:has-data-[icon=inline-start]:ps-1.5 ppx:[&_svg:not([class*=size-])]:size-3",
30
+ sm: "ppx:h-8 ppx:gap-1 ppx:rounded-[min(var(--radius-md),10px)] ppx:px-2.5 ppx:in-data-[slot=button-group]:rounded-md ppx:has-data-[icon=inline-end]:pe-1.5 ppx:has-data-[icon=inline-start]:ps-1.5",
31
+ lg: "ppx:h-10 ppx:gap-1.5 ppx:px-2.5 ppx:has-data-[icon=inline-end]:pe-2 ppx:has-data-[icon=inline-start]:ps-2",
32
+ icon: "ppx:size-9",
33
+ "icon-xs": "ppx:size-6 ppx:rounded-[min(var(--radius-md),8px)] ppx:in-data-[slot=button-group]:rounded-md ppx:[&_svg:not([class*=size-])]:size-3",
34
+ "icon-sm": "ppx:size-8 ppx:rounded-[min(var(--radius-md),10px)] ppx:in-data-[slot=button-group]:rounded-md",
35
+ "icon-lg": "ppx:size-10"
36
+ }
37
+ },
38
+ defaultVariants: {
39
+ variant: "default",
40
+ size: "default"
41
+ }
42
+ });
43
+ function Button({ className, variant = "default", size = "default", ...props }) {
44
+ return /* @__PURE__ */ jsx(Button$1, {
45
+ "data-slot": "button",
46
+ className: cn(buttonVariants({
47
+ variant,
48
+ size,
49
+ className
50
+ })),
51
+ ...props
52
+ });
53
+ }
54
+
55
+ //#endregion
56
+ //#region src/providers/px-ui-provider.tsx
57
+ const PXUIContext = createContext(null);
58
+ function PXUIProvider(props) {
59
+ return /* @__PURE__ */ jsx(PXUIContext.Provider, {
60
+ value: null,
61
+ children: props.children
62
+ });
63
+ }
64
+
65
+ //#endregion
66
+ //#region src/hooks/use-debounce.ts
67
+ function useDebounce(callback, delay, deps) {
68
+ const timeout = useRef(null);
69
+ const cb = useEffectEvent(callback);
70
+ useEffect(() => {
71
+ if (timeout.current) clearTimeout(timeout.current);
72
+ timeout.current = setTimeout(() => {
73
+ cb();
74
+ timeout.current = null;
75
+ }, delay);
76
+ return () => {
77
+ if (timeout.current) {
78
+ clearTimeout(timeout.current);
79
+ timeout.current = null;
80
+ }
81
+ };
82
+ }, [...deps, delay]);
83
+ }
84
+
85
+ //#endregion
86
+ //#region src/hooks/use-intersection-observer.ts
87
+ function useIntersectionObserver(options = {}) {
88
+ const { threshold = 1, root = null, rootMargin = "0px" } = options;
89
+ const [entry, setEntry] = React.useState(null);
90
+ const previousObserver = React.useRef(null);
91
+ return [React.useCallback((node) => {
92
+ if (previousObserver.current) {
93
+ previousObserver.current.disconnect();
94
+ previousObserver.current = null;
95
+ }
96
+ if (node?.nodeType === Node.ELEMENT_NODE) {
97
+ const observer = new IntersectionObserver(([entry$1]) => {
98
+ setEntry(entry$1 ?? null);
99
+ }, {
100
+ threshold,
101
+ root,
102
+ rootMargin
103
+ });
104
+ observer.observe(node);
105
+ previousObserver.current = observer;
106
+ }
107
+ }, [
108
+ threshold,
109
+ root,
110
+ rootMargin
111
+ ]), entry];
112
+ }
113
+
114
+ //#endregion
115
+ //#region src/hooks/use-infinite-scroll.ts
116
+ const DEFAULT_DELAY_IN_MS = 100;
117
+ function useInfiniteScroll({ isLoadingMore, hasMore, onLoadMore, rootMargin, disabled, threshold = 0 }) {
118
+ const [ref, entry] = useIntersectionObserver({
119
+ rootMargin,
120
+ threshold
121
+ });
122
+ const isVisible = Boolean(entry?.isIntersecting);
123
+ const onLoadMoreEvent = useEffectEvent(() => {
124
+ onLoadMore();
125
+ });
126
+ useEffect(() => {
127
+ if (disabled) return;
128
+ if (isVisible && hasMore && !isLoadingMore && !disabled) {
129
+ const timer = setTimeout(() => {
130
+ onLoadMoreEvent();
131
+ }, DEFAULT_DELAY_IN_MS);
132
+ return () => {
133
+ clearTimeout(timer);
134
+ };
135
+ }
136
+ }, [
137
+ isVisible,
138
+ isLoadingMore,
139
+ hasMore,
140
+ disabled
141
+ ]);
142
+ return [ref];
143
+ }
144
+ var use_infinite_scroll_default = useInfiniteScroll;
145
+
146
+ //#endregion
147
+ //#region src/hooks/use-async-options.ts
148
+ function useAsyncOptions(params, { isOpen, inputValue }) {
149
+ const isFetchedAfterMount = useRef(false);
150
+ const query = useInfiniteQuery({
151
+ queryKey: [...params.cacheKey, inputValue],
152
+ queryFn: async ({ pageParam, signal }) => {
153
+ const { error, data } = await params.loader({
154
+ search: inputValue,
155
+ page: pageParam,
156
+ signal
157
+ });
158
+ if (error) throw new Error(error);
159
+ return data;
160
+ },
161
+ enabled: isOpen,
162
+ initialPageParam: 1,
163
+ staleTime: () => isFetchedAfterMount.current ? Infinity : 0,
164
+ getNextPageParam: (lastPage, allPages) => lastPage.hasMore ? allPages.length + 1 : void 0,
165
+ retry: false
166
+ });
167
+ if (query.isFetchedAfterMount && !isFetchedAfterMount.current) isFetchedAfterMount.current = true;
168
+ const items = query.data?.pages.flatMap((page) => page.options);
169
+ const handleLoadMore = () => {
170
+ query.fetchNextPage();
171
+ };
172
+ return {
173
+ items,
174
+ isLoading: query.isLoading,
175
+ isLoadingMore: query.isFetchingNextPage,
176
+ hasMore: query.hasNextPage,
177
+ isError: query.isError,
178
+ onLoadMore: handleLoadMore,
179
+ _query: query
180
+ };
181
+ }
182
+ function defineLoadOptions(loadOptions) {
183
+ return loadOptions;
184
+ }
185
+
186
+ //#endregion
187
+ //#region src/hooks/use-file-upload.ts
188
+ const formatBytes = (bytes, decimals = 2) => {
189
+ if (bytes === 0) return "0 Bytes";
190
+ const k = 1024;
191
+ const dm = decimals < 0 ? 0 : decimals;
192
+ const sizes = [
193
+ "Bytes",
194
+ "KB",
195
+ "MB",
196
+ "GB",
197
+ "TB",
198
+ "PB",
199
+ "EB",
200
+ "ZB",
201
+ "YB"
202
+ ];
203
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
204
+ return Number.parseFloat((bytes / k ** i).toFixed(dm)) + sizes[i];
205
+ };
206
+ /**
207
+ * Extract error message from various error formats, including S3 XML errors
208
+ */
209
+ function extractErrorMessage(error) {
210
+ if (typeof error === "string") return error;
211
+ if (error instanceof Error) {
212
+ const xmlMatch = error.message.match(/<Message>(.*?)<\/Message>/);
213
+ if (xmlMatch) return xmlMatch[1];
214
+ return error.message;
215
+ }
216
+ if (error && typeof error === "object") {
217
+ if ("responseText" in error && typeof error.responseText === "string") {
218
+ const xmlMatch = error.responseText.match(/<Message>(.*?)<\/Message>/);
219
+ if (xmlMatch) return xmlMatch[1];
220
+ }
221
+ if ("body" in error && typeof error.body === "string") {
222
+ const xmlMatch = error.body.match(/<Message>(.*?)<\/Message>/);
223
+ if (xmlMatch) return xmlMatch[1];
224
+ }
225
+ if ("message" in error && typeof error.message === "string") return error.message;
226
+ if ("error" in error) return extractErrorMessage(error.error);
227
+ }
228
+ return "Upload failed";
229
+ }
230
+ const useFileUpload = (options = {}) => {
231
+ const { upload, ...baseOptions } = options;
232
+ const autoUpload = upload?.autoUpload ?? true;
233
+ const [files, setFilesState] = useState((baseOptions.initialFiles ?? []).map((file) => ({
234
+ file,
235
+ id: file.id,
236
+ preview: file.url,
237
+ progress: 100,
238
+ status: "complete",
239
+ uploadedUrl: file.url
240
+ })));
241
+ const [isUploading, setIsUploading] = useState(false);
242
+ const [isDragging, setIsDragging] = useState(false);
243
+ const [errors, setErrors] = useState([]);
244
+ const [instanceKey, setInstanceKey] = useState(0);
245
+ const inputRef = useRef(null);
246
+ const abortControllersRef = useRef(/* @__PURE__ */ new Map());
247
+ const cancelAllUploads = useCallback(() => {
248
+ for (const controller of abortControllersRef.current.values()) controller.abort();
249
+ abortControllersRef.current.clear();
250
+ }, []);
251
+ const { maxFiles = Number.POSITIVE_INFINITY, maxSize = Number.POSITIVE_INFINITY, minSize = 4 * 1024, accept = "*", multiple = false, onFilesChange, onFileAdd, onFileRemove } = baseOptions;
252
+ const validateFile = useCallback((file) => {
253
+ if (file.size > maxSize) return `File "${file.name}" exceeds the maximum size of ${formatBytes(maxSize)}.`;
254
+ if (file.size < minSize) return `File "${file.name}" is below the minimum size of ${formatBytes(minSize)}.`;
255
+ if (accept !== "*") {
256
+ const acceptedTypes = accept.split(",").map((type) => type.trim());
257
+ const fileType = file.type || "";
258
+ const fileExtension = `.${file.name.split(".").pop()}`;
259
+ if (!acceptedTypes.some((type) => {
260
+ if (type.startsWith(".")) return fileExtension.toLowerCase() === type.toLowerCase();
261
+ if (type.endsWith("/*")) {
262
+ const baseType = type.split("/")[0];
263
+ return fileType.startsWith(`${baseType}/`);
264
+ }
265
+ return fileType === type;
266
+ })) return `File "${file.name}" is not an accepted file type.`;
267
+ }
268
+ return null;
269
+ }, [
270
+ accept,
271
+ maxSize,
272
+ minSize
273
+ ]);
274
+ const createPreview = useCallback((file) => {
275
+ if (file.type.startsWith("image/")) return URL.createObjectURL(file);
276
+ }, []);
277
+ const generateUniqueId = useCallback((file) => {
278
+ return `${file.name}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
279
+ }, []);
280
+ const uploadSingleFile = useCallback(async (fileWithStatus) => {
281
+ if (!upload) return {
282
+ ...fileWithStatus,
283
+ status: "error",
284
+ error: "No upload config provided"
285
+ };
286
+ const file = fileWithStatus.file;
287
+ if (!(file instanceof File)) return {
288
+ ...fileWithStatus,
289
+ status: "complete",
290
+ progress: 100
291
+ };
292
+ const abortController = new AbortController();
293
+ abortControllersRef.current.set(fileWithStatus.id, abortController);
294
+ try {
295
+ setFilesState((prev) => prev.map((f) => f.id === fileWithStatus.id ? {
296
+ ...f,
297
+ status: "uploading",
298
+ progress: 0
299
+ } : f));
300
+ const { result: presignedResult, error: presignedError } = await upload.getPresignedUrl({
301
+ filename: file.name,
302
+ contentType: file.type,
303
+ size: file.size,
304
+ signal: abortController.signal
305
+ });
306
+ if (presignedError || !presignedResult) throw presignedError || /* @__PURE__ */ new Error("Failed to get presigned URL");
307
+ setFilesState((prev) => prev.map((f) => f.id === fileWithStatus.id ? {
308
+ ...f,
309
+ progress: 10
310
+ } : f));
311
+ const { result: uploadResult, error: uploadError } = await upload.uploadFile(presignedResult.url, file, presignedResult, (progress) => {
312
+ const scaledProgress = Math.min(10 + progress * .89, 99);
313
+ setFilesState((prev) => prev.map((f) => f.id === fileWithStatus.id ? {
314
+ ...f,
315
+ progress: Math.round(scaledProgress)
316
+ } : f));
317
+ }, abortController.signal);
318
+ if (uploadError) throw uploadError;
319
+ if (!uploadResult?.url) throw new Error("Upload failed, no URL was returned");
320
+ setFilesState((prev) => prev.map((f) => f.id === fileWithStatus.id ? {
321
+ ...f,
322
+ progress: 100
323
+ } : f));
324
+ await new Promise((resolve) => setTimeout(resolve, 150));
325
+ const uploadedFile = {
326
+ ...fileWithStatus,
327
+ status: "complete",
328
+ progress: 100,
329
+ uploadedUrl: uploadResult.url
330
+ };
331
+ setFilesState((prev) => prev.map((f) => f.id === fileWithStatus.id ? uploadedFile : f));
332
+ upload.onUploadComplete?.(uploadedFile);
333
+ return uploadedFile;
334
+ } catch (error) {
335
+ if (error instanceof Error && (error.name === "AbortError" || abortController.signal.aborted)) return {
336
+ ...fileWithStatus,
337
+ status: "idle",
338
+ progress: 0
339
+ };
340
+ const errorMessage = extractErrorMessage(error);
341
+ const failedFile = {
342
+ ...fileWithStatus,
343
+ status: "error",
344
+ error: errorMessage
345
+ };
346
+ setFilesState((prev) => prev.map((f) => f.id === fileWithStatus.id ? failedFile : f));
347
+ upload.onUploadError?.(failedFile, error);
348
+ return failedFile;
349
+ } finally {
350
+ abortControllersRef.current.delete(fileWithStatus.id);
351
+ }
352
+ }, [upload]);
353
+ const uploadFiles = useCallback(async (filesToUpload) => {
354
+ const targetFiles = filesToUpload ?? files.filter((f) => f.status === "idle");
355
+ if (targetFiles.length === 0) return [];
356
+ setIsUploading(true);
357
+ const results = [];
358
+ for (const file of targetFiles) {
359
+ const result = await uploadSingleFile(file);
360
+ results.push(result);
361
+ }
362
+ setIsUploading(false);
363
+ upload?.onAllUploadsComplete?.(results);
364
+ return results;
365
+ }, [
366
+ files,
367
+ uploadSingleFile,
368
+ upload
369
+ ]);
370
+ const addFiles = useCallback((newFiles) => {
371
+ if (!newFiles || newFiles.length === 0) return;
372
+ const newFilesArray = Array.from(newFiles);
373
+ const newErrors = [];
374
+ setErrors([]);
375
+ if (!multiple) setFilesState((prev) => {
376
+ for (const file of prev) if (file.preview && file.file instanceof File) URL.revokeObjectURL(file.preview);
377
+ return [];
378
+ });
379
+ if (multiple && maxFiles !== Number.POSITIVE_INFINITY && files.length + newFilesArray.length > maxFiles) {
380
+ newErrors.push(`You can only upload a maximum of ${maxFiles} files.`);
381
+ setErrors(newErrors);
382
+ return;
383
+ }
384
+ const validFiles = [];
385
+ for (const file of newFilesArray) {
386
+ if (multiple) {
387
+ if (files.some((existingFile) => existingFile.file.name === file.name && existingFile.file.size === file.size)) continue;
388
+ }
389
+ const error = validateFile(file);
390
+ if (error) {
391
+ newErrors.push(error);
392
+ continue;
393
+ }
394
+ validFiles.push({
395
+ file,
396
+ id: generateUniqueId(file),
397
+ preview: createPreview(file),
398
+ progress: 0,
399
+ status: "idle"
400
+ });
401
+ }
402
+ if (validFiles.length > 0) {
403
+ if (onFileAdd) for (const file of validFiles) onFileAdd(file);
404
+ setFilesState((prev) => {
405
+ const updatedFiles = !multiple ? validFiles : [...prev, ...validFiles];
406
+ onFilesChange?.(updatedFiles);
407
+ return updatedFiles;
408
+ });
409
+ if (autoUpload && upload) setTimeout(() => {
410
+ uploadFiles(validFiles);
411
+ }, 0);
412
+ }
413
+ if (newErrors.length > 0) setErrors(newErrors);
414
+ if (inputRef.current) inputRef.current.value = "";
415
+ }, [
416
+ files,
417
+ maxFiles,
418
+ multiple,
419
+ validateFile,
420
+ createPreview,
421
+ generateUniqueId,
422
+ onFilesChange,
423
+ onFileAdd,
424
+ autoUpload,
425
+ upload,
426
+ uploadFiles
427
+ ]);
428
+ const removeFile = useCallback((id) => {
429
+ const controller = abortControllersRef.current.get(id);
430
+ if (controller) {
431
+ controller.abort();
432
+ abortControllersRef.current.delete(id);
433
+ }
434
+ setFilesState((prev) => {
435
+ const fileToRemove = prev.find((file) => file.id === id);
436
+ if (fileToRemove) {
437
+ onFileRemove?.(fileToRemove);
438
+ if (fileToRemove.preview && fileToRemove.file instanceof File) URL.revokeObjectURL(fileToRemove.preview);
439
+ }
440
+ const newFiles = prev.filter((file) => file.id !== id);
441
+ onFilesChange?.(newFiles);
442
+ return newFiles;
443
+ });
444
+ }, [onFilesChange, onFileRemove]);
445
+ const clearFiles = useCallback(() => {
446
+ cancelAllUploads();
447
+ setFilesState((prev) => {
448
+ for (const file of prev) {
449
+ onFileRemove?.(file);
450
+ if (file.preview && file.file instanceof File) URL.revokeObjectURL(file.preview);
451
+ }
452
+ return [];
453
+ });
454
+ if (inputRef.current) inputRef.current.value = "";
455
+ setErrors([]);
456
+ onFilesChange?.([]);
457
+ }, [
458
+ cancelAllUploads,
459
+ onFilesChange,
460
+ onFileRemove
461
+ ]);
462
+ const clearErrors = useCallback(() => {
463
+ setErrors([]);
464
+ }, []);
465
+ const reset = useCallback((newFiles) => {
466
+ cancelAllUploads();
467
+ for (const file of files) if (file.preview && file.file instanceof File) URL.revokeObjectURL(file.preview);
468
+ if (inputRef.current) inputRef.current.value = "";
469
+ setErrors([]);
470
+ const initializedFiles = (newFiles ?? []).map((file) => ({
471
+ file,
472
+ id: file.id,
473
+ preview: file.url,
474
+ progress: 100,
475
+ status: "complete",
476
+ uploadedUrl: file.url
477
+ }));
478
+ setFilesState(initializedFiles);
479
+ onFilesChange?.(initializedFiles);
480
+ setInstanceKey((prev) => prev + 1);
481
+ }, [
482
+ cancelAllUploads,
483
+ files,
484
+ onFilesChange
485
+ ]);
486
+ const setFiles = useCallback((newFiles) => {
487
+ setFilesState((prev) => {
488
+ const nextFiles = typeof newFiles === "function" ? newFiles(prev) : newFiles;
489
+ onFilesChange?.(nextFiles);
490
+ return nextFiles;
491
+ });
492
+ }, [onFilesChange]);
493
+ const retryUpload = useCallback(async (id) => {
494
+ const file = files.find((f) => f.id === id);
495
+ if (file && file.status === "error") {
496
+ setFilesState((prev) => prev.map((f) => f.id === id ? {
497
+ ...f,
498
+ status: "idle",
499
+ error: void 0
500
+ } : f));
501
+ await uploadSingleFile({
502
+ ...file,
503
+ status: "idle",
504
+ error: void 0
505
+ });
506
+ }
507
+ }, [files, uploadSingleFile]);
508
+ const cancelUpload = useCallback((id) => {
509
+ const controller = abortControllersRef.current.get(id);
510
+ if (controller) {
511
+ controller.abort();
512
+ abortControllersRef.current.delete(id);
513
+ }
514
+ setFilesState((prev) => prev.map((f) => f.id === id ? {
515
+ ...f,
516
+ status: "idle",
517
+ progress: 0
518
+ } : f));
519
+ }, []);
520
+ const handleDragEnter = useCallback((e) => {
521
+ e.preventDefault();
522
+ e.stopPropagation();
523
+ setIsDragging(true);
524
+ }, []);
525
+ const handleDragLeave = useCallback((e) => {
526
+ e.preventDefault();
527
+ e.stopPropagation();
528
+ if (e.currentTarget.contains(e.relatedTarget)) return;
529
+ setIsDragging(false);
530
+ }, []);
531
+ const handleDragOver = useCallback((e) => {
532
+ e.preventDefault();
533
+ e.stopPropagation();
534
+ }, []);
535
+ const handleDrop = useCallback((e) => {
536
+ e.preventDefault();
537
+ e.stopPropagation();
538
+ setIsDragging(false);
539
+ if (inputRef.current?.disabled) return;
540
+ if (e.dataTransfer.files?.length > 0) if (!multiple) addFiles([e.dataTransfer.files[0]]);
541
+ else addFiles(e.dataTransfer.files);
542
+ }, [addFiles, multiple]);
543
+ const handleFileChange = useCallback((e) => {
544
+ if (e.target.files?.length) addFiles(e.target.files);
545
+ }, [addFiles]);
546
+ return {
547
+ files,
548
+ isDragging,
549
+ isUploading,
550
+ errors,
551
+ addFiles,
552
+ removeFile,
553
+ clearFiles,
554
+ clearErrors,
555
+ reset,
556
+ setFiles,
557
+ instanceKey,
558
+ uploadFiles,
559
+ retryUpload,
560
+ cancelUpload,
561
+ handleDragEnter,
562
+ handleDragLeave,
563
+ handleDragOver,
564
+ handleDrop,
565
+ handleFileChange,
566
+ openFileDialog: useCallback(() => {
567
+ inputRef.current?.click();
568
+ }, []),
569
+ getInputProps: useCallback((props = {}) => ({
570
+ ...props,
571
+ accept: props.accept || accept,
572
+ multiple: props.multiple !== void 0 ? props.multiple : multiple,
573
+ onChange: handleFileChange,
574
+ ref: inputRef,
575
+ type: "file"
576
+ }), [
577
+ accept,
578
+ multiple,
579
+ handleFileChange
580
+ ])
581
+ };
582
+ };
583
+
584
+ //#endregion
585
+ //#region src/hooks/use-mobile.ts
586
+ const MOBILE_BREAKPOINT = 768;
587
+ function useIsMobile() {
588
+ const [isMobile, setIsMobile] = React$1.useState(void 0);
589
+ React$1.useEffect(() => {
590
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
591
+ const onChange = () => {
592
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
593
+ };
594
+ mql.addEventListener("change", onChange);
595
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
596
+ return () => mql.removeEventListener("change", onChange);
597
+ }, []);
598
+ return !!isMobile;
599
+ }
600
+
601
+ //#endregion
602
+ export { Button, PXUIProvider, buttonVariants, cn, defineLoadOptions, formatBytes, useAsyncOptions, useDebounce, useFileUpload, use_infinite_scroll_default as useInfiniteScroll, useIntersectionObserver, useIsMobile };
603
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["ButtonPrimitive","entry","uploadedFile: FileUploadItem","failedFile: FileUploadItem","results: FileUploadItem[]","newErrors: string[]","validFiles: FileUploadItem[]","initializedFiles: FileUploadItem[]","React"],"sources":["../src/lib/utils.ts","../src/components/button.tsx","../src/providers/px-ui-provider.tsx","../src/hooks/use-debounce.ts","../src/hooks/use-intersection-observer.ts","../src/hooks/use-infinite-scroll.ts","../src/hooks/use-async-options.ts","../src/hooks/use-file-upload.ts","../src/hooks/use-mobile.ts"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { Button as ButtonPrimitive } from \"@base-ui/react/button\"\nimport { cva, type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"@px-ui/ui/lib/utils\"\n\nconst buttonVariants = cva(\n \"ppx:group/button ppx:inline-flex ppx:shrink-0 ppx:items-center ppx:justify-center ppx:rounded-md ppx:border ppx:border-transparent ppx:bg-clip-padding ppx:text-sm ppx:font-medium ppx:whitespace-nowrap ppx:transition-all ppx:outline-none ppx:select-none ppx:focus-visible:border-ring ppx:focus-visible:ring-3 ppx:focus-visible:ring-ring/50 ppx:active:not-aria-[haspopup]:translate-y-px ppx:disabled:pointer-events-none ppx:disabled:opacity-50 ppx:aria-invalid:border-destructive ppx:aria-invalid:ring-3 ppx:aria-invalid:ring-destructive/20 ppx:dark:aria-invalid:border-destructive/50 ppx:dark:aria-invalid:ring-destructive/40 ppx:[&_svg]:pointer-events-none ppx:[&_svg]:shrink-0 ppx:[&_svg:not([class*=size-])]:size-4\",\n {\n variants: {\n variant: {\n default: \"ppx:bg-primary ppx:text-primary-foreground ppx:hover:bg-primary/80\",\n outline:\n \"ppx:border-border ppx:bg-background ppx:shadow-xs ppx:hover:bg-muted ppx:hover:text-foreground ppx:aria-expanded:bg-muted ppx:aria-expanded:text-foreground ppx:dark:border-input ppx:dark:bg-input/30 ppx:dark:hover:bg-input/50\",\n secondary:\n \"ppx:bg-secondary ppx:text-secondary-foreground ppx:hover:bg-secondary/80 ppx:aria-expanded:bg-secondary ppx:aria-expanded:text-secondary-foreground\",\n ghost:\n \"ppx:hover:bg-muted ppx:hover:text-foreground ppx:aria-expanded:bg-muted ppx:aria-expanded:text-foreground ppx:dark:hover:bg-muted/50\",\n destructive:\n \"ppx:bg-destructive/10 ppx:text-destructive ppx:hover:bg-destructive/20 ppx:focus-visible:border-destructive/40 ppx:focus-visible:ring-destructive/20 ppx:dark:bg-destructive/20 ppx:dark:hover:bg-destructive/30 ppx:dark:focus-visible:ring-destructive/40\",\n link: \"ppx:text-primary ppx:underline-offset-4 ppx:hover:underline\",\n },\n size: {\n default:\n \"ppx:h-9 ppx:gap-1.5 ppx:px-2.5 ppx:in-data-[slot=button-group]:rounded-md ppx:has-data-[icon=inline-end]:pe-2 ppx:has-data-[icon=inline-start]:ps-2\",\n xs: \"ppx:h-6 ppx:gap-1 ppx:rounded-[min(var(--radius-md),8px)] ppx:px-2 ppx:text-xs ppx:in-data-[slot=button-group]:rounded-md ppx:has-data-[icon=inline-end]:pe-1.5 ppx:has-data-[icon=inline-start]:ps-1.5 ppx:[&_svg:not([class*=size-])]:size-3\",\n sm: \"ppx:h-8 ppx:gap-1 ppx:rounded-[min(var(--radius-md),10px)] ppx:px-2.5 ppx:in-data-[slot=button-group]:rounded-md ppx:has-data-[icon=inline-end]:pe-1.5 ppx:has-data-[icon=inline-start]:ps-1.5\",\n lg: \"ppx:h-10 ppx:gap-1.5 ppx:px-2.5 ppx:has-data-[icon=inline-end]:pe-2 ppx:has-data-[icon=inline-start]:ps-2\",\n icon: \"ppx:size-9\",\n \"icon-xs\":\n \"ppx:size-6 ppx:rounded-[min(var(--radius-md),8px)] ppx:in-data-[slot=button-group]:rounded-md ppx:[&_svg:not([class*=size-])]:size-3\",\n \"icon-sm\":\n \"ppx:size-8 ppx:rounded-[min(var(--radius-md),10px)] ppx:in-data-[slot=button-group]:rounded-md\",\n \"icon-lg\": \"ppx:size-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n)\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n ...props\n}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {\n return (\n <ButtonPrimitive\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","import { createContext } from \"react\";\n\nconst PXUIContext = createContext(null);\n\nexport function PXUIProvider(props: React.PropsWithChildren) {\n return <PXUIContext.Provider value={null}>{props.children}</PXUIContext.Provider>;\n}\n","import { useEffect, useEffectEvent, useRef } from \"react\";\n\nexport default function useDebounce<T>(\n callback: () => void,\n delay: number,\n deps: React.DependencyList,\n) {\n const timeout = useRef<ReturnType<typeof setTimeout> | null>(null);\n const cb = useEffectEvent(callback);\n\n useEffect(() => {\n if (timeout.current) {\n clearTimeout(timeout.current);\n }\n\n timeout.current = setTimeout(() => {\n cb();\n timeout.current = null;\n }, delay);\n\n return () => {\n if (timeout.current) {\n clearTimeout(timeout.current);\n timeout.current = null;\n }\n };\n }, [...deps, delay]);\n}\n","import React from \"react\";\n\nexport interface UseIntersectionObserverOptions {\n threshold?: number | number[];\n root?: Element | Document | null;\n rootMargin?: string;\n}\n\nexport type UseIntersectionObserverReturn = [\n (node: Element | null) => void,\n IntersectionObserverEntry | null,\n];\n\nexport function useIntersectionObserver(\n options: UseIntersectionObserverOptions = {},\n): UseIntersectionObserverReturn {\n const { threshold = 1, root = null, rootMargin = \"0px\" } = options;\n const [entry, setEntry] = React.useState<IntersectionObserverEntry | null>(\n null,\n );\n\n const previousObserver = React.useRef<IntersectionObserver | null>(null);\n\n const customRef = React.useCallback(\n (node: Element | null) => {\n if (previousObserver.current) {\n previousObserver.current.disconnect();\n previousObserver.current = null;\n }\n\n if (node?.nodeType === Node.ELEMENT_NODE) {\n const observer = new IntersectionObserver(\n ([entry]: IntersectionObserverEntry[]) => {\n setEntry(entry ?? null);\n },\n { threshold, root, rootMargin },\n );\n\n observer.observe(node);\n previousObserver.current = observer;\n }\n },\n [threshold, root, rootMargin],\n );\n\n return [customRef, entry] as const;\n}\n","import { useEffect, useEffectEvent } from \"react\";\nimport {\n useIntersectionObserver,\n type UseIntersectionObserverOptions,\n} from \"./use-intersection-observer\";\n\nconst DEFAULT_DELAY_IN_MS = 100;\n\nexport type UseInfiniteScrollHookArgs = Pick<\n UseIntersectionObserverOptions,\n \"rootMargin\" | \"threshold\"\n> & {\n isLoadingMore: boolean;\n hasMore: boolean;\n onLoadMore: () => unknown;\n disabled?: boolean;\n};\n\nfunction useInfiniteScroll({\n isLoadingMore,\n hasMore,\n onLoadMore,\n rootMargin,\n disabled,\n threshold = 0,\n}: UseInfiniteScrollHookArgs) {\n const [ref, entry] = useIntersectionObserver({\n rootMargin,\n threshold,\n });\n const isVisible = Boolean(entry?.isIntersecting);\n\n const onLoadMoreEvent = useEffectEvent(() => {\n onLoadMore();\n });\n\n useEffect(() => {\n if (disabled) return;\n\n if (isVisible && hasMore && !isLoadingMore && !disabled) {\n const timer = setTimeout(() => {\n onLoadMoreEvent();\n }, DEFAULT_DELAY_IN_MS);\n\n return () => {\n clearTimeout(timer);\n };\n }\n }, [isVisible, isLoadingMore, hasMore, disabled]);\n\n return [ref] as const;\n}\n\nexport default useInfiniteScroll;\n","import { useRef } from \"react\";\nimport { useInfiniteQuery, type QueryKey } from \"@tanstack/react-query\";\n\ntype LoadOptionsData<TData> = {\n options: TData[];\n hasMore: boolean;\n};\n\ntype LoadOptionsReturn<TData> = {\n data: LoadOptionsData<TData>;\n error: any;\n};\n\ntype LoadOptionsFn<TData> = (params: {\n search: string;\n page: number;\n signal: AbortSignal;\n}) => Promise<LoadOptionsReturn<TData>>;\n\nexport function useAsyncOptions<TData = any>(\n params: {\n cacheKey: QueryKey;\n loader: LoadOptionsFn<TData>;\n },\n {\n isOpen,\n inputValue,\n }: {\n isOpen: boolean;\n inputValue: string;\n },\n) {\n const isFetchedAfterMount = useRef(false);\n\n const query = useInfiniteQuery({\n queryKey: [...params.cacheKey, inputValue],\n queryFn: async ({ pageParam, signal }) => {\n const { error, data } = await params.loader({\n search: inputValue,\n page: pageParam,\n signal,\n });\n\n if (error) {\n throw new Error(error);\n }\n\n return data;\n },\n enabled: isOpen,\n initialPageParam: 1,\n staleTime: () => (isFetchedAfterMount.current ? Infinity : 0),\n getNextPageParam: (lastPage, allPages) =>\n lastPage.hasMore ? allPages.length + 1 : undefined,\n retry: false,\n });\n\n if (query.isFetchedAfterMount && !isFetchedAfterMount.current) {\n isFetchedAfterMount.current = true;\n }\n\n const items = query.data?.pages.flatMap((page) => page.options);\n\n const handleLoadMore = () => {\n query.fetchNextPage();\n };\n\n return {\n items,\n isLoading: query.isLoading,\n isLoadingMore: query.isFetchingNextPage,\n hasMore: query.hasNextPage,\n isError: query.isError,\n onLoadMore: handleLoadMore,\n _query: query,\n };\n}\n\nexport type LoadOptionsConfig<TData> = {\n cacheKey: QueryKey;\n loader: LoadOptionsFn<TData>;\n};\n\nexport function defineLoadOptions<TData = any>(\n loadOptions: LoadOptionsConfig<TData>,\n) {\n return loadOptions;\n}\n\nexport type InferOption<T> =\n T extends LoadOptionsConfig<infer TData> ? TData : never;\nexport type InferOptions<T> =\n T extends LoadOptionsConfig<infer TData> ? TData[] : never;\n","import {\n type ChangeEvent,\n type DragEvent,\n type InputHTMLAttributes,\n useCallback,\n useRef,\n useState,\n} from \"react\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type FileMetadata = {\n name: string;\n size: number;\n type: string;\n url: string;\n id: string;\n};\n\nexport type UploadStatus = \"idle\" | \"uploading\" | \"complete\" | \"error\";\n\nexport type FileUploadItem = {\n file: File | FileMetadata;\n id: string;\n preview?: string;\n progress: number;\n status: UploadStatus;\n error?: string;\n uploadedUrl?: string;\n};\n\nexport type PresignedUrlResponse = {\n /** The S3 bucket URL to POST to */\n url: string;\n /** The full accessible path after upload (may include signed query params) */\n full_path: string;\n /** The S3 object key (e.g., \"uuid/filename.pdf\") */\n key?: string;\n /** Access control level */\n acl?: string;\n /** Expected response status from S3 */\n success_action_status?: string;\n /** Content-Type for the upload */\n \"Content-Type\"?: string;\n /** Base64-encoded policy document */\n policy?: string;\n /** AWS credential string */\n \"x-amz-credential\"?: string;\n /** AWS signing algorithm */\n \"x-amz-algorithm\"?: string;\n /** AWS request date */\n \"x-amz-date\"?: string;\n /** AWS request signature */\n \"x-amz-signature\"?: string;\n /** Additional fields */\n [key: string]: unknown;\n};\n\nexport type UploadConfig = {\n /**\n * Function to get a presigned URL for uploading\n */\n getPresignedUrl: (params: {\n filename: string;\n contentType: string;\n size: number;\n signal?: AbortSignal;\n }) => Promise<{\n result?: PresignedUrlResponse | null;\n error?: unknown;\n }>;\n\n /**\n * Function to upload the file to the storage\n */\n uploadFile: (\n url: string,\n file: File,\n presignedData: PresignedUrlResponse,\n onProgress?: (progress: number) => void,\n signal?: AbortSignal,\n ) => Promise<{\n result?: { url: string; [key: string]: unknown };\n error?: unknown;\n }>;\n\n /**\n * Whether to auto-upload files when they are added\n * @default true\n */\n autoUpload?: boolean;\n\n /**\n * Called when a single file upload completes\n */\n onUploadComplete?: (file: FileUploadItem) => void;\n\n /**\n * Called when a single file upload fails\n */\n onUploadError?: (file: FileUploadItem, error: unknown) => void;\n\n /**\n * Called when all files finish uploading\n */\n onAllUploadsComplete?: (files: FileUploadItem[]) => void;\n};\n\nexport type UseFileUploadOptions = {\n maxFiles?: number;\n maxSize?: number;\n minSize?: number;\n accept?: string;\n multiple?: boolean;\n initialFiles?: FileMetadata[];\n onFilesChange?: (files: FileUploadItem[]) => void;\n onFileAdd?: (addedFile: FileUploadItem) => void;\n onFileRemove?: (removedFile: FileUploadItem) => void;\n upload?: UploadConfig;\n};\n\nexport type UseFileUploadReturn = {\n // State\n files: FileUploadItem[];\n isDragging: boolean;\n isUploading: boolean;\n errors: string[];\n\n // Core Actions\n addFiles: (files: FileList | File[]) => void;\n removeFile: (id: string) => void;\n clearFiles: () => void;\n clearErrors: () => void;\n\n // Form integration\n reset: (newFiles?: FileMetadata[]) => void;\n setFiles: (\n files: FileUploadItem[] | ((prev: FileUploadItem[]) => FileUploadItem[]),\n ) => void;\n instanceKey: number;\n\n // Upload Actions\n uploadFiles: (files?: FileUploadItem[]) => Promise<FileUploadItem[]>;\n retryUpload: (id: string) => Promise<void>;\n cancelUpload: (id: string) => void;\n\n // Drag/Drop + Input\n handleDragEnter: (e: DragEvent<HTMLElement>) => void;\n handleDragLeave: (e: DragEvent<HTMLElement>) => void;\n handleDragOver: (e: DragEvent<HTMLElement>) => void;\n handleDrop: (e: DragEvent<HTMLElement>) => void;\n handleFileChange: (e: ChangeEvent<HTMLInputElement>) => void;\n openFileDialog: () => void;\n getInputProps: (\n props?: InputHTMLAttributes<HTMLInputElement>,\n ) => InputHTMLAttributes<HTMLInputElement> & {\n // biome-ignore lint/suspicious/noExplicitAny: intentional for cross-React ref compatibility\n ref: any;\n };\n};\n\n// Helper function to format bytes to human-readable format\nexport const formatBytes = (bytes: number, decimals = 2): string => {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\", \"EB\", \"ZB\", \"YB\"];\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return Number.parseFloat((bytes / k ** i).toFixed(dm)) + sizes[i];\n};\n\n/**\n * Extract error message from various error formats, including S3 XML errors\n */\nfunction extractErrorMessage(error: unknown): string {\n if (typeof error === \"string\") {\n return error;\n }\n\n if (error instanceof Error) {\n // Check if it's an S3 XML error in the message\n const xmlMatch = error.message.match(/<Message>(.*?)<\\/Message>/);\n if (xmlMatch) {\n return xmlMatch[1];\n }\n return error.message;\n }\n\n // Handle objects with message property\n if (error && typeof error === \"object\") {\n // Check for responseText with XML\n if (\"responseText\" in error && typeof error.responseText === \"string\") {\n const xmlMatch = error.responseText.match(/<Message>(.*?)<\\/Message>/);\n if (xmlMatch) {\n return xmlMatch[1];\n }\n }\n\n // Check for body with XML\n if (\"body\" in error && typeof error.body === \"string\") {\n const xmlMatch = error.body.match(/<Message>(.*?)<\\/Message>/);\n if (xmlMatch) {\n return xmlMatch[1];\n }\n }\n\n // Standard message property\n if (\"message\" in error && typeof error.message === \"string\") {\n return error.message;\n }\n\n // Check for error property\n if (\"error\" in error) {\n return extractErrorMessage(error.error);\n }\n }\n\n return \"Upload failed\";\n}\n\n// ============================================================================\n// Hook\n// ============================================================================\n\nexport const useFileUpload = (\n options: UseFileUploadOptions = {},\n): UseFileUploadReturn => {\n const { upload, ...baseOptions } = options;\n const autoUpload = upload?.autoUpload ?? true;\n\n const [files, setFilesState] = useState<FileUploadItem[]>(\n (baseOptions.initialFiles ?? []).map((file) => ({\n file,\n id: file.id,\n preview: file.url,\n progress: 100,\n status: \"complete\" as const,\n uploadedUrl: file.url,\n })),\n );\n const [isUploading, setIsUploading] = useState(false);\n const [isDragging, setIsDragging] = useState(false);\n const [errors, setErrors] = useState<string[]>([]);\n const [instanceKey, setInstanceKey] = useState(0);\n\n const inputRef = useRef<HTMLInputElement>(null);\n const abortControllersRef = useRef<Map<string, AbortController>>(new Map());\n\n // Helper to cancel all ongoing uploads\n const cancelAllUploads = useCallback(() => {\n for (const controller of abortControllersRef.current.values()) {\n controller.abort();\n }\n abortControllersRef.current.clear();\n }, []);\n\n const {\n maxFiles = Number.POSITIVE_INFINITY,\n maxSize = Number.POSITIVE_INFINITY,\n minSize = 4 * 1024,\n accept = \"*\",\n multiple = false,\n onFilesChange,\n onFileAdd,\n onFileRemove,\n } = baseOptions;\n\n const validateFile = useCallback(\n (file: File): string | null => {\n if (file.size > maxSize) {\n return `File \"${file.name}\" exceeds the maximum size of ${formatBytes(maxSize)}.`;\n }\n\n if (file.size < minSize) {\n return `File \"${file.name}\" is below the minimum size of ${formatBytes(minSize)}.`;\n }\n\n if (accept !== \"*\") {\n const acceptedTypes = accept.split(\",\").map((type) => type.trim());\n const fileType = file.type || \"\";\n const fileExtension = `.${file.name.split(\".\").pop()}`;\n\n const isAccepted = acceptedTypes.some((type) => {\n if (type.startsWith(\".\")) {\n return fileExtension.toLowerCase() === type.toLowerCase();\n }\n if (type.endsWith(\"/*\")) {\n const baseType = type.split(\"/\")[0];\n return fileType.startsWith(`${baseType}/`);\n }\n return fileType === type;\n });\n\n if (!isAccepted) {\n return `File \"${file.name}\" is not an accepted file type.`;\n }\n }\n\n return null;\n },\n [accept, maxSize, minSize],\n );\n\n const createPreview = useCallback((file: File): string | undefined => {\n if (file.type.startsWith(\"image/\")) {\n return URL.createObjectURL(file);\n }\n return undefined;\n }, []);\n\n const generateUniqueId = useCallback((file: File): string => {\n return `${file.name}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n }, []);\n\n const uploadSingleFile = useCallback(\n async (fileWithStatus: FileUploadItem): Promise<FileUploadItem> => {\n if (!upload) {\n return {\n ...fileWithStatus,\n status: \"error\",\n error: \"No upload config provided\",\n };\n }\n\n const file = fileWithStatus.file as File;\n if (!(file instanceof File)) {\n // Already uploaded file (FileMetadata)\n return { ...fileWithStatus, status: \"complete\", progress: 100 };\n }\n\n const abortController = new AbortController();\n abortControllersRef.current.set(fileWithStatus.id, abortController);\n\n try {\n // Update status to uploading\n setFilesState((prev) =>\n prev.map((f) =>\n f.id === fileWithStatus.id\n ? { ...f, status: \"uploading\" as const, progress: 0 }\n : f,\n ),\n );\n\n // Step 1: Get presigned URL\n const { result: presignedResult, error: presignedError } =\n await upload.getPresignedUrl({\n filename: file.name,\n contentType: file.type,\n size: file.size,\n signal: abortController.signal,\n });\n\n if (presignedError || !presignedResult) {\n throw presignedError || new Error(\"Failed to get presigned URL\");\n }\n\n // Update progress after getting presigned URL\n setFilesState((prev) =>\n prev.map((f) =>\n f.id === fileWithStatus.id ? { ...f, progress: 10 } : f,\n ),\n );\n\n // Step 2: Upload file\n const { result: uploadResult, error: uploadError } =\n await upload.uploadFile(\n presignedResult.url,\n file,\n presignedResult,\n (progress) => {\n // Scale progress from 10-99 (leave room for final 100%)\n const scaledProgress = Math.min(10 + progress * 0.89, 99);\n setFilesState((prev) =>\n prev.map((f) =>\n f.id === fileWithStatus.id\n ? { ...f, progress: Math.round(scaledProgress) }\n : f,\n ),\n );\n },\n abortController.signal,\n );\n\n if (uploadError) {\n throw uploadError;\n }\n\n if (!uploadResult?.url) {\n throw new Error(\"Upload failed, no URL was returned\");\n }\n\n // Ensure progress hits 100% before changing status\n setFilesState((prev) =>\n prev.map((f) =>\n f.id === fileWithStatus.id ? { ...f, progress: 100 } : f,\n ),\n );\n\n // Small delay to show 100% before marking complete\n await new Promise((resolve) => setTimeout(resolve, 150));\n\n const uploadedFile: FileUploadItem = {\n ...fileWithStatus,\n status: \"complete\",\n progress: 100,\n uploadedUrl: uploadResult.url,\n };\n\n setFilesState((prev) =>\n prev.map((f) => (f.id === fileWithStatus.id ? uploadedFile : f)),\n );\n\n upload.onUploadComplete?.(uploadedFile);\n\n return uploadedFile;\n } catch (error) {\n // Check if the upload was cancelled via AbortController\n const isAborted =\n error instanceof Error &&\n (error.name === \"AbortError\" || abortController.signal.aborted);\n\n if (isAborted) {\n // Upload was cancelled - return idle state\n const cancelledFile: FileUploadItem = {\n ...fileWithStatus,\n status: \"idle\",\n progress: 0,\n };\n return cancelledFile;\n }\n\n const errorMessage = extractErrorMessage(error);\n const failedFile: FileUploadItem = {\n ...fileWithStatus,\n status: \"error\",\n error: errorMessage,\n };\n\n setFilesState((prev) =>\n prev.map((f) => (f.id === fileWithStatus.id ? failedFile : f)),\n );\n\n upload.onUploadError?.(failedFile, error);\n\n return failedFile;\n } finally {\n abortControllersRef.current.delete(fileWithStatus.id);\n }\n },\n [upload],\n );\n\n const uploadFiles = useCallback(\n async (filesToUpload?: FileUploadItem[]): Promise<FileUploadItem[]> => {\n const targetFiles =\n filesToUpload ?? files.filter((f) => f.status === \"idle\");\n\n if (targetFiles.length === 0) return [];\n\n setIsUploading(true);\n const results: FileUploadItem[] = [];\n\n for (const file of targetFiles) {\n const result = await uploadSingleFile(file);\n results.push(result);\n }\n\n setIsUploading(false);\n upload?.onAllUploadsComplete?.(results);\n\n return results;\n },\n [files, uploadSingleFile, upload],\n );\n\n const addFiles = useCallback(\n (newFiles: FileList | File[]) => {\n if (!newFiles || newFiles.length === 0) return;\n\n const newFilesArray = Array.from(newFiles);\n const newErrors: string[] = [];\n\n // Clear existing errors\n setErrors([]);\n\n // In single file mode, clear existing files first\n if (!multiple) {\n setFilesState((prev) => {\n for (const file of prev) {\n if (file.preview && file.file instanceof File) {\n URL.revokeObjectURL(file.preview);\n }\n }\n return [];\n });\n }\n\n // Check if adding these files would exceed maxFiles\n if (\n multiple &&\n maxFiles !== Number.POSITIVE_INFINITY &&\n files.length + newFilesArray.length > maxFiles\n ) {\n newErrors.push(`You can only upload a maximum of ${maxFiles} files.`);\n setErrors(newErrors);\n return;\n }\n\n const validFiles: FileUploadItem[] = [];\n\n for (const file of newFilesArray) {\n // Check for duplicates in multiple mode\n if (multiple) {\n const isDuplicate = files.some(\n (existingFile) =>\n existingFile.file.name === file.name &&\n existingFile.file.size === file.size,\n );\n if (isDuplicate) continue;\n }\n\n const error = validateFile(file);\n if (error) {\n newErrors.push(error);\n continue;\n }\n\n validFiles.push({\n file,\n id: generateUniqueId(file),\n preview: createPreview(file),\n progress: 0,\n status: \"idle\",\n });\n }\n\n if (validFiles.length > 0) {\n // Call onFileAdd callback for each file\n if (onFileAdd) {\n for (const file of validFiles) {\n onFileAdd(file);\n }\n }\n\n setFilesState((prev) => {\n const updatedFiles = !multiple ? validFiles : [...prev, ...validFiles];\n onFilesChange?.(updatedFiles);\n return updatedFiles;\n });\n\n // Auto-upload if configured\n if (autoUpload && upload) {\n // Use setTimeout to ensure state is updated before uploading\n setTimeout(() => {\n uploadFiles(validFiles);\n }, 0);\n }\n }\n\n if (newErrors.length > 0) {\n setErrors(newErrors);\n }\n\n // Reset input value\n if (inputRef.current) {\n inputRef.current.value = \"\";\n }\n },\n [\n files,\n maxFiles,\n multiple,\n validateFile,\n createPreview,\n generateUniqueId,\n onFilesChange,\n onFileAdd,\n autoUpload,\n upload,\n uploadFiles,\n ],\n );\n\n const removeFile = useCallback(\n (id: string) => {\n // Cancel any ongoing upload\n const controller = abortControllersRef.current.get(id);\n if (controller) {\n controller.abort();\n abortControllersRef.current.delete(id);\n }\n\n setFilesState((prev) => {\n const fileToRemove = prev.find((file) => file.id === id);\n if (fileToRemove) {\n // Call onFileRemove callback before removing\n onFileRemove?.(fileToRemove);\n\n if (fileToRemove.preview && fileToRemove.file instanceof File) {\n URL.revokeObjectURL(fileToRemove.preview);\n }\n }\n\n const newFiles = prev.filter((file) => file.id !== id);\n onFilesChange?.(newFiles);\n return newFiles;\n });\n },\n [onFilesChange, onFileRemove],\n );\n\n const clearFiles = useCallback(() => {\n cancelAllUploads();\n\n setFilesState((prev) => {\n for (const file of prev) {\n // Call onFileRemove for each file\n onFileRemove?.(file);\n\n if (file.preview && file.file instanceof File) {\n URL.revokeObjectURL(file.preview);\n }\n }\n return [];\n });\n\n if (inputRef.current) {\n inputRef.current.value = \"\";\n }\n\n setErrors([]);\n onFilesChange?.([]);\n }, [cancelAllUploads, onFilesChange, onFileRemove]);\n\n const clearErrors = useCallback(() => {\n setErrors([]);\n }, []);\n\n const reset = useCallback(\n (newFiles?: FileMetadata[]) => {\n cancelAllUploads();\n\n // Revoke all previews\n for (const file of files) {\n if (file.preview && file.file instanceof File) {\n URL.revokeObjectURL(file.preview);\n }\n }\n\n // Reset input\n if (inputRef.current) {\n inputRef.current.value = \"\";\n }\n\n // Clear errors\n setErrors([]);\n\n // Set new files or clear\n const initializedFiles: FileUploadItem[] = (newFiles ?? []).map(\n (file) => ({\n file,\n id: file.id,\n preview: file.url,\n progress: 100,\n status: \"complete\" as const,\n uploadedUrl: file.url,\n }),\n );\n\n setFilesState(initializedFiles);\n onFilesChange?.(initializedFiles);\n\n // Increment instance key to force remount\n setInstanceKey((prev) => prev + 1);\n },\n [cancelAllUploads, files, onFilesChange],\n );\n\n const setFiles = useCallback(\n (\n newFiles:\n | FileUploadItem[]\n | ((prev: FileUploadItem[]) => FileUploadItem[]),\n ) => {\n setFilesState((prev) => {\n const nextFiles =\n typeof newFiles === \"function\" ? newFiles(prev) : newFiles;\n onFilesChange?.(nextFiles);\n return nextFiles;\n });\n },\n [onFilesChange],\n );\n\n const retryUpload = useCallback(\n async (id: string) => {\n const file = files.find((f) => f.id === id);\n if (file && file.status === \"error\") {\n setFilesState((prev) =>\n prev.map((f) =>\n f.id === id\n ? { ...f, status: \"idle\" as const, error: undefined }\n : f,\n ),\n );\n await uploadSingleFile({ ...file, status: \"idle\", error: undefined });\n }\n },\n [files, uploadSingleFile],\n );\n\n const cancelUpload = useCallback((id: string) => {\n const controller = abortControllersRef.current.get(id);\n if (controller) {\n controller.abort();\n abortControllersRef.current.delete(id);\n }\n\n setFilesState((prev) =>\n prev.map((f) =>\n f.id === id ? { ...f, status: \"idle\" as const, progress: 0 } : f,\n ),\n );\n }, []);\n\n const handleDragEnter = useCallback((e: DragEvent<HTMLElement>) => {\n e.preventDefault();\n e.stopPropagation();\n setIsDragging(true);\n }, []);\n\n const handleDragLeave = useCallback((e: DragEvent<HTMLElement>) => {\n e.preventDefault();\n e.stopPropagation();\n if (e.currentTarget.contains(e.relatedTarget as Node)) return;\n setIsDragging(false);\n }, []);\n\n const handleDragOver = useCallback((e: DragEvent<HTMLElement>) => {\n e.preventDefault();\n e.stopPropagation();\n }, []);\n\n const handleDrop = useCallback(\n (e: DragEvent<HTMLElement>) => {\n e.preventDefault();\n e.stopPropagation();\n setIsDragging(false);\n\n if (inputRef.current?.disabled) return;\n\n if (e.dataTransfer.files?.length > 0) {\n if (!multiple) {\n addFiles([e.dataTransfer.files[0]]);\n } else {\n addFiles(e.dataTransfer.files);\n }\n }\n },\n [addFiles, multiple],\n );\n\n const handleFileChange = useCallback(\n (e: ChangeEvent<HTMLInputElement>) => {\n if (e.target.files?.length) {\n addFiles(e.target.files);\n }\n },\n [addFiles],\n );\n\n const openFileDialog = useCallback(() => {\n inputRef.current?.click();\n }, []);\n\n const getInputProps = useCallback(\n (props: InputHTMLAttributes<HTMLInputElement> = {}) => ({\n ...props,\n accept: props.accept || accept,\n multiple: props.multiple !== undefined ? props.multiple : multiple,\n onChange: handleFileChange,\n // biome-ignore lint/suspicious/noExplicitAny: intentional\n ref: inputRef as any,\n type: \"file\" as const,\n }),\n [accept, multiple, handleFileChange],\n );\n\n return {\n // State\n files,\n isDragging,\n isUploading,\n errors,\n\n // Core Actions\n addFiles,\n removeFile,\n clearFiles,\n clearErrors,\n\n // Form integration\n reset,\n setFiles,\n instanceKey,\n\n // Upload Actions\n uploadFiles,\n retryUpload,\n cancelUpload,\n\n // Drag/Drop + Input\n handleDragEnter,\n handleDragLeave,\n handleDragOver,\n handleDrop,\n handleFileChange,\n openFileDialog,\n getInputProps,\n };\n};\n\n// Legacy type exports for backwards compatibility\nexport type FileWithPreview = FileUploadItem;\nexport type FileWithUploadStatus = FileUploadItem;\nexport type FileUploadOptions = UseFileUploadOptions;\nexport type FileUploadWithUploaderOptions = UseFileUploadOptions;\n","import * as React from \"react\";\n\nconst MOBILE_BREAKPOINT = 768;\n\nexport function useIsMobile() {\n const [isMobile, setIsMobile] = React.useState<boolean | undefined>(\n undefined,\n );\n\n React.useEffect(() => {\n const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);\n const onChange = () => {\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n };\n mql.addEventListener(\"change\", onChange);\n setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n\n return !!isMobile;\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,89 @@
1
+ {
2
+ "name": "@px-ui/ui",
3
+ "version": "0.1.0",
4
+ "description": "A modern React component library built with Tailwind CSS and shadcn/ui",
5
+ "type": "module",
6
+ "module": "./dist/index.js",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "sideEffects": false,
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./styles.css": "./dist/index.css",
19
+ "./fonts.css": "./dist/assets/fonts.css"
20
+ },
21
+ "scripts": {
22
+ "lint": "eslint src --ext .ts,.tsx",
23
+ "build": "tsdown",
24
+ "dev": "tsdown --watch",
25
+ "typecheck": "tsc --noEmit",
26
+ "prepublishOnly": "pnpm run build"
27
+ },
28
+ "keywords": [
29
+ "react",
30
+ "components",
31
+ "ui",
32
+ "tailwind",
33
+ "design-system",
34
+ "shadcn"
35
+ ],
36
+ "author": "",
37
+ "license": "MIT",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/Pros-Perix/px-ui.git",
41
+ "directory": "packages/ui"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "peerDependencies": {
47
+ "@tanstack/react-query": ">=5.0.0",
48
+ "date-fns": ">2.0.0",
49
+ "react": ">=16.8.0",
50
+ "react-dom": ">=16.8.0"
51
+ },
52
+ "peerDependenciesMeta": {
53
+ "@tanstack/react-query": {
54
+ "optional": true
55
+ },
56
+ "date-fns": {
57
+ "optional": true
58
+ }
59
+ },
60
+ "dependencies": {
61
+ "@base-ui/react": "^1.5.0",
62
+ "class-variance-authority": "^0.7.1",
63
+ "clsx": "^2.1.1",
64
+ "lucide-react": "^1.16.0",
65
+ "shadcn": "^4.7.0",
66
+ "tailwind-merge": "^3.6.0",
67
+ "tw-animate-css": "^1.4.0",
68
+ "zod": "^3.25.76"
69
+ },
70
+ "devDependencies": {
71
+ "@eslint/js": "^9.39.2",
72
+ "@tanstack/react-query": "^5.90.1",
73
+ "@tsconfig/strictest": "^2.0.8",
74
+ "@types/node": "^25.1.0",
75
+ "@types/react": "^19.2.10",
76
+ "@types/react-dom": "^19.2.3",
77
+ "date-fns": "^4.1.0",
78
+ "eslint": "^9.39.2",
79
+ "eslint-plugin-react-hooks": "^7.0.1",
80
+ "eslint-plugin-react-refresh": "^0.4.24",
81
+ "globals": "^17.2.0",
82
+ "react": "19.2.0",
83
+ "react-dom": "19.2.0",
84
+ "tailwindcss": "^4.1.18",
85
+ "tsdown": "^0.16.4",
86
+ "typescript": "^5.9.3",
87
+ "typescript-eslint": "^8.54.0"
88
+ }
89
+ }