@px-ui/ui 1.0.3 → 1.0.5
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/{avatar-B2FY4pgk.js → avatar-B1kOPk5C.js} +4 -4
- package/dist/{avatar-B2FY4pgk.js.map → avatar-B1kOPk5C.js.map} +1 -1
- package/dist/{avatar-CtZEW1Kd.d.ts → avatar-CxSoP9EP.d.ts} +6 -6
- package/dist/{avatar-CtZEW1Kd.d.ts.map → avatar-CxSoP9EP.d.ts.map} +1 -1
- package/dist/{button-peTbHgr6.d.ts → button-CzxAq5Hs.d.ts} +7 -7
- package/dist/{button-peTbHgr6.d.ts.map → button-CzxAq5Hs.d.ts.map} +1 -1
- package/dist/{button-B3SNyz-U.js → button-hBUFMmsZ.js} +2 -2
- package/dist/{button-B3SNyz-U.js.map → button-hBUFMmsZ.js.map} +1 -1
- package/dist/{combobox-CBerynHz.js → combobox-C-dF33ce.js} +9 -9
- package/dist/{combobox-CBerynHz.js.map → combobox-C-dF33ce.js.map} +1 -1
- package/dist/{combobox-BGJkF5tM.d.ts → combobox-xkPhjyC8.d.ts} +18 -18
- package/dist/{combobox-BGJkF5tM.d.ts.map → combobox-xkPhjyC8.d.ts.map} +1 -1
- package/dist/components/accordion.js +1 -1
- package/dist/components/alert-dialog.d.ts +16 -16
- package/dist/components/alert-dialog.d.ts.map +1 -1
- package/dist/components/alert-dialog.js +5 -5
- package/dist/components/alert-dialog.js.map +1 -1
- package/dist/components/alert.d.ts +7 -7
- package/dist/components/alert.d.ts.map +1 -1
- package/dist/components/alert.js +2 -2
- package/dist/components/aspect-ratio.js +1 -1
- package/dist/components/avatar-group.d.ts +4 -4
- package/dist/components/avatar-group.js +5 -5
- package/dist/components/avatar.d.ts +1 -1
- package/dist/components/avatar.js +3 -3
- package/dist/components/badge.d.ts +4 -4
- package/dist/components/badge.d.ts.map +1 -1
- package/dist/components/badge.js +1 -1
- package/dist/components/breadcrumb.d.ts +7 -7
- package/dist/components/breadcrumb.js +2 -2
- package/dist/components/button-group.d.ts +9 -9
- package/dist/components/button-group.js +2 -2
- package/dist/components/button.d.ts +1 -1
- package/dist/components/button.js +2 -2
- package/dist/components/calendar.d.ts +4 -4
- package/dist/components/calendar.js +3 -3
- package/dist/components/card.d.ts +8 -8
- package/dist/components/card.js +2 -2
- package/dist/components/carousel.d.ts +7 -7
- package/dist/components/carousel.d.ts.map +1 -1
- package/dist/components/carousel.js +3 -3
- package/dist/components/chart.d.ts +5 -5
- package/dist/components/chart.js +2 -2
- package/dist/components/checkbox.d.ts +2 -2
- package/dist/components/checkbox.js +1 -1
- package/dist/components/collapsible.d.ts +4 -4
- package/dist/components/collapsible.js +1 -1
- package/dist/components/combobox.d.ts +1 -1
- package/dist/components/combobox.js +11 -11
- package/dist/components/command.d.ts +11 -11
- package/dist/components/command.js +7 -7
- package/dist/components/context-menu.d.ts +16 -16
- package/dist/components/context-menu.js +2 -2
- package/dist/components/currency-select-field.d.ts +3 -3
- package/dist/components/currency-select-field.js +12 -12
- package/dist/components/dialog.d.ts +11 -11
- package/dist/components/dialog.d.ts.map +1 -1
- package/dist/components/dialog.js +3 -3
- package/dist/components/direction.js +1 -1
- package/dist/components/drawer.d.ts +11 -11
- package/dist/components/drawer.d.ts.map +1 -1
- package/dist/components/drawer.js +2 -2
- package/dist/components/dropdown-menu.d.ts +1 -1
- package/dist/components/dropdown-menu.js +2 -2
- package/dist/components/empty.d.ts +9 -9
- package/dist/components/empty.d.ts.map +1 -1
- package/dist/components/empty.js +1 -1
- package/dist/components/field.d.ts +16 -16
- package/dist/components/field.d.ts.map +1 -1
- package/dist/components/field.js +4 -4
- package/dist/components/file-upload-field.d.ts +2 -2
- package/dist/components/file-upload-field.js +6 -6
- package/dist/components/file-upload.d.ts +2 -2
- package/dist/components/file-upload.js +4 -4
- package/dist/components/hover-card.d.ts +4 -4
- package/dist/components/hover-card.d.ts.map +1 -1
- package/dist/components/hover-card.js +1 -1
- package/dist/components/input-group.d.ts +11 -11
- package/dist/components/input-group.d.ts.map +1 -1
- package/dist/components/input-group.js +5 -5
- package/dist/components/input-otp.d.ts +5 -5
- package/dist/components/input-otp.d.ts.map +1 -1
- package/dist/components/input-otp.js +2 -2
- package/dist/components/input.d.ts +1 -1
- package/dist/components/input.js +2 -2
- package/dist/components/item.d.ts +15 -15
- package/dist/components/item.d.ts.map +1 -1
- package/dist/components/item.js +3 -3
- package/dist/components/kbd.d.ts +3 -3
- package/dist/components/kbd.js +1 -1
- package/dist/components/label.d.ts +1 -1
- package/dist/components/label.js +2 -2
- package/dist/components/menubar.d.ts +18 -18
- package/dist/components/menubar.js +3 -3
- package/dist/components/native-select.d.ts +4 -4
- package/dist/components/native-select.js +2 -2
- package/dist/components/navigation-menu.d.ts +11 -11
- package/dist/components/navigation-menu.d.ts.map +1 -1
- package/dist/components/navigation-menu.js +1 -1
- package/dist/components/pagination.d.ts +9 -9
- package/dist/components/pagination.d.ts.map +1 -1
- package/dist/components/pagination.js +3 -3
- package/dist/components/phone-input.d.ts +2 -2
- package/dist/components/phone-input.d.ts.map +1 -1
- package/dist/components/phone-input.js +4 -4
- package/dist/components/phone-input.js.map +1 -1
- package/dist/components/popover.d.ts +7 -7
- package/dist/components/popover.d.ts.map +1 -1
- package/dist/components/popover.js +2 -2
- package/dist/components/progress.d.ts +6 -6
- package/dist/components/progress.d.ts.map +1 -1
- package/dist/components/progress.js +1 -1
- package/dist/components/radio-group.d.ts +3 -3
- package/dist/components/radio-group.d.ts.map +1 -1
- package/dist/components/radio-group.js +1 -1
- package/dist/components/resizable.d.ts +4 -4
- package/dist/components/resizable.js +1 -1
- package/dist/components/scroll-area.d.ts +3 -3
- package/dist/components/scroll-area.js +2 -2
- package/dist/components/select.d.ts +10 -10
- package/dist/components/select.js +2 -2
- package/dist/components/separator.d.ts +1 -1
- package/dist/components/separator.js +2 -2
- package/dist/components/sheet.d.ts +9 -9
- package/dist/components/sheet.js +3 -3
- package/dist/components/sidebar.d.ts +26 -26
- package/dist/components/sidebar.d.ts.map +1 -1
- package/dist/components/sidebar.js +9 -9
- package/dist/components/skeleton.d.ts +2 -2
- package/dist/components/skeleton.js +2 -2
- package/dist/components/slider.d.ts +2 -2
- package/dist/components/slider.js +1 -1
- package/dist/components/spinner.d.ts +2 -2
- package/dist/components/spinner.js +2 -2
- package/dist/components/switch.d.ts +2 -2
- package/dist/components/switch.js +1 -1
- package/dist/components/table.d.ts +9 -9
- package/dist/components/table.js +2 -2
- package/dist/components/tabs.d.ts +8 -8
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +1 -1
- package/dist/components/textarea.d.ts +2 -2
- package/dist/components/textarea.d.ts.map +1 -1
- package/dist/components/textarea.js +2 -2
- package/dist/components/toast.d.ts +1 -1
- package/dist/components/toast.js +2 -2
- package/dist/components/toggle-group.d.ts +4 -4
- package/dist/components/toggle-group.js +3 -3
- package/dist/components/toggle.d.ts +1 -1
- package/dist/components/toggle.js +2 -2
- package/dist/components/tooltip.d.ts +1 -1
- package/dist/components/tooltip.js +2 -2
- package/dist/{dialog-6oTB2jEH.js → dialog-DrdXva1a.js} +4 -4
- package/dist/{dialog-6oTB2jEH.js.map → dialog-DrdXva1a.js.map} +1 -1
- package/dist/{dropdown-menu-CN7gHXiF.js → dropdown-menu-CcumvwhM.js} +3 -3
- package/dist/{dropdown-menu-CN7gHXiF.js.map → dropdown-menu-CcumvwhM.js.map} +1 -1
- package/dist/{dropdown-menu-z9weMtW6.d.ts → dropdown-menu-CgS0xs0A.d.ts} +17 -17
- package/dist/{dropdown-menu-z9weMtW6.d.ts.map → dropdown-menu-CgS0xs0A.d.ts.map} +1 -1
- package/dist/{file-upload-CHIguMmH.d.ts → file-upload-CklY15-S.d.ts} +12 -12
- package/dist/{file-upload-CHIguMmH.d.ts.map → file-upload-CklY15-S.d.ts.map} +1 -1
- package/dist/{file-upload-CPaU1bTN.js → file-upload-DeCIv13t.js} +5 -5
- package/dist/{file-upload-CPaU1bTN.js.map → file-upload-DeCIv13t.js.map} +1 -1
- package/dist/hooks/use-async-options.js +1 -1
- package/dist/hooks/use-debounce.js +1 -1
- package/dist/hooks/use-file-upload.js +1 -1
- package/dist/hooks/use-infinite-scroll.js +2 -2
- package/dist/hooks/use-intersection-observer.js +1 -1
- package/dist/hooks/use-mobile.js +1 -1
- package/dist/{input-ByI__4Tb.js → input-8jqdnUZl.js} +3 -3
- package/dist/{input-ByI__4Tb.js.map → input-8jqdnUZl.js.map} +1 -1
- package/dist/input-Yb6j16S2.d.ts +12 -0
- package/dist/input-Yb6j16S2.d.ts.map +1 -0
- package/dist/{input-group-Dw3LpMMS.js → input-group-DqEkmpXW.js} +6 -6
- package/dist/{input-group-Dw3LpMMS.js.map → input-group-DqEkmpXW.js.map} +1 -1
- package/dist/{label-DJ-ONU1d.js → label-AaH9ZYCZ.js} +3 -3
- package/dist/{label-DJ-ONU1d.js.map → label-AaH9ZYCZ.js.map} +1 -1
- package/dist/label-iMKAd7JB.d.ts +11 -0
- package/dist/label-iMKAd7JB.d.ts.map +1 -0
- package/dist/lib/utils.js +1 -1
- package/dist/{popover-z4aFoVCA.js → popover-C9D43EuT.js} +3 -3
- package/dist/{popover-z4aFoVCA.js.map → popover-C9D43EuT.js.map} +1 -1
- package/dist/providers/px-ui-provider.d.ts +4 -4
- package/dist/providers/px-ui-provider.d.ts.map +1 -1
- package/dist/providers/px-ui-provider.js +8 -5
- package/dist/providers/px-ui-provider.js.map +1 -1
- package/dist/{separator-Cay2tYJi.js → separator-BS3ilczC.js} +2 -2
- package/dist/{separator-Cay2tYJi.js.map → separator-BS3ilczC.js.map} +1 -1
- package/dist/{separator-7n2clBT1.d.ts → separator-_9aINuaq.d.ts} +3 -3
- package/dist/{separator-7n2clBT1.d.ts.map → separator-_9aINuaq.d.ts.map} +1 -1
- package/dist/{sheet-BLGcp2vY.js → sheet-CLjNDrfL.js} +4 -4
- package/dist/{sheet-BLGcp2vY.js.map → sheet-CLjNDrfL.js.map} +1 -1
- package/dist/{skeleton-Cpayevuv.js → skeleton-BcIxI5Wq.js} +2 -2
- package/dist/{skeleton-Cpayevuv.js.map → skeleton-BcIxI5Wq.js.map} +1 -1
- package/dist/{spinner-EaERkwNt.js → spinner-Bo0sUZEg.js} +2 -2
- package/dist/{spinner-EaERkwNt.js.map → spinner-Bo0sUZEg.js.map} +1 -1
- package/dist/{textarea-CD4lb4bT.js → textarea-Cq0o323P.js} +3 -3
- package/dist/{textarea-CD4lb4bT.js.map → textarea-Cq0o323P.js.map} +1 -1
- package/dist/{toast-BIAksZXy.js → toast-COWAQbIk.js} +3 -3
- package/dist/{toast-BIAksZXy.js.map → toast-COWAQbIk.js.map} +1 -1
- package/dist/{toast-C3bNvyqf.d.ts → toast-Dnn2IfCp.d.ts} +4 -4
- package/dist/{toast-C3bNvyqf.d.ts.map → toast-Dnn2IfCp.d.ts.map} +1 -1
- package/dist/{toggle-Cwsbk1jY.js → toggle-DhJ_qwgw.js} +2 -2
- package/dist/{toggle-Cwsbk1jY.js.map → toggle-DhJ_qwgw.js.map} +1 -1
- package/dist/{toggle-E0JXUyzs.d.ts → toggle-DyQHyjuR.d.ts} +5 -5
- package/dist/{toggle-E0JXUyzs.d.ts.map → toggle-DyQHyjuR.d.ts.map} +1 -1
- package/dist/{tooltip-CIwyqk_3.js → tooltip-DkPxCJIL.js} +2 -2
- package/dist/{tooltip-CIwyqk_3.js.map → tooltip-DkPxCJIL.js.map} +1 -1
- package/dist/{tooltip-ChzCP6Bv.d.ts → tooltip-aifJKLEQ.d.ts} +6 -6
- package/dist/{tooltip-ChzCP6Bv.d.ts.map → tooltip-aifJKLEQ.d.ts.map} +1 -1
- package/dist/{use-async-options-MznolA0k.js → use-async-options-wZ9LJ2BJ.js} +1 -1
- package/dist/{use-async-options-MznolA0k.js.map → use-async-options-wZ9LJ2BJ.js.map} +1 -1
- package/dist/{use-debounce-CwnVqtDF.js → use-debounce-_qaEZqtt.js} +1 -1
- package/dist/{use-debounce-CwnVqtDF.js.map → use-debounce-_qaEZqtt.js.map} +1 -1
- package/dist/{use-file-upload-CiAqUFFh.js → use-file-upload-Dxrej28u.js} +1 -1
- package/dist/{use-file-upload-CiAqUFFh.js.map → use-file-upload-Dxrej28u.js.map} +1 -1
- package/dist/{use-infinite-scroll-DHi7CKyt.js → use-infinite-scroll-CpbhGGT5.js} +2 -2
- package/dist/{use-infinite-scroll-DHi7CKyt.js.map → use-infinite-scroll-CpbhGGT5.js.map} +1 -1
- package/dist/{use-intersection-observer-h5ZpX18y.js → use-intersection-observer-CZfnep4z.js} +1 -1
- package/dist/{use-intersection-observer-h5ZpX18y.js.map → use-intersection-observer-CZfnep4z.js.map} +1 -1
- package/dist/{use-mobile-CFSCadSm.js → use-mobile-l5ebOdQl.js} +1 -1
- package/dist/{use-mobile-CFSCadSm.js.map → use-mobile-l5ebOdQl.js.map} +1 -1
- package/dist/{utils-8o789qxH.js → utils-mWJR2N24.js} +1 -1
- package/dist/{utils-8o789qxH.js.map → utils-mWJR2N24.js.map} +1 -1
- package/package.json +1 -1
- package/dist/input-BY_HLiBI.d.ts +0 -12
- package/dist/input-BY_HLiBI.d.ts.map +0 -1
- package/dist/label-C3yvmi-S.d.ts +0 -11
- package/dist/label-C3yvmi-S.d.ts.map +0 -1
- /package/dist/{direction-CB0IW0WZ.js → direction-BKSh6e6q.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-debounce-
|
|
1
|
+
{"version":3,"file":"use-debounce-_qaEZqtt.js","names":[],"sources":["../src/hooks/use-debounce.ts"],"sourcesContent":["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"],"mappings":";;;AAEA,SAAwB,YACtB,UACA,OACA,MACA;CACA,MAAM,UAAU,OAA6C,KAAK;CAClE,MAAM,KAAK,eAAe,SAAS;AAEnC,iBAAgB;AACd,MAAI,QAAQ,QACV,cAAa,QAAQ,QAAQ;AAG/B,UAAQ,UAAU,iBAAiB;AACjC,OAAI;AACJ,WAAQ,UAAU;KACjB,MAAM;AAET,eAAa;AACX,OAAI,QAAQ,SAAS;AACnB,iBAAa,QAAQ,QAAQ;AAC7B,YAAQ,UAAU;;;IAGrB,CAAC,GAAG,MAAM,MAAM,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-file-upload-CiAqUFFh.js","names":["uploadedFile: FileUploadItem","failedFile: FileUploadItem","results: FileUploadItem[]","newErrors: string[]","validFiles: FileUploadItem[]","initializedFiles: FileUploadItem[]"],"sources":["../src/hooks/use-file-upload.ts"],"sourcesContent":["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"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"use-file-upload-Dxrej28u.js","names":["uploadedFile: FileUploadItem","failedFile: FileUploadItem","results: FileUploadItem[]","newErrors: string[]","validFiles: FileUploadItem[]","initializedFiles: FileUploadItem[]"],"sources":["../src/hooks/use-file-upload.ts"],"sourcesContent":["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"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as useIntersectionObserver } from "./use-intersection-observer-
|
|
1
|
+
import { t as useIntersectionObserver } from "./use-intersection-observer-CZfnep4z.js";
|
|
2
2
|
import { useEffect, useEffectEvent } from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/hooks/use-infinite-scroll.ts
|
|
@@ -35,4 +35,4 @@ var use_infinite_scroll_default = useInfiniteScroll;
|
|
|
35
35
|
|
|
36
36
|
//#endregion
|
|
37
37
|
export { use_infinite_scroll_default as t };
|
|
38
|
-
//# sourceMappingURL=use-infinite-scroll-
|
|
38
|
+
//# sourceMappingURL=use-infinite-scroll-CpbhGGT5.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-infinite-scroll-
|
|
1
|
+
{"version":3,"file":"use-infinite-scroll-CpbhGGT5.js","names":[],"sources":["../src/hooks/use-infinite-scroll.ts"],"sourcesContent":["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\" | \"root\"\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 root,\n disabled,\n threshold = 0,\n}: UseInfiniteScrollHookArgs) {\n const [ref, entry] = useIntersectionObserver({\n rootMargin,\n threshold,\n root,\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"],"mappings":";;;;AAMA,MAAM,sBAAsB;AAY5B,SAAS,kBAAkB,EACzB,eACA,SACA,YACA,YACA,MACA,UACA,YAAY,KACgB;CAC5B,MAAM,CAAC,KAAK,SAAS,wBAAwB;EAC3C;EACA;EACA;EACD,CAAC;CACF,MAAM,YAAY,QAAQ,OAAO,eAAe;CAEhD,MAAM,kBAAkB,qBAAqB;AAC3C,cAAY;GACZ;AAEF,iBAAgB;AACd,MAAI,SAAU;AAEd,MAAI,aAAa,WAAW,CAAC,iBAAiB,CAAC,UAAU;GACvD,MAAM,QAAQ,iBAAiB;AAC7B,qBAAiB;MAChB,oBAAoB;AAEvB,gBAAa;AACX,iBAAa,MAAM;;;IAGtB;EAAC;EAAW;EAAe;EAAS;EAAS,CAAC;AAEjD,QAAO,CAAC,IAAI;;AAGd,kCAAe"}
|
package/dist/{use-intersection-observer-h5ZpX18y.js.map → use-intersection-observer-CZfnep4z.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-intersection-observer-
|
|
1
|
+
{"version":3,"file":"use-intersection-observer-CZfnep4z.js","names":["entry"],"sources":["../src/hooks/use-intersection-observer.ts"],"sourcesContent":["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"],"mappings":";;;AAaA,SAAgB,wBACd,UAA0C,EAAE,EACb;CAC/B,MAAM,EAAE,YAAY,GAAG,OAAO,MAAM,aAAa,UAAU;CAC3D,MAAM,CAAC,OAAO,YAAY,MAAM,SAC9B,KACD;CAED,MAAM,mBAAmB,MAAM,OAAoC,KAAK;AAwBxE,QAAO,CAtBW,MAAM,aACrB,SAAyB;AACxB,MAAI,iBAAiB,SAAS;AAC5B,oBAAiB,QAAQ,YAAY;AACrC,oBAAiB,UAAU;;AAG7B,MAAI,MAAM,aAAa,KAAK,cAAc;GACxC,MAAM,WAAW,IAAI,sBAClB,CAACA,aAAwC;AACxC,aAASA,WAAS,KAAK;MAEzB;IAAE;IAAW;IAAM;IAAY,CAChC;AAED,YAAS,QAAQ,KAAK;AACtB,oBAAiB,UAAU;;IAG/B;EAAC;EAAW;EAAM;EAAW,CAC9B,EAEkB,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-mobile-
|
|
1
|
+
{"version":3,"file":"use-mobile-l5ebOdQl.js","names":["React"],"sources":["../src/hooks/use-mobile.ts"],"sourcesContent":["import * as React from \"react\"\n\nconst MOBILE_BREAKPOINT = 768\n\nexport function useIsMobile() {\n const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)\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":";;;AAEA,MAAM,oBAAoB;AAE1B,SAAgB,cAAc;CAC5B,MAAM,CAAC,UAAU,eAAeA,QAAM,SAA8B,OAAU;AAE9E,SAAM,gBAAgB;EACpB,MAAM,MAAM,OAAO,WAAW,eAAe,oBAAoB,EAAE,KAAK;EACxE,MAAM,iBAAiB;AACrB,eAAY,OAAO,aAAa,kBAAkB;;AAEpD,MAAI,iBAAiB,UAAU,SAAS;AACxC,cAAY,OAAO,aAAa,kBAAkB;AAClD,eAAa,IAAI,oBAAoB,UAAU,SAAS;IACvD,EAAE,CAAC;AAEN,QAAO,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils-
|
|
1
|
+
{"version":3,"file":"utils-mWJR2N24.js","names":[],"sources":["../src/lib/utils.ts"],"sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { extendTailwindMerge } from \"tailwind-merge\"\n\n// Custom tailwind-merge instance that knows about px-ui's bespoke utilities so\n// conflicting classes still collapse to the last one. Extend this whenever a\n// new custom utility/token is added to `globals.css` (mirror the `--*` token\n// name into the matching class group below).\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n // Custom font families (--font-heading, --font-sans-light, …)\n \"font-family\": [\n { font: [\"heading\", \"sans-light\", \"sans-sb\", \"sans-b\"] },\n ],\n // Conflict-isolation letter-spacing tokens (--tracking-ppx-*)\n tracking: [\n {\n tracking: [\n \"ppx-tighter\",\n \"ppx-tight\",\n \"ppx-normal\",\n \"ppx-wide\",\n \"ppx-wider\",\n \"ppx-widest\",\n ],\n },\n ],\n // Extended radius scale (--radius-4xl)\n rounded: [{ rounded: [\"4xl\"] }],\n },\n },\n})\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],"mappings":";;;;AAOA,MAAM,UAAU,oBAAoB,EAClC,QAAQ,EACN,aAAa;CAEX,eAAe,CACb,EAAE,MAAM;EAAC;EAAW;EAAc;EAAW;EAAS,EAAE,CACzD;CAED,UAAU,CACR,EACE,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;EACD,EACF,CACF;CAED,SAAS,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;CAChC,EACF,EACF,CAAC;AAEF,SAAgB,GAAG,GAAG,QAAsB;AAC1C,QAAO,QAAQ,KAAK,OAAO,CAAC"}
|
package/package.json
CHANGED
package/dist/input-BY_HLiBI.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import * as React$1 from "react";
|
|
2
|
-
import * as react_jsx_runtime121 from "react/jsx-runtime";
|
|
3
|
-
|
|
4
|
-
//#region src/components/input.d.ts
|
|
5
|
-
declare function Input({
|
|
6
|
-
className,
|
|
7
|
-
type,
|
|
8
|
-
...props
|
|
9
|
-
}: React$1.ComponentProps<"input">): react_jsx_runtime121.JSX.Element;
|
|
10
|
-
//#endregion
|
|
11
|
-
export { Input as t };
|
|
12
|
-
//# sourceMappingURL=input-BY_HLiBI.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"input-BY_HLiBI.d.ts","names":[],"sources":["../src/components/input.tsx"],"sourcesContent":[],"mappings":";;;;iBAKS,KAAA;;;;GAAqC,OAAA,CAAM,0BAAuB,oBAAA,CAAA,GAAA,CAAA"}
|
package/dist/label-C3yvmi-S.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import * as React$1 from "react";
|
|
2
|
-
import * as react_jsx_runtime80 from "react/jsx-runtime";
|
|
3
|
-
|
|
4
|
-
//#region src/components/label.d.ts
|
|
5
|
-
declare function Label({
|
|
6
|
-
className,
|
|
7
|
-
...props
|
|
8
|
-
}: React$1.ComponentProps<"label">): react_jsx_runtime80.JSX.Element;
|
|
9
|
-
//#endregion
|
|
10
|
-
export { Label as t };
|
|
11
|
-
//# sourceMappingURL=label-C3yvmi-S.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"label-C3yvmi-S.d.ts","names":[],"sources":["../src/components/label.tsx"],"sourcesContent":[],"mappings":";;;;iBAMS,KAAA;;;GAA+B,OAAA,CAAM,0BAAuB,mBAAA,CAAA,GAAA,CAAA"}
|
|
File without changes
|