@burdenoff/microfe-files 2026.830.2 → 2026.1013.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"FileInfoPanel.js","names":[],"sources":["../../../src/files/components/FileInfoPanel.tsx"],"sourcesContent":["import { useEffect, useEffectEvent, useState, lazy, Suspense } from \"react\";\nimport {\n X,\n FileText,\n Calendar,\n User,\n HardDrive,\n Link as LinkIcon,\n Clock,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { GlassCard, ShowMore } from \"@burdenoff/fe-libs/ui\";\nimport type { FileItem, FolderItem } from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\n// Lazy load FileVersionPanel to avoid loading heavy dependencies until needed\nconst FileVersionPanel = lazy(() =>\n import(\"./FileVersionPanel\").then((m) => ({ default: m.FileVersionPanel })),\n);\n\ninterface FileInfoPanelProps {\n item: FileItem | FolderItem;\n type: \"file\" | \"folder\";\n onClose: () => void;\n}\n\nexport function FileInfoPanel({ item, type, onClose }: FileInfoPanelProps) {\n const { t } = useI18n();\n const isFile = type === \"file\";\n const file = isFile ? (item as FileItem) : null;\n const folder = !isFile ? (item as FolderItem) : null;\n const [showVersionHistory, setShowVersionHistory] = useState(false);\n\n const onEscape = useEffectEvent(() => {\n if (showVersionHistory) {\n setShowVersionHistory(false);\n return;\n }\n onClose();\n });\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n onEscape();\n }\n };\n\n document.addEventListener(\"keydown\", handleEscape);\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, []);\n\n const formatDate = (date: string) => {\n return new Date(date).toLocaleString(\"en-US\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n };\n\n const formatSize = (bytes: number) => {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`;\n return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;\n };\n\n return (\n <div\n className=\"fixed inset-y-0 right-0 z-[9999] w-full max-w-full overflow-y-auto border-l border-border-seam bg-bg-surface shadow-2xl animate-in slide-in-from-right duration-200 sm:w-96 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]\"\n role=\"dialog\"\n aria-modal=\"false\"\n aria-label={\n isFile\n ? tWithFallback(t, \"files.info.fileInfo\", \"File information\")\n : tWithFallback(t, \"files.info.folderInfo\", \"Folder information\")\n }\n >\n {/* Header */}\n <div className=\"sticky top-0 z-10 bg-bg-surface border-b border-border-seam px-6 py-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {isFile\n ? tWithFallback(t, \"files.info.fileInfo\", \"File Information\")\n : tWithFallback(t, \"files.info.folderInfo\", \"Folder Information\")}\n </h2>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1.5 rounded-md border border-border-default text-text-secondary bg-bg-elevated hover:bg-action-ghost-bg-hover hover:text-text-primary transition-colors shadow-sm\"\n aria-label={tWithFallback(\n t,\n \"files.info.closePanel\",\n \"Close information panel\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"p-6 space-y-6\">\n {/* Preview/Icon */}\n <GlassCard\n treatment=\"metric\"\n className=\"flex items-center justify-center py-8\"\n >\n {isFile && file?.mimeType.startsWith(\"image/\") ? (\n <img\n src={file.downloadUrl || file.url}\n alt={item.name}\n className=\"max-w-full max-h-48 object-contain rounded\"\n />\n ) : (\n <FileText className=\"size-20 text-text-muted\" />\n )}\n </GlassCard>\n\n {/* Name */}\n <div>\n <h3 className=\"text-xl font-semibold text-text-primary break-words\">\n {item.name}\n </h3>\n {item.description && (\n <p className=\"mt-2 text-sm text-text-secondary\">\n {item.description}\n </p>\n )}\n </div>\n\n {/* Details */}\n <div className=\"space-y-4\">\n <div className=\"flex items-start gap-3\">\n <FileText className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.type\", \"Type\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {isFile\n ? file?.mimeType ||\n tWithFallback(t, \"files.info.unknown\", \"Unknown\")\n : tWithFallback(t, \"files.info.folder\", \"Folder\")}\n </p>\n </div>\n </div>\n\n {isFile && file && (\n <div className=\"flex items-start gap-3\">\n <HardDrive className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.size\", \"Size\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {formatSize(file.size)}\n </p>\n </div>\n </div>\n )}\n\n {!isFile && folder && (\n <>\n <div className=\"flex items-start gap-3\">\n <FileText className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.contents\", \"Contents\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {folder.filesCount}{\" \"}\n {tWithFallback(t, \"files.info.files\", \"files\")},{\" \"}\n {folder.subfoldersCount}{\" \"}\n {tWithFallback(t, \"files.info.folders\", \"folders\")}\n </p>\n </div>\n </div>\n\n {folder.path && (\n <div className=\"flex items-start gap-3\">\n <LinkIcon className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.path\", \"Path\")}\n </p>\n <p className=\"text-sm text-text-secondary break-all\">\n {folder.path}\n </p>\n </div>\n </div>\n )}\n </>\n )}\n\n <div className=\"flex items-start gap-3\">\n <Calendar className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.created\", \"Created\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {formatDate(item.createdAt)}\n </p>\n </div>\n </div>\n\n {item.updatedAt && (\n <div className=\"flex items-start gap-3\">\n <Calendar className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.modified\", \"Modified\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {formatDate(item.updatedAt)}\n </p>\n </div>\n </div>\n )}\n\n <div className=\"flex items-start gap-3\">\n <User className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.owner\", \"Owner\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {item.createdBy?.name ||\n item.createdById ||\n tWithFallback(t, \"files.info.unknown\", \"Unknown\")}\n </p>\n </div>\n </div>\n </div>\n\n {/* Additional Info — secondary properties tucked behind progressive\n disclosure to keep the rail focused on the essentials. */}\n <div className=\"pt-6 border-t border-border-seam\">\n <h4 className=\"text-sm font-semibold text-text-primary mb-3\">\n {tWithFallback(t, \"files.info.properties\", \"Properties\")}\n </h4>\n <ShowMore collapsedHeight={56}>\n <dl className=\"space-y-2\">\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-text-secondary\">\n {tWithFallback(t, \"files.info.status\", \"Status\")}\n </dt>\n <dd className=\"text-sm font-medium text-text-primary\">\n {item.status}\n </dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-text-secondary\">\n {tWithFallback(t, \"files.info.visibility\", \"Visibility\")}\n </dt>\n <dd className=\"text-sm font-medium text-text-primary\">\n {item.visibility}\n </dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-text-secondary\">\n {tWithFallback(t, \"files.info.starred\", \"Starred\")}\n </dt>\n <dd className=\"text-sm font-medium text-text-primary\">\n {item.starred\n ? tWithFallback(t, \"files.info.yes\", \"Yes\")\n : tWithFallback(t, \"files.info.no\", \"No\")}\n </dd>\n </div>\n </dl>\n </ShowMore>\n </div>\n\n {/* Version History Button (Files only) */}\n {isFile && file && (\n <div className=\"pt-6 border-t border-border-seam\">\n <button\n type=\"button\"\n onClick={() => setShowVersionHistory(true)}\n className=\"w-full flex items-center justify-center gap-2 px-4 py-3 text-sm font-medium text-action-primary-text bg-action-primary-bg/10 hover:bg-action-primary-bg/20 rounded-lg transition-all duration-200\"\n >\n <Clock className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.info.viewVersionHistory\",\n \"View Version History\",\n )}\n </button>\n </div>\n )}\n </div>\n\n {/* Version History Modal */}\n {isFile && file && showVersionHistory && (\n <Suspense\n fallback={\n <div className=\"fixed inset-0 z-50 bg-bg-overlay flex items-center justify-center\">\n <div className=\"bg-bg-surface rounded-lg p-8 animate-in fade-in zoom-in duration-200\">\n <div className=\"flex items-center gap-3\">\n <Clock className=\"size-6 text-action-primary-bg animate-spin\" />\n <span className=\"text-text-primary\">\n {tWithFallback(\n t,\n \"files.info.loadingVersionHistory\",\n \"Loading version history...\",\n )}\n </span>\n </div>\n </div>\n </div>\n }\n >\n <FileVersionPanel\n file={file}\n onClose={() => setShowVersionHistory(false)}\n />\n </Suspense>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"FileInfoPanel.js","names":[],"sources":["../../../src/files/components/FileInfoPanel.tsx"],"sourcesContent":["import { useEffect, useEffectEvent, useState, lazy, Suspense } from \"react\";\nimport {\n X,\n FileText,\n Calendar,\n User,\n HardDrive,\n Link as LinkIcon,\n Clock,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { GlassCard, ShowMore } from \"@burdenoff/fe-libs/ui\";\nimport type { FileItem, FolderItem } from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\n// Lazy load FileVersionPanel to avoid loading heavy dependencies until needed\nconst FileVersionPanel = lazy(() =>\n import(\"./FileVersionPanel\").then((m) => ({ default: m.FileVersionPanel })),\n);\n\ninterface FileInfoPanelProps {\n item: FileItem | FolderItem;\n type: \"file\" | \"folder\";\n onClose: () => void;\n}\n\nexport function FileInfoPanel({ item, type, onClose }: FileInfoPanelProps) {\n const { t } = useI18n();\n const isFile = type === \"file\";\n const file = isFile ? (item as FileItem) : null;\n const folder = !isFile ? (item as FolderItem) : null;\n const [showVersionHistory, setShowVersionHistory] = useState(false);\n\n const onEscape = useEffectEvent(() => {\n if (showVersionHistory) {\n setShowVersionHistory(false);\n return;\n }\n onClose();\n });\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n onEscape();\n }\n };\n\n document.addEventListener(\"keydown\", handleEscape);\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, []);\n\n const formatDate = (date: string) => {\n return new Date(date).toLocaleString(\"en-US\", {\n year: \"numeric\",\n month: \"long\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n };\n\n const formatSize = (bytes: number) => {\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`;\n if (bytes < 1024 * 1024 * 1024)\n return `${(bytes / 1024 / 1024).toFixed(2)} MB`;\n return `${(bytes / 1024 / 1024 / 1024).toFixed(2)} GB`;\n };\n\n return (\n <div\n className=\"fixed inset-y-0 right-0 z-[9999] w-full max-w-full overflow-y-auto border-l border-border-seam bg-bg-surface shadow-2xl animate-in slide-in-from-right duration-200 sm:w-96 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]\"\n role=\"dialog\"\n aria-modal=\"false\"\n aria-label={\n isFile\n ? tWithFallback(t, \"files.info.fileInfo\", \"File information\")\n : tWithFallback(t, \"files.info.folderInfo\", \"Folder information\")\n }\n >\n {/* Header */}\n <div className=\"sticky top-0 z-10 bg-bg-surface border-b border-border-seam px-6 py-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {isFile\n ? tWithFallback(t, \"files.info.fileInfo\", \"File Information\")\n : tWithFallback(t, \"files.info.folderInfo\", \"Folder Information\")}\n </h2>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1.5 rounded-md border border-border-default text-text-secondary bg-bg-elevated hover:bg-action-ghost-bg-hover hover:text-text-primary transition-colors shadow-sm\"\n aria-label={tWithFallback(\n t,\n \"files.info.closePanel\",\n \"Close information panel\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"p-6 space-y-6\">\n {/* Preview/Icon */}\n <GlassCard\n treatment=\"metric\"\n className=\"flex items-center justify-center py-8\"\n >\n {isFile && file?.mimeType.startsWith(\"image/\") ? (\n <img\n src={file.downloadUrl || file.url}\n alt={item.name}\n className=\"max-w-full max-h-48 object-contain rounded\"\n />\n ) : (\n <FileText className=\"size-20 text-text-muted\" />\n )}\n </GlassCard>\n\n {/* Name */}\n <div>\n <h3 className=\"text-xl font-semibold text-text-primary break-words\">\n {item.name}\n </h3>\n {item.description && (\n <p className=\"mt-2 text-sm text-text-secondary\">\n {item.description}\n </p>\n )}\n </div>\n\n {/* Details */}\n <div className=\"space-y-4\">\n <div className=\"flex items-start gap-3\">\n <FileText className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.type\", \"Type\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {isFile\n ? file?.mimeType ||\n tWithFallback(t, \"files.info.unknown\", \"Unknown\")\n : tWithFallback(t, \"files.info.folder\", \"Folder\")}\n </p>\n </div>\n </div>\n\n {isFile && file && (\n <div className=\"flex items-start gap-3\">\n <HardDrive className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.size\", \"Size\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {formatSize(file.size)}\n </p>\n </div>\n </div>\n )}\n\n {!isFile && folder && (\n <>\n <div className=\"flex items-start gap-3\">\n <FileText className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.contents\", \"Contents\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {folder.filesCount}{\" \"}\n {tWithFallback(t, \"files.info.files\", \"files\")},{\" \"}\n {folder.subfoldersCount}{\" \"}\n {tWithFallback(t, \"files.info.folders\", \"folders\")}\n </p>\n </div>\n </div>\n\n {folder.path && (\n <div className=\"flex items-start gap-3\">\n <LinkIcon className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.path\", \"Path\")}\n </p>\n <p className=\"text-sm text-text-secondary break-all\">\n {folder.path}\n </p>\n </div>\n </div>\n )}\n </>\n )}\n\n <div className=\"flex items-start gap-3\">\n <Calendar className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.created\", \"Created\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {formatDate(item.createdAt)}\n </p>\n </div>\n </div>\n\n {item.updatedAt && (\n <div className=\"flex items-start gap-3\">\n <Calendar className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.modified\", \"Modified\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {formatDate(item.updatedAt)}\n </p>\n </div>\n </div>\n )}\n\n <div className=\"flex items-start gap-3\">\n <User className=\"size-5 text-text-muted mt-0.5\" />\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tWithFallback(t, \"files.info.owner\", \"Owner\")}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {item.createdBy?.name ||\n item.createdById ||\n tWithFallback(t, \"files.info.unknown\", \"Unknown\")}\n </p>\n </div>\n </div>\n </div>\n\n {/* Additional Info — secondary properties tucked behind progressive\n disclosure to keep the rail focused on the essentials. */}\n <div className=\"pt-6 border-t border-border-seam\">\n <h4 className=\"text-sm font-semibold text-text-primary mb-3\">\n {tWithFallback(t, \"files.info.properties\", \"Properties\")}\n </h4>\n <ShowMore collapsedHeight={56}>\n <dl className=\"space-y-2\">\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-text-secondary\">\n {tWithFallback(t, \"files.info.status\", \"Status\")}\n </dt>\n <dd className=\"text-sm font-medium text-text-primary\">\n {item.status}\n </dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-text-secondary\">\n {tWithFallback(t, \"files.info.visibility\", \"Visibility\")}\n </dt>\n <dd className=\"text-sm font-medium text-text-primary\">\n {item.visibility}\n </dd>\n </div>\n <div className=\"flex justify-between\">\n <dt className=\"text-sm text-text-secondary\">\n {tWithFallback(t, \"files.info.starred\", \"Starred\")}\n </dt>\n <dd className=\"text-sm font-medium text-text-primary\">\n {item.starred\n ? tWithFallback(t, \"files.info.yes\", \"Yes\")\n : tWithFallback(t, \"files.info.no\", \"No\")}\n </dd>\n </div>\n </dl>\n </ShowMore>\n </div>\n\n {/* Version History Button (Files only) */}\n {isFile && file && (\n <div className=\"pt-6 border-t border-border-seam\">\n <button\n type=\"button\"\n onClick={() => setShowVersionHistory(true)}\n className=\"w-full flex items-center justify-center gap-2 px-4 py-3 text-sm font-medium text-action-primary-text bg-action-primary-bg/10 hover:bg-action-primary-bg/20 rounded-lg transition-all duration-200\"\n >\n <Clock className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.info.viewVersionHistory\",\n \"View Version History\",\n )}\n </button>\n </div>\n )}\n </div>\n\n {/* Version History Modal */}\n {isFile && file && showVersionHistory && (\n <Suspense\n fallback={\n <div className=\"fixed inset-0 z-50 bg-bg-overlay flex items-center justify-center\">\n <div className=\"bg-bg-surface rounded-lg p-8 animate-in fade-in zoom-in duration-200\">\n <div className=\"flex items-center gap-3\">\n <Clock className=\"size-6 text-action-primary-bg animate-spin\" />\n <span className=\"text-text-primary\">\n {tWithFallback(\n t,\n \"files.info.loadingVersionHistory\",\n \"Loading version history...\",\n )}\n </span>\n </div>\n </div>\n </div>\n }\n >\n <FileVersionPanel\n file={file}\n onClose={() => setShowVersionHistory(false)}\n />\n </Suspense>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -132,7 +132,7 @@ function f({ onSearch: f, placeholder: p = "Search files and folders...", initia
132
132
  fileType: e.target.value || void 0
133
133
  }),
134
134
  "aria-label": e(g, "files.search.filters.fileType.label", "File Type"),
135
- className: "w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200",
135
+ className: "w-full px-3 py-2 border border-border-default rounded-lg bg-bg-surface text-text-primary scheme-normal dark:scheme-dark focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200 [&_option]:bg-bg-surface [&_option]:text-text-primary",
136
136
  children: [
137
137
  /* @__PURE__ */ c("option", {
138
138
  value: "",
@@ -170,7 +170,7 @@ function f({ onSearch: f, placeholder: p = "Search files and folders...", initia
170
170
  status: e.target.value || void 0
171
171
  }),
172
172
  "aria-label": e(g, "files.search.filters.status.label", "Status"),
173
- className: "w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200",
173
+ className: "w-full px-3 py-2 border border-border-default rounded-lg bg-bg-surface text-text-primary scheme-normal dark:scheme-dark focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200 [&_option]:bg-bg-surface [&_option]:text-text-primary",
174
174
  children: [
175
175
  /* @__PURE__ */ c("option", {
176
176
  value: "",
@@ -200,7 +200,7 @@ function f({ onSearch: f, placeholder: p = "Search files and folders...", initia
200
200
  visibility: e.target.value || void 0
201
201
  }),
202
202
  "aria-label": e(g, "files.search.filters.visibility.label", "Visibility"),
203
- className: "w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200",
203
+ className: "w-full px-3 py-2 border border-border-default rounded-lg bg-bg-surface text-text-primary scheme-normal dark:scheme-dark focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200 [&_option]:bg-bg-surface [&_option]:text-text-primary",
204
204
  children: [
205
205
  /* @__PURE__ */ c("option", {
206
206
  value: "",
@@ -1 +1 @@
1
- {"version":3,"file":"SearchBar.js","names":[],"sources":["../../../src/files/components/SearchBar.tsx"],"sourcesContent":["import { useState, useEffect, useEffectEvent, useCallback } from \"react\";\nimport { Search, X, Filter } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileFilterInput, FolderFilterInput } from \"../graphql-types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface SearchBarProps {\n onSearch: (\n query: string,\n filters?: {\n fileFilter?: FileFilterInput;\n folderFilter?: FolderFilterInput;\n },\n ) => void;\n placeholder?: string;\n initialQuery?: string;\n showFilters?: boolean;\n}\n\nconst FILE_TYPE_MIME_MAP: Record<string, string[]> = {\n image: [\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"image/svg+xml\",\n ],\n video: [\"video/mp4\", \"video/webm\", \"video/quicktime\"],\n document: [\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"text/plain\",\n \"text/csv\",\n \"application/json\",\n ],\n pdf: [\"application/pdf\"],\n archive: [\n \"application/zip\",\n \"application/x-tar\",\n \"application/x-7z-compressed\",\n \"application/gzip\",\n \"application/x-rar-compressed\",\n ],\n};\n\nexport function SearchBar({\n onSearch,\n placeholder = \"Search files and folders...\",\n initialQuery = \"\",\n showFilters = true,\n}: SearchBarProps) {\n const { t } = useI18n();\n const [query, setQuery] = useState(initialQuery);\n const [showFilterPanel, setShowFilterPanel] = useState(false);\n const [filters, setFilters] = useState<{\n fileType?: string;\n status?: string;\n visibility?: string;\n starred?: boolean;\n }>({});\n const fileStatus = filters.status as FileFilterInput[\"status\"] | undefined;\n const folderStatus = filters.status as\n | FolderFilterInput[\"status\"]\n | undefined;\n const fileVisibility = filters.visibility as\n | FileFilterInput[\"visibility\"]\n | undefined;\n const fileMimeTypes = filters.fileType\n ? FILE_TYPE_MIME_MAP[filters.fileType]\n : undefined;\n\n useEffect(() => {\n setQuery(initialQuery);\n }, [initialQuery]);\n\n const runSearch = useEffectEvent(() => {\n if (query.length >= 2 || query.length === 0) {\n const fileFilter: FileFilterInput = {\n ...(fileMimeTypes && { mimeTypes: fileMimeTypes }),\n ...(fileStatus && { status: fileStatus }),\n ...(fileVisibility && { visibility: fileVisibility }),\n ...(filters.starred !== undefined && { starred: filters.starred }),\n };\n\n const folderFilter: FolderFilterInput = {\n ...(folderStatus && { status: folderStatus }),\n ...(filters.starred !== undefined && { starred: filters.starred }),\n };\n\n onSearch(query, { fileFilter, folderFilter });\n }\n });\n\n // Debounced search\n useEffect(() => {\n const timer = setTimeout(() => {\n runSearch();\n }, 300);\n\n return () => clearTimeout(timer);\n }, [fileStatus, fileMimeTypes, fileVisibility, folderStatus, filters, query]);\n\n const handleClear = useCallback(() => {\n setQuery(\"\");\n setFilters({});\n onSearch(\"\");\n }, [onSearch]);\n\n const activeFilterCount = Object.values(filters).filter(\n (v) => v !== undefined,\n ).length;\n\n return (\n <div className=\"relative\">\n {/* Search Input */}\n <div className=\"relative flex items-center\">\n <Search className=\"absolute left-3 size-4 text-text-tertiary\" />\n <label htmlFor=\"files-search-input\" className=\"sr-only\">\n {tWithFallback(\n t,\n \"files.search.input.label\",\n \"Search files and folders\",\n )}\n </label>\n <input\n id=\"files-search-input\"\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={placeholder}\n aria-label={tWithFallback(\n t,\n \"files.search.input.label\",\n \"Search files and folders\",\n )}\n className=\"w-full pl-[3.25rem] pr-20 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border focus:border-action-primary-border transition-colors duration-200\"\n />\n <div className=\"absolute right-2 flex items-center gap-1\">\n {query && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"p-1.5 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.search.actions.clearSearch\",\n \"Clear search\",\n )}\n aria-label={tWithFallback(\n t,\n \"files.search.actions.clearSearch\",\n \"Clear search\",\n )}\n >\n <X className=\"size-4\" />\n </button>\n )}\n {showFilters && (\n <button\n type=\"button\"\n onClick={() => setShowFilterPanel(!showFilterPanel)}\n aria-expanded={showFilterPanel}\n aria-controls=\"files-search-filter-panel\"\n aria-label={tWithFallback(\n t,\n \"files.search.filters.toggleAria\",\n \"Toggle filters\",\n )}\n className={`p-1.5 rounded transition-colors duration-200 relative ${\n showFilterPanel || activeFilterCount > 0\n ? \"text-text-link bg-bg-accent hover:bg-action-primary-bg/20\"\n : \"text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover\"\n }`}\n title={tWithFallback(t, \"files.search.filters.title\", \"Filters\")}\n >\n <Filter className=\"size-4\" />\n {activeFilterCount > 0 && (\n <span className=\"absolute -top-1 -right-1 size-4 bg-action-primary-bg text-text-inverse text-[10px] font-bold rounded-full flex items-center justify-center\">\n {activeFilterCount}\n </span>\n )}\n </button>\n )}\n </div>\n </div>\n\n {/* Filter Panel */}\n {showFilterPanel && (\n <div\n id=\"files-search-filter-panel\"\n className=\"absolute top-full left-0 right-0 mt-2 p-4 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10 animate-in fade-in duration-200\"\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.label\",\n \"File Type\",\n )}\n </label>\n <select\n value={filters.fileType || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n fileType: e.target.value || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.fileType.label\",\n \"File Type\",\n )}\n className=\"w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.all\",\n \"All types\",\n )}\n </option>\n <option value=\"image\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.image\",\n \"Images\",\n )}\n </option>\n <option value=\"video\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.video\",\n \"Videos\",\n )}\n </option>\n <option value=\"document\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.document\",\n \"Documents\",\n )}\n </option>\n <option value=\"pdf\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.pdf\",\n \"PDFs\",\n )}\n </option>\n <option value=\"archive\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.archive\",\n \"Archives\",\n )}\n </option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.status.label\",\n \"Status\",\n )}\n </label>\n <select\n value={filters.status || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n status: e.target.value || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.status.label\",\n \"Status\",\n )}\n className=\"w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.all\",\n \"All statuses\",\n )}\n </option>\n <option value=\"ACTIVE\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.active\",\n \"Active\",\n )}\n </option>\n <option value=\"ARCHIVED\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.archived\",\n \"Archived\",\n )}\n </option>\n <option value=\"DELETED\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.deleted\",\n \"Deleted\",\n )}\n </option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.label\",\n \"Visibility\",\n )}\n </label>\n <select\n value={filters.visibility || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n visibility: e.target.value || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.visibility.label\",\n \"Visibility\",\n )}\n className=\"w-full px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.all\",\n \"All visibility\",\n )}\n </option>\n <option value=\"PRIVATE\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.private\",\n \"Private\",\n )}\n </option>\n <option value=\"TEAM\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.team\",\n \"Team\",\n )}\n </option>\n <option value=\"WORKSPACE\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.workspace\",\n \"Workspace\",\n )}\n </option>\n <option value=\"PUBLIC\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.public\",\n \"Public\",\n )}\n </option>\n </select>\n </div>\n\n <div className=\"flex items-center\">\n <input\n type=\"checkbox\"\n id=\"starred-filter\"\n checked={filters.starred || false}\n onChange={(e) =>\n setFilters({\n ...filters,\n starred: e.target.checked || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.starred.label\",\n \"Starred only\",\n )}\n className=\"size-4 text-action-primary-bg border-border-default rounded focus:ring-action-primary-border transition-colors duration-200\"\n />\n <label\n htmlFor=\"starred-filter\"\n className=\"ml-2 text-sm text-text-primary\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.starredOnly\",\n \"Starred only\",\n )}\n </label>\n </div>\n\n <div className=\"flex justify-end gap-2 pt-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={() => {\n setFilters({});\n setShowFilterPanel(false);\n }}\n className=\"px-3 py-1.5 text-sm text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.actions.clear\",\n \"Clear filters\",\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => setShowFilterPanel(false)}\n className=\"px-3 py-1.5 text-sm bg-action-primary-bg text-text-inverse rounded hover:bg-action-primary-bg-hover transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.actions.apply\",\n \"Apply\",\n )}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"SearchBar.js","names":[],"sources":["../../../src/files/components/SearchBar.tsx"],"sourcesContent":["import { useState, useEffect, useEffectEvent, useCallback } from \"react\";\nimport { Search, X, Filter } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileFilterInput, FolderFilterInput } from \"../graphql-types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\ninterface SearchBarProps {\n onSearch: (\n query: string,\n filters?: {\n fileFilter?: FileFilterInput;\n folderFilter?: FolderFilterInput;\n },\n ) => void;\n placeholder?: string;\n initialQuery?: string;\n showFilters?: boolean;\n}\n\nconst FILE_TYPE_MIME_MAP: Record<string, string[]> = {\n image: [\n \"image/jpeg\",\n \"image/png\",\n \"image/gif\",\n \"image/webp\",\n \"image/svg+xml\",\n ],\n video: [\"video/mp4\", \"video/webm\", \"video/quicktime\"],\n document: [\n \"application/msword\",\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n \"application/vnd.ms-excel\",\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n \"application/vnd.ms-powerpoint\",\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n \"text/plain\",\n \"text/csv\",\n \"application/json\",\n ],\n pdf: [\"application/pdf\"],\n archive: [\n \"application/zip\",\n \"application/x-tar\",\n \"application/x-7z-compressed\",\n \"application/gzip\",\n \"application/x-rar-compressed\",\n ],\n};\n\nexport function SearchBar({\n onSearch,\n placeholder = \"Search files and folders...\",\n initialQuery = \"\",\n showFilters = true,\n}: SearchBarProps) {\n const { t } = useI18n();\n const [query, setQuery] = useState(initialQuery);\n const [showFilterPanel, setShowFilterPanel] = useState(false);\n const [filters, setFilters] = useState<{\n fileType?: string;\n status?: string;\n visibility?: string;\n starred?: boolean;\n }>({});\n const fileStatus = filters.status as FileFilterInput[\"status\"] | undefined;\n const folderStatus = filters.status as\n | FolderFilterInput[\"status\"]\n | undefined;\n const fileVisibility = filters.visibility as\n | FileFilterInput[\"visibility\"]\n | undefined;\n const fileMimeTypes = filters.fileType\n ? FILE_TYPE_MIME_MAP[filters.fileType]\n : undefined;\n\n useEffect(() => {\n setQuery(initialQuery);\n }, [initialQuery]);\n\n const runSearch = useEffectEvent(() => {\n if (query.length >= 2 || query.length === 0) {\n const fileFilter: FileFilterInput = {\n ...(fileMimeTypes && { mimeTypes: fileMimeTypes }),\n ...(fileStatus && { status: fileStatus }),\n ...(fileVisibility && { visibility: fileVisibility }),\n ...(filters.starred !== undefined && { starred: filters.starred }),\n };\n\n const folderFilter: FolderFilterInput = {\n ...(folderStatus && { status: folderStatus }),\n ...(filters.starred !== undefined && { starred: filters.starred }),\n };\n\n onSearch(query, { fileFilter, folderFilter });\n }\n });\n\n // Debounced search\n useEffect(() => {\n const timer = setTimeout(() => {\n runSearch();\n }, 300);\n\n return () => clearTimeout(timer);\n }, [fileStatus, fileMimeTypes, fileVisibility, folderStatus, filters, query]);\n\n const handleClear = useCallback(() => {\n setQuery(\"\");\n setFilters({});\n onSearch(\"\");\n }, [onSearch]);\n\n const activeFilterCount = Object.values(filters).filter(\n (v) => v !== undefined,\n ).length;\n\n return (\n <div className=\"relative\">\n {/* Search Input */}\n <div className=\"relative flex items-center\">\n <Search className=\"absolute left-3 size-4 text-text-tertiary\" />\n <label htmlFor=\"files-search-input\" className=\"sr-only\">\n {tWithFallback(\n t,\n \"files.search.input.label\",\n \"Search files and folders\",\n )}\n </label>\n <input\n id=\"files-search-input\"\n type=\"text\"\n value={query}\n onChange={(e) => setQuery(e.target.value)}\n placeholder={placeholder}\n aria-label={tWithFallback(\n t,\n \"files.search.input.label\",\n \"Search files and folders\",\n )}\n className=\"w-full pl-[3.25rem] pr-20 py-2 border border-border-default rounded-lg focus:outline-none focus:ring-2 focus:ring-action-primary-border focus:border-action-primary-border transition-colors duration-200\"\n />\n <div className=\"absolute right-2 flex items-center gap-1\">\n {query && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"p-1.5 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.search.actions.clearSearch\",\n \"Clear search\",\n )}\n aria-label={tWithFallback(\n t,\n \"files.search.actions.clearSearch\",\n \"Clear search\",\n )}\n >\n <X className=\"size-4\" />\n </button>\n )}\n {showFilters && (\n <button\n type=\"button\"\n onClick={() => setShowFilterPanel(!showFilterPanel)}\n aria-expanded={showFilterPanel}\n aria-controls=\"files-search-filter-panel\"\n aria-label={tWithFallback(\n t,\n \"files.search.filters.toggleAria\",\n \"Toggle filters\",\n )}\n className={`p-1.5 rounded transition-colors duration-200 relative ${\n showFilterPanel || activeFilterCount > 0\n ? \"text-text-link bg-bg-accent hover:bg-action-primary-bg/20\"\n : \"text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover\"\n }`}\n title={tWithFallback(t, \"files.search.filters.title\", \"Filters\")}\n >\n <Filter className=\"size-4\" />\n {activeFilterCount > 0 && (\n <span className=\"absolute -top-1 -right-1 size-4 bg-action-primary-bg text-text-inverse text-[10px] font-bold rounded-full flex items-center justify-center\">\n {activeFilterCount}\n </span>\n )}\n </button>\n )}\n </div>\n </div>\n\n {/* Filter Panel */}\n {showFilterPanel && (\n <div\n id=\"files-search-filter-panel\"\n className=\"absolute top-full left-0 right-0 mt-2 p-4 bg-bg-surface border border-border-default rounded-lg shadow-lg z-10 animate-in fade-in duration-200\"\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.label\",\n \"File Type\",\n )}\n </label>\n <select\n value={filters.fileType || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n fileType: e.target.value || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.fileType.label\",\n \"File Type\",\n )}\n className=\"w-full px-3 py-2 border border-border-default rounded-lg bg-bg-surface text-text-primary scheme-normal dark:scheme-dark focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200 [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.all\",\n \"All types\",\n )}\n </option>\n <option value=\"image\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.image\",\n \"Images\",\n )}\n </option>\n <option value=\"video\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.video\",\n \"Videos\",\n )}\n </option>\n <option value=\"document\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.document\",\n \"Documents\",\n )}\n </option>\n <option value=\"pdf\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.pdf\",\n \"PDFs\",\n )}\n </option>\n <option value=\"archive\">\n {tWithFallback(\n t,\n \"files.search.filters.fileType.options.archive\",\n \"Archives\",\n )}\n </option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.status.label\",\n \"Status\",\n )}\n </label>\n <select\n value={filters.status || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n status: e.target.value || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.status.label\",\n \"Status\",\n )}\n className=\"w-full px-3 py-2 border border-border-default rounded-lg bg-bg-surface text-text-primary scheme-normal dark:scheme-dark focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200 [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.all\",\n \"All statuses\",\n )}\n </option>\n <option value=\"ACTIVE\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.active\",\n \"Active\",\n )}\n </option>\n <option value=\"ARCHIVED\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.archived\",\n \"Archived\",\n )}\n </option>\n <option value=\"DELETED\">\n {tWithFallback(\n t,\n \"files.search.filters.status.options.deleted\",\n \"Deleted\",\n )}\n </option>\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.label\",\n \"Visibility\",\n )}\n </label>\n <select\n value={filters.visibility || \"\"}\n onChange={(e) =>\n setFilters({\n ...filters,\n visibility: e.target.value || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.visibility.label\",\n \"Visibility\",\n )}\n className=\"w-full px-3 py-2 border border-border-default rounded-lg bg-bg-surface text-text-primary scheme-normal dark:scheme-dark focus:outline-none focus:ring-2 focus:ring-action-primary-border transition-colors duration-200 [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.all\",\n \"All visibility\",\n )}\n </option>\n <option value=\"PRIVATE\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.private\",\n \"Private\",\n )}\n </option>\n <option value=\"TEAM\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.team\",\n \"Team\",\n )}\n </option>\n <option value=\"WORKSPACE\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.workspace\",\n \"Workspace\",\n )}\n </option>\n <option value=\"PUBLIC\">\n {tWithFallback(\n t,\n \"files.search.filters.visibility.options.public\",\n \"Public\",\n )}\n </option>\n </select>\n </div>\n\n <div className=\"flex items-center\">\n <input\n type=\"checkbox\"\n id=\"starred-filter\"\n checked={filters.starred || false}\n onChange={(e) =>\n setFilters({\n ...filters,\n starred: e.target.checked || undefined,\n })\n }\n aria-label={tWithFallback(\n t,\n \"files.search.filters.starred.label\",\n \"Starred only\",\n )}\n className=\"size-4 text-action-primary-bg border-border-default rounded focus:ring-action-primary-border transition-colors duration-200\"\n />\n <label\n htmlFor=\"starred-filter\"\n className=\"ml-2 text-sm text-text-primary\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.starredOnly\",\n \"Starred only\",\n )}\n </label>\n </div>\n\n <div className=\"flex justify-end gap-2 pt-2 border-t border-border-default\">\n <button\n type=\"button\"\n onClick={() => {\n setFilters({});\n setShowFilterPanel(false);\n }}\n className=\"px-3 py-1.5 text-sm text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.actions.clear\",\n \"Clear filters\",\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => setShowFilterPanel(false)}\n className=\"px-3 py-1.5 text-sm bg-action-primary-bg text-text-inverse rounded hover:bg-action-primary-bg-hover transition-colors duration-200\"\n >\n {tWithFallback(\n t,\n \"files.search.filters.actions.apply\",\n \"Apply\",\n )}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ShareDialog.d.ts","sourceRoot":"","sources":["../../../src/files/components/ShareDialog.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGrD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,aAAa,GAAG,cAAc,CAAC;IAC1C,WAAW,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,aAAa,EAAE,CACb,KAAK,EAAE,gBAAgB,KACpB,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,aAAa,EACb,SAAS,GACV,EAAE,gBAAgB,+BA+YlB"}
1
+ {"version":3,"file":"ShareDialog.d.ts","sourceRoot":"","sources":["../../../src/files/components/ShareDialog.tsx"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGrD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,aAAa,GAAG,cAAc,CAAC;IAC1C,WAAW,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,aAAa,EAAE,CACb,KAAK,EAAE,gBAAgB,KACpB,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,aAAa,EACb,SAAS,GACV,EAAE,gBAAgB,+BAwZlB"}
@@ -29,12 +29,15 @@ function S({ item: S, itemType: C, onClose: w, onCreateShare: T, isLoading: E })
29
29
  className: "space-y-4",
30
30
  children: [
31
31
  /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ p("label", {
32
+ htmlFor: "share-dialog-expiry-select",
32
33
  className: "flex items-center gap-2 text-sm font-medium text-text-primary mb-2",
33
34
  children: [/* @__PURE__ */ f(r, { className: "size-4" }), e(D, "files.shareDialog.expiry.label", "Link expires in")]
34
35
  }), /* @__PURE__ */ p("select", {
36
+ id: "share-dialog-expiry-select",
37
+ "aria-label": e(D, "files.shareDialog.expiry.label", "Link expires in"),
35
38
  value: P,
36
39
  onChange: (e) => F(Number(e.target.value)),
37
- className: "w-full min-h-11 px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200",
40
+ className: "w-full min-h-11 px-3 py-2 border border-border-default rounded-lg bg-bg-surface text-text-primary scheme-normal dark:scheme-dark focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200 [&_option]:bg-bg-surface [&_option]:text-text-primary",
38
41
  children: [
39
42
  /* @__PURE__ */ f("option", {
40
43
  value: 0,
@@ -1 +1 @@
1
- {"version":3,"file":"ShareDialog.js","names":[],"sources":["../../../src/files/components/ShareDialog.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport {\n Link,\n Mail,\n Calendar,\n Download,\n Lock,\n Copy,\n Check,\n Loader2,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogFooter,\n OptionalFields,\n Button,\n} from \"@burdenoff/fe-libs/ui\";\nimport type { FileItem, FolderItem } from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\nexport interface ShareDialogInput {\n fileId?: string;\n folderId?: string;\n shareType: \"PUBLIC_LINK\" | \"EMAIL_INVITE\";\n accessLevel: \"VIEW\" | \"DOWNLOAD\" | \"EDIT\";\n expiresAt?: string;\n maxDownloads?: number;\n password?: string;\n invitedEmail?: string;\n}\n\ninterface ShareDialogProps {\n item: FileItem | FolderItem;\n itemType: \"file\" | \"folder\";\n onClose: () => void;\n onCreateShare: (\n input: ShareDialogInput,\n ) => Promise<{ publicUrl: string; token: string }>;\n isLoading?: boolean;\n}\n\nexport function ShareDialog({\n item,\n itemType,\n onClose,\n onCreateShare,\n isLoading,\n}: ShareDialogProps) {\n const { t } = useI18n();\n const [shareUrl, setShareUrl] = useState<string | null>(null);\n const [shareType, setShareType] = useState<\n \"PUBLIC_LINK\" | \"EMAIL_INVITE\" | null\n >(null);\n const [copied, setCopied] = useState(false);\n const [expiryDays, setExpiryDays] = useState<number>(7);\n const [maxDownloads, setMaxDownloads] = useState<number>(0);\n const [hasPassword, setHasPassword] = useState(false);\n const [password, setPassword] = useState(\"\");\n const [invitedEmail, setInvitedEmail] = useState(\"\");\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n const handleCreateShare = async () => {\n try {\n setErrorMessage(null);\n const expiresAt =\n expiryDays > 0\n ? new Date(\n Date.now() + expiryDays * 24 * 60 * 60 * 1000,\n ).toISOString()\n : undefined;\n\n const type: ShareDialogInput[\"shareType\"] = invitedEmail\n ? \"EMAIL_INVITE\"\n : \"PUBLIC_LINK\";\n\n const shareInput: ShareDialogInput = {\n ...(itemType === \"file\" ? { fileId: item.id } : { folderId: item.id }),\n shareType: type,\n accessLevel: \"DOWNLOAD\",\n expiresAt,\n maxDownloads: maxDownloads > 0 ? maxDownloads : undefined,\n password: hasPassword && password ? password : undefined,\n invitedEmail: invitedEmail || undefined,\n };\n\n const result = await onCreateShare(shareInput);\n\n setShareType(type);\n setShareUrl(result.publicUrl || \"\");\n } catch (error) {\n setErrorMessage(\n error instanceof Error ? error.message : \"Failed to create share\",\n );\n }\n };\n\n const handleCopyLink = async () => {\n if (shareUrl) {\n await navigator.clipboard.writeText(shareUrl);\n setCopied(true);\n }\n };\n\n useEffect(() => {\n if (copied) {\n const timer = setTimeout(() => setCopied(false), 2000);\n return () => clearTimeout(timer);\n }\n return undefined;\n }, [copied]);\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(next: boolean) => {\n if (!next) onClose();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tWithFallback(\n t,\n \"files.shareDialog.title\",\n 'Share {{itemType}} \"{{name}}\"',\n {\n itemType: tWithFallback(\n t,\n itemType === \"folder\"\n ? \"files.shareDialog.itemType.folder\"\n : \"files.shareDialog.itemType.file\",\n itemType === \"folder\" ? \"folder\" : \"file\",\n ),\n name: item.name,\n },\n )}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n\n {shareType === null ? (\n <>\n {errorMessage && (\n <div className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {errorMessage}\n </div>\n )}\n\n <div className=\"space-y-4\">\n {/* Expiry — smart-default field, visible above the fold */}\n <div>\n <label className=\"flex items-center gap-2 text-sm font-medium text-text-primary mb-2\">\n <Calendar className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.label\",\n \"Link expires in\",\n )}\n </label>\n <select\n value={expiryDays}\n onChange={(e) => setExpiryDays(Number(e.target.value))}\n className=\"w-full min-h-11 px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n >\n <option value={0}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.never\",\n \"Never\",\n )}\n </option>\n <option value={1}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.oneDay\",\n \"1 day\",\n )}\n </option>\n <option value={7}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.sevenDays\",\n \"7 days\",\n )}\n </option>\n <option value={30}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.thirtyDays\",\n \"30 days\",\n )}\n </option>\n <option value={90}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.ninetyDays\",\n \"90 days\",\n )}\n </option>\n </select>\n </div>\n\n {/* Invited Email — smart-default; \"leave empty for public link\" */}\n <div>\n <label className=\"flex items-center gap-2 text-sm font-medium text-text-primary mb-2\">\n <Mail className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.invite.label\",\n \"Invite specific person (optional)\",\n )}\n </label>\n <input\n type=\"email\"\n value={invitedEmail}\n onChange={(e) => setInvitedEmail(e.target.value)}\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.invite.label\",\n \"Invite specific person (optional)\",\n )}\n placeholder=\"user@example.com\"\n className=\"w-full min-h-11 px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {tWithFallback(\n t,\n \"files.shareDialog.invite.help\",\n \"Leave empty for public link\",\n )}\n </p>\n </div>\n\n {/* Advanced: ACL overrides + retention */}\n <OptionalFields\n title={tWithFallback(\n t,\n \"files.shareDialog.advanced.title\",\n \"Advanced\",\n )}\n description={tWithFallback(\n t,\n \"files.shareDialog.advanced.description\",\n \"Download limits and password protection\",\n )}\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"flex items-center gap-2 text-sm font-medium text-text-primary mb-2\">\n <Download className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.downloads.label\",\n \"Maximum downloads\",\n )}\n </label>\n <input\n type=\"number\"\n min=\"0\"\n value={maxDownloads}\n onChange={(e) => setMaxDownloads(Number(e.target.value))}\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.downloads.label\",\n \"Maximum downloads\",\n )}\n placeholder={tWithFallback(\n t,\n \"files.shareDialog.downloads.placeholder\",\n \"Unlimited\",\n )}\n className=\"w-full min-h-11 px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n </div>\n\n <div>\n <label className=\"flex items-center gap-2 text-sm font-medium text-text-primary mb-2\">\n <Lock className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.password.label\",\n \"Password protection\",\n )}\n </label>\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n checked={hasPassword}\n onChange={(e) => setHasPassword(e.target.checked)}\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.password.require\",\n \"Require password\",\n )}\n className=\"rounded transition-colors duration-200\"\n />\n <span className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.shareDialog.password.require\",\n \"Require password\",\n )}\n </span>\n </div>\n {hasPassword && (\n <>\n <input\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.password.label\",\n \"Password protection\",\n )}\n placeholder={tWithFallback(\n t,\n \"files.shareDialog.password.placeholder\",\n \"Enter password\",\n )}\n className=\"w-full min-h-11 mt-2 px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n <p className=\"mt-1 text-xs text-text-tertiary\">\n {tWithFallback(\n t,\n \"files.shareDialog.password.help\",\n \"Password must be at least 8 characters and contain uppercase, lowercase, and number\",\n )}\n </p>\n </>\n )}\n </div>\n </div>\n </OptionalFields>\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={onClose}>\n {tWithFallback(t, \"files.common.actions.cancel\", \"Cancel\")}\n </Button>\n <Button\n type=\"button\"\n onClick={handleCreateShare}\n disabled={isLoading || (hasPassword && !password)}\n >\n {isLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tWithFallback(\n t,\n \"files.shareDialog.actions.creating\",\n \"Creating...\",\n )}\n </>\n ) : (\n <>\n <Link className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.actions.createLink\",\n \"Create share link\",\n )}\n </>\n )}\n </Button>\n </ResponsiveDialogFooter>\n </>\n ) : (\n <>\n <div className=\"space-y-4\">\n <div className=\"bg-status-success-bg-subtle border border-status-success-border rounded-lg p-4\">\n {shareType === \"EMAIL_INVITE\" ? (\n <>\n <p className=\"text-sm font-medium text-status-success-text mb-2\">\n {tWithFallback(\n t,\n \"files.shareDialog.success.invitationSent\",\n \"Invitation sent successfully!\",\n )}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.shareDialog.success.invitationSentTo\",\n \"An invitation has been sent to {{email}}\",\n { email: invitedEmail },\n )}\n </p>\n </>\n ) : (\n <>\n <p className=\"text-sm font-medium text-status-success-text mb-2\">\n {tWithFallback(\n t,\n \"files.shareDialog.success.linkCreated\",\n \"Share link created successfully!\",\n )}\n </p>\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <input\n type=\"text\"\n value={shareUrl || \"\"}\n readOnly\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.success.linkCreated\",\n \"Share link\",\n )}\n className=\"flex-1 min-h-11 px-3 py-2 bg-bg-surface border border-status-success-border rounded-lg text-sm\"\n />\n <Button type=\"button\" onClick={handleCopyLink}>\n {copied ? (\n <>\n <Check className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.actions.copied\",\n \"Copied!\",\n )}\n </>\n ) : (\n <>\n <Copy className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.common.actions.copy\",\n \"Copy\",\n )}\n </>\n )}\n </Button>\n </div>\n </>\n )}\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" onClick={onClose} className=\"w-full\">\n {tWithFallback(t, \"files.common.actions.done\", \"Done\")}\n </Button>\n </ResponsiveDialogFooter>\n </div>\n </>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":";;;;;;;AA6CA,SAAgB,EAAY,EAC1B,SACA,aACA,YACA,kBACA,gBACmB;CACnB,IAAM,EAAE,SAAM,GAAS,EACjB,CAAC,GAAU,KAAe,EAAwB,KAAK,EACvD,CAAC,GAAW,KAAgB,EAEhC,KAAK,EACD,CAAC,GAAQ,KAAa,EAAS,GAAM,EACrC,CAAC,GAAY,KAAiB,EAAiB,EAAE,EACjD,CAAC,GAAc,KAAmB,EAAiB,EAAE,EACrD,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAc,KAAmB,EAAS,GAAG,EAC9C,CAAC,GAAc,KAAmB,EAAwB,KAAK;AAoDrE,QARA,QAAgB;AACd,MAAI,GAAQ;GACV,IAAM,IAAQ,iBAAiB,EAAU,GAAM,EAAE,IAAK;AACtD,gBAAa,aAAa,EAAM;;IAGjC,CAAC,EAAO,CAAC,EAGV,kBAAC,GAAD;EACE,MAAA;EACA,eAAe,MAAkB;AAC/B,GAAK,KAAM,GAAS;;YAGtB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UACG,EACC,GACA,2BACA,mCACA;GACE,UAAU,EACR,GACA,MAAa,WACT,sCACA,mCACJ,MAAa,WAAW,WAAW,OACpC;GACD,MAAM,EAAK;GACZ,CACF,EACqB,CAAA,EACD,CAAA,EAExB,MAAc,OACb,kBAAA,GAAA,EAAA,UAAA;GACG,KACC,kBAAC,OAAD;IAAK,WAAU;cACZ;IACG,CAAA;GAGR,kBAAC,OAAD;IAAK,WAAU;cAAf;KAEE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MAAO,WAAU;gBAAjB,CACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA,EAC9B,EACC,GACA,kCACA,kBACD,CACK;SACR,kBAAC,UAAD;MACE,OAAO;MACP,WAAW,MAAM,EAAc,OAAO,EAAE,OAAO,MAAM,CAAC;MACtD,WAAU;gBAHZ;OAKE,kBAAC,UAAD;QAAQ,OAAO;kBACZ,EACC,GACA,0CACA,QACD;QACM,CAAA;OACT,kBAAC,UAAD;QAAQ,OAAO;kBACZ,EACC,GACA,2CACA,QACD;QACM,CAAA;OACT,kBAAC,UAAD;QAAQ,OAAO;kBACZ,EACC,GACA,8CACA,SACD;QACM,CAAA;OACT,kBAAC,UAAD;QAAQ,OAAO;kBACZ,EACC,GACA,+CACA,UACD;QACM,CAAA;OACT,kBAAC,UAAD;QAAQ,OAAO;kBACZ,EACC,GACA,+CACA,UACD;QACM,CAAA;OACF;QACL,EAAA,CAAA;KAGN,kBAAC,OAAD,EAAA,UAAA;MACE,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EACC,GACA,kCACA,oCACD,CACK;;MACR,kBAAC,SAAD;OACE,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAgB,EAAE,OAAO,MAAM;OAChD,cAAY,EACV,GACA,kCACA,oCACD;OACD,aAAY;OACZ,WAAU;OACV,CAAA;MACF,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,GACA,iCACA,8BACD;OACC,CAAA;MACA,EAAA,CAAA;KAGN,kBAAC,GAAD;MACE,OAAO,EACL,GACA,oCACA,WACD;MACD,aAAa,EACX,GACA,0CACA,0CACD;gBAED,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QAAO,WAAU;kBAAjB,CACE,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA,EAC9B,EACC,GACA,qCACA,oBACD,CACK;WACR,kBAAC,SAAD;QACE,MAAK;QACL,KAAI;QACJ,OAAO;QACP,WAAW,MAAM,EAAgB,OAAO,EAAE,OAAO,MAAM,CAAC;QACxD,cAAY,EACV,GACA,qCACA,oBACD;QACD,aAAa,EACX,GACA,2CACA,YACD;QACD,WAAU;QACV,CAAA,CACE,EAAA,CAAA,EAEN,kBAAC,OAAD,EAAA,UAAA;QACE,kBAAC,SAAD;SAAO,WAAU;mBAAjB,CACE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EACC,GACA,oCACA,sBACD,CACK;;QACR,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,SAAD;UACE,MAAK;UACL,SAAS;UACT,WAAW,MAAM,EAAe,EAAE,OAAO,QAAQ;UACjD,cAAY,EACV,GACA,sCACA,mBACD;UACD,WAAU;UACV,CAAA,EACF,kBAAC,QAAD;UAAM,WAAU;oBACb,EACC,GACA,sCACA,mBACD;UACI,CAAA,CACH;;QACL,KACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,SAAD;SACE,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM;SAC5C,cAAY,EACV,GACA,oCACA,sBACD;SACD,aAAa,EACX,GACA,0CACA,iBACD;SACD,WAAU;SACV,CAAA,EACF,kBAAC,KAAD;SAAG,WAAU;mBACV,EACC,GACA,mCACA,sFACD;SACC,CAAA,CACH,EAAA,CAAA;QAED,EAAA,CAAA,CACF;;MACS,CAAA;KACb;;GAEN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAQ,MAAK;IAAS,SAAQ;IAAU,SAAS;cAC9C,EAAc,GAAG,+BAA+B,SAAS;IACnD,CAAA,EACT,kBAAC,GAAD;IACE,MAAK;IACL,SAxRY,YAAY;AACpC,SAAI;AACF,QAAgB,KAAK;MACrB,IAAM,IACJ,IAAa,IACT,IAAI,KACF,KAAK,KAAK,GAAG,IAAa,KAAK,KAAK,KAAK,IAC1C,CAAC,aAAa,GACf,KAAA,GAEA,IAAsC,IACxC,iBACA,eAYE,IAAS,MAAM,EAVgB;OACnC,GAAI,MAAa,SAAS,EAAE,QAAQ,EAAK,IAAI,GAAG,EAAE,UAAU,EAAK,IAAI;OACrE,WAAW;OACX,aAAa;OACb;OACA,cAAc,IAAe,IAAI,IAAe,KAAA;OAChD,UAAU,KAAe,IAAW,IAAW,KAAA;OAC/C,cAAc,KAAgB,KAAA;OAC/B,CAE6C;AAG9C,MADA,EAAa,EAAK,EAClB,EAAY,EAAO,aAAa,GAAG;cAC5B,GAAO;AACd,QACE,aAAiB,QAAQ,EAAM,UAAU,yBAC1C;;;IA0PS,UAAU,KAAc,KAAe,CAAC;cAEvC,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAS,WAAU,uBAAwB,CAAA,EAC1C,EACC,GACA,sCACA,cACD,CACA,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EACC,GACA,wCACA,oBACD,CACA,EAAA,CAAA;IAEE,CAAA,CACc,EAAA,CAAA;GACxB,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cACZ,MAAc,iBACb,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,GACA,4CACA,gCACD;KACC,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,GACA,8CACA,4CACA,EAAE,OAAO,GAAc,CACxB;KACC,CAAA,CACH,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EACC,GACA,yCACA,mCACD;KACC,CAAA,EACJ,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,SAAD;MACE,MAAK;MACL,OAAO,KAAY;MACnB,UAAA;MACA,cAAY,EACV,GACA,yCACA,aACD;MACD,WAAU;MACV,CAAA,EACF,kBAAC,GAAD;MAAQ,MAAK;MAAS,SAzTnB,YAAY;AACjC,OAAI,MACF,MAAM,UAAU,UAAU,UAAU,EAAS,EAC7C,EAAU,GAAK;;gBAuTI,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA,EAC3B,EACC,GACA,oCACA,UACD,CACA,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD,EAAM,WAAU,UAAW,CAAA,EAC1B,EACC,GACA,6BACA,OACD,CACA,EAAA,CAAA;MAEE,CAAA,CACL;OACL,EAAA,CAAA;IAED,CAAA,EAEN,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;IAAQ,MAAK;IAAS,SAAS;IAAS,WAAU;cAC/C,EAAc,GAAG,6BAA6B,OAAO;IAC/C,CAAA,EACc,CAAA,CACrB;MACL,CAAA,CAEmB,EAAA,CAAA;EACT,CAAA"}
1
+ {"version":3,"file":"ShareDialog.js","names":[],"sources":["../../../src/files/components/ShareDialog.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport {\n Link,\n Mail,\n Calendar,\n Download,\n Lock,\n Copy,\n Check,\n Loader2,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogFooter,\n OptionalFields,\n Button,\n} from \"@burdenoff/fe-libs/ui\";\nimport type { FileItem, FolderItem } from \"../types\";\nimport { tWithFallback } from \"../utils/i18n\";\n\nexport interface ShareDialogInput {\n fileId?: string;\n folderId?: string;\n shareType: \"PUBLIC_LINK\" | \"EMAIL_INVITE\";\n accessLevel: \"VIEW\" | \"DOWNLOAD\" | \"EDIT\";\n expiresAt?: string;\n maxDownloads?: number;\n password?: string;\n invitedEmail?: string;\n}\n\ninterface ShareDialogProps {\n item: FileItem | FolderItem;\n itemType: \"file\" | \"folder\";\n onClose: () => void;\n onCreateShare: (\n input: ShareDialogInput,\n ) => Promise<{ publicUrl: string; token: string }>;\n isLoading?: boolean;\n}\n\nexport function ShareDialog({\n item,\n itemType,\n onClose,\n onCreateShare,\n isLoading,\n}: ShareDialogProps) {\n const { t } = useI18n();\n const [shareUrl, setShareUrl] = useState<string | null>(null);\n const [shareType, setShareType] = useState<\n \"PUBLIC_LINK\" | \"EMAIL_INVITE\" | null\n >(null);\n const [copied, setCopied] = useState(false);\n const [expiryDays, setExpiryDays] = useState<number>(7);\n const [maxDownloads, setMaxDownloads] = useState<number>(0);\n const [hasPassword, setHasPassword] = useState(false);\n const [password, setPassword] = useState(\"\");\n const [invitedEmail, setInvitedEmail] = useState(\"\");\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n const handleCreateShare = async () => {\n try {\n setErrorMessage(null);\n const expiresAt =\n expiryDays > 0\n ? new Date(\n Date.now() + expiryDays * 24 * 60 * 60 * 1000,\n ).toISOString()\n : undefined;\n\n const type: ShareDialogInput[\"shareType\"] = invitedEmail\n ? \"EMAIL_INVITE\"\n : \"PUBLIC_LINK\";\n\n const shareInput: ShareDialogInput = {\n ...(itemType === \"file\" ? { fileId: item.id } : { folderId: item.id }),\n shareType: type,\n accessLevel: \"DOWNLOAD\",\n expiresAt,\n maxDownloads: maxDownloads > 0 ? maxDownloads : undefined,\n password: hasPassword && password ? password : undefined,\n invitedEmail: invitedEmail || undefined,\n };\n\n const result = await onCreateShare(shareInput);\n\n setShareType(type);\n setShareUrl(result.publicUrl || \"\");\n } catch (error) {\n setErrorMessage(\n error instanceof Error ? error.message : \"Failed to create share\",\n );\n }\n };\n\n const handleCopyLink = async () => {\n if (shareUrl) {\n await navigator.clipboard.writeText(shareUrl);\n setCopied(true);\n }\n };\n\n useEffect(() => {\n if (copied) {\n const timer = setTimeout(() => setCopied(false), 2000);\n return () => clearTimeout(timer);\n }\n return undefined;\n }, [copied]);\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(next: boolean) => {\n if (!next) onClose();\n }}\n >\n <ResponsiveDialogContent>\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>\n {tWithFallback(\n t,\n \"files.shareDialog.title\",\n 'Share {{itemType}} \"{{name}}\"',\n {\n itemType: tWithFallback(\n t,\n itemType === \"folder\"\n ? \"files.shareDialog.itemType.folder\"\n : \"files.shareDialog.itemType.file\",\n itemType === \"folder\" ? \"folder\" : \"file\",\n ),\n name: item.name,\n },\n )}\n </ResponsiveDialogTitle>\n </ResponsiveDialogHeader>\n\n {shareType === null ? (\n <>\n {errorMessage && (\n <div className=\"mb-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {errorMessage}\n </div>\n )}\n\n <div className=\"space-y-4\">\n {/* Expiry — smart-default field, visible above the fold */}\n <div>\n <label\n htmlFor=\"share-dialog-expiry-select\"\n className=\"flex items-center gap-2 text-sm font-medium text-text-primary mb-2\"\n >\n <Calendar className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.label\",\n \"Link expires in\",\n )}\n </label>\n <select\n id=\"share-dialog-expiry-select\"\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.expiry.label\",\n \"Link expires in\",\n )}\n value={expiryDays}\n onChange={(e) => setExpiryDays(Number(e.target.value))}\n className=\"w-full min-h-11 px-3 py-2 border border-border-default rounded-lg bg-bg-surface text-text-primary scheme-normal dark:scheme-dark focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200 [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value={0}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.never\",\n \"Never\",\n )}\n </option>\n <option value={1}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.oneDay\",\n \"1 day\",\n )}\n </option>\n <option value={7}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.sevenDays\",\n \"7 days\",\n )}\n </option>\n <option value={30}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.thirtyDays\",\n \"30 days\",\n )}\n </option>\n <option value={90}>\n {tWithFallback(\n t,\n \"files.shareDialog.expiry.options.ninetyDays\",\n \"90 days\",\n )}\n </option>\n </select>\n </div>\n\n {/* Invited Email — smart-default; \"leave empty for public link\" */}\n <div>\n <label className=\"flex items-center gap-2 text-sm font-medium text-text-primary mb-2\">\n <Mail className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.invite.label\",\n \"Invite specific person (optional)\",\n )}\n </label>\n <input\n type=\"email\"\n value={invitedEmail}\n onChange={(e) => setInvitedEmail(e.target.value)}\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.invite.label\",\n \"Invite specific person (optional)\",\n )}\n placeholder=\"user@example.com\"\n className=\"w-full min-h-11 px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {tWithFallback(\n t,\n \"files.shareDialog.invite.help\",\n \"Leave empty for public link\",\n )}\n </p>\n </div>\n\n {/* Advanced: ACL overrides + retention */}\n <OptionalFields\n title={tWithFallback(\n t,\n \"files.shareDialog.advanced.title\",\n \"Advanced\",\n )}\n description={tWithFallback(\n t,\n \"files.shareDialog.advanced.description\",\n \"Download limits and password protection\",\n )}\n >\n <div className=\"space-y-4\">\n <div>\n <label className=\"flex items-center gap-2 text-sm font-medium text-text-primary mb-2\">\n <Download className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.downloads.label\",\n \"Maximum downloads\",\n )}\n </label>\n <input\n type=\"number\"\n min=\"0\"\n value={maxDownloads}\n onChange={(e) => setMaxDownloads(Number(e.target.value))}\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.downloads.label\",\n \"Maximum downloads\",\n )}\n placeholder={tWithFallback(\n t,\n \"files.shareDialog.downloads.placeholder\",\n \"Unlimited\",\n )}\n className=\"w-full min-h-11 px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n </div>\n\n <div>\n <label className=\"flex items-center gap-2 text-sm font-medium text-text-primary mb-2\">\n <Lock className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.password.label\",\n \"Password protection\",\n )}\n </label>\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n checked={hasPassword}\n onChange={(e) => setHasPassword(e.target.checked)}\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.password.require\",\n \"Require password\",\n )}\n className=\"rounded transition-colors duration-200\"\n />\n <span className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.shareDialog.password.require\",\n \"Require password\",\n )}\n </span>\n </div>\n {hasPassword && (\n <>\n <input\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.password.label\",\n \"Password protection\",\n )}\n placeholder={tWithFallback(\n t,\n \"files.shareDialog.password.placeholder\",\n \"Enter password\",\n )}\n className=\"w-full min-h-11 mt-2 px-3 py-2 border border-border-default rounded-lg focus:outline-none focus:border-action-primary-border text-sm transition-colors duration-200\"\n />\n <p className=\"mt-1 text-xs text-text-tertiary\">\n {tWithFallback(\n t,\n \"files.shareDialog.password.help\",\n \"Password must be at least 8 characters and contain uppercase, lowercase, and number\",\n )}\n </p>\n </>\n )}\n </div>\n </div>\n </OptionalFields>\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" variant=\"outline\" onClick={onClose}>\n {tWithFallback(t, \"files.common.actions.cancel\", \"Cancel\")}\n </Button>\n <Button\n type=\"button\"\n onClick={handleCreateShare}\n disabled={isLoading || (hasPassword && !password)}\n >\n {isLoading ? (\n <>\n <Loader2 className=\"size-4 animate-spin\" />\n {tWithFallback(\n t,\n \"files.shareDialog.actions.creating\",\n \"Creating...\",\n )}\n </>\n ) : (\n <>\n <Link className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.actions.createLink\",\n \"Create share link\",\n )}\n </>\n )}\n </Button>\n </ResponsiveDialogFooter>\n </>\n ) : (\n <>\n <div className=\"space-y-4\">\n <div className=\"bg-status-success-bg-subtle border border-status-success-border rounded-lg p-4\">\n {shareType === \"EMAIL_INVITE\" ? (\n <>\n <p className=\"text-sm font-medium text-status-success-text mb-2\">\n {tWithFallback(\n t,\n \"files.shareDialog.success.invitationSent\",\n \"Invitation sent successfully!\",\n )}\n </p>\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.shareDialog.success.invitationSentTo\",\n \"An invitation has been sent to {{email}}\",\n { email: invitedEmail },\n )}\n </p>\n </>\n ) : (\n <>\n <p className=\"text-sm font-medium text-status-success-text mb-2\">\n {tWithFallback(\n t,\n \"files.shareDialog.success.linkCreated\",\n \"Share link created successfully!\",\n )}\n </p>\n <div className=\"flex flex-col sm:flex-row items-stretch sm:items-center gap-2\">\n <input\n type=\"text\"\n value={shareUrl || \"\"}\n readOnly\n aria-label={tWithFallback(\n t,\n \"files.shareDialog.success.linkCreated\",\n \"Share link\",\n )}\n className=\"flex-1 min-h-11 px-3 py-2 bg-bg-surface border border-status-success-border rounded-lg text-sm\"\n />\n <Button type=\"button\" onClick={handleCopyLink}>\n {copied ? (\n <>\n <Check className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.shareDialog.actions.copied\",\n \"Copied!\",\n )}\n </>\n ) : (\n <>\n <Copy className=\"size-4\" />\n {tWithFallback(\n t,\n \"files.common.actions.copy\",\n \"Copy\",\n )}\n </>\n )}\n </Button>\n </div>\n </>\n )}\n </div>\n\n <ResponsiveDialogFooter>\n <Button type=\"button\" onClick={onClose} className=\"w-full\">\n {tWithFallback(t, \"files.common.actions.done\", \"Done\")}\n </Button>\n </ResponsiveDialogFooter>\n </div>\n </>\n )}\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../../src/files/components/viewers/VideoPlayer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,gBAAgB,+BAmN1D"}
1
+ {"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../../src/files/components/viewers/VideoPlayer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,gBAAgB,+BAuN1D"}
@@ -91,9 +91,10 @@ function d({ file: d, url: f }) {
91
91
  })]
92
92
  }),
93
93
  /* @__PURE__ */ u("select", {
94
+ "aria-label": p("files.preview.video.playbackRate", "Playback speed"),
94
95
  value: T,
95
96
  onChange: (e) => j(Number(e.target.value)),
96
- className: "px-2 py-1 text-sm bg-bg-surface border border-border-default rounded text-text-primary cursor-pointer",
97
+ className: "px-2 py-1 text-sm bg-bg-surface border border-border-default rounded text-text-primary scheme-normal dark:scheme-dark cursor-pointer [&_option]:bg-bg-surface [&_option]:text-text-primary",
97
98
  children: [
98
99
  /* @__PURE__ */ l("option", {
99
100
  value: "0.5",
@@ -1 +1 @@
1
- {"version":3,"file":"VideoPlayer.js","names":[],"sources":["../../../../src/files/components/viewers/VideoPlayer.tsx"],"sourcesContent":["import { useState, useRef, useEffect } from \"react\";\nimport { Play, Pause, Volume2, VolumeX, Maximize } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\nimport { useTr } from \"../../../shared/hooks/useTr\";\n\ninterface VideoPlayerProps {\n file: FileItem;\n url: string;\n}\n\nexport function VideoPlayer({ file, url }: VideoPlayerProps) {\n const tr = useTr();\n const videoRef = useRef<HTMLVideoElement>(null);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [volume, setVolume] = useState(1);\n const [isMuted, setIsMuted] = useState(false);\n const [playbackRate, setPlaybackRate] = useState(1);\n\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const handleTimeUpdate = () => setCurrentTime(video.currentTime);\n const handleLoadedMetadata = () => setDuration(video.duration);\n const handleEnded = () => setIsPlaying(false);\n\n video.addEventListener(\"timeupdate\", handleTimeUpdate);\n video.addEventListener(\"loadedmetadata\", handleLoadedMetadata);\n video.addEventListener(\"ended\", handleEnded);\n\n return () => {\n video.removeEventListener(\"timeupdate\", handleTimeUpdate);\n video.removeEventListener(\"loadedmetadata\", handleLoadedMetadata);\n video.removeEventListener(\"ended\", handleEnded);\n };\n }, []);\n\n const togglePlay = () => {\n if (videoRef.current) {\n if (isPlaying) {\n videoRef.current.pause();\n } else {\n videoRef.current.play();\n }\n setIsPlaying(!isPlaying);\n }\n };\n\n const handleSeek = (time: number) => {\n if (videoRef.current) {\n videoRef.current.currentTime = time;\n setCurrentTime(time);\n }\n };\n\n const handleVolumeChange = (newVolume: number) => {\n setVolume(newVolume);\n setIsMuted(newVolume === 0);\n if (videoRef.current) {\n videoRef.current.volume = newVolume;\n }\n };\n\n const toggleMute = () => {\n if (videoRef.current) {\n const newMuted = !isMuted;\n setIsMuted(newMuted);\n videoRef.current.muted = newMuted;\n }\n };\n\n const handlePlaybackRateChange = (rate: number) => {\n setPlaybackRate(rate);\n if (videoRef.current) {\n videoRef.current.playbackRate = rate;\n }\n };\n\n const handleFullscreen = () => {\n if (videoRef.current) {\n videoRef.current.requestFullscreen();\n }\n };\n\n const formatTime = (seconds: number): string => {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, \"0\")}`;\n };\n\n return (\n <div className=\"flex h-full flex-col bg-bg-canvas\">\n {/* Video */}\n <div className=\"flex-1 flex items-center justify-center\">\n <video\n ref={videoRef}\n src={url}\n aria-label={file.name}\n className=\"max-w-full max-h-full\"\n onClick={togglePlay}\n >\n <track kind=\"captions\" />\n </video>\n </div>\n\n {/* Controls */}\n <div className=\"bg-bg-surface border-t border-border-default p-4\">\n {/* Progress Bar */}\n <div className=\"mb-4\">\n <input\n type=\"range\"\n min={0}\n max={duration || 0}\n value={currentTime}\n onChange={(e) => handleSeek(Number(e.target.value))}\n aria-label={tr(\"files.preview.video.seek\", \"Seek\")}\n className=\"w-full h-1 bg-bg-sunken rounded-lg appearance-none cursor-pointer accent-action-primary-bg\"\n />\n <div className=\"flex justify-between mt-1 text-xs text-text-secondary\">\n <span>{formatTime(currentTime)}</span>\n <span>{formatTime(duration)}</span>\n </div>\n </div>\n\n {/* Control Buttons */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {/* Play/Pause */}\n <button\n type=\"button\"\n onClick={togglePlay}\n className=\"p-2 text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={\n isPlaying\n ? tr(\"files.preview.video.pause\", \"Pause\")\n : tr(\"files.preview.video.play\", \"Play\")\n }\n aria-label={\n isPlaying\n ? tr(\"files.preview.video.pauseVideo\", \"Pause video\")\n : tr(\"files.preview.video.playVideo\", \"Play video\")\n }\n >\n {isPlaying ? (\n <Pause className=\"size-5\" />\n ) : (\n <Play className=\"size-5\" />\n )}\n </button>\n\n {/* Volume */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={toggleMute}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={\n isMuted\n ? tr(\"files.preview.video.unmute\", \"Unmute\")\n : tr(\"files.preview.video.mute\", \"Mute\")\n }\n aria-label={\n isMuted\n ? tr(\"files.preview.video.unmuteVideo\", \"Unmute video\")\n : tr(\"files.preview.video.muteVideo\", \"Mute video\")\n }\n >\n {isMuted ? (\n <VolumeX className=\"size-5\" />\n ) : (\n <Volume2 className=\"size-5\" />\n )}\n </button>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.1}\n value={volume}\n onChange={(e) => handleVolumeChange(Number(e.target.value))}\n aria-label={tr(\"files.preview.video.volume\", \"Volume\")}\n className=\"w-20 h-1 bg-bg-sunken rounded-lg appearance-none cursor-pointer accent-action-primary-bg\"\n />\n </div>\n\n {/* Playback Speed */}\n <select\n value={playbackRate}\n onChange={(e) => handlePlaybackRateChange(Number(e.target.value))}\n className=\"px-2 py-1 text-sm bg-bg-surface border border-border-default rounded text-text-primary cursor-pointer\"\n >\n <option value=\"0.5\">0.5x</option>\n <option value=\"0.75\">0.75x</option>\n <option value=\"1\">1x</option>\n <option value=\"1.25\">1.25x</option>\n <option value=\"1.5\">1.5x</option>\n <option value=\"2\">2x</option>\n </select>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">{file.name}</span>\n <button\n type=\"button\"\n onClick={handleFullscreen}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={tr(\"files.preview.video.fullscreen\", \"Fullscreen\")}\n aria-label={tr(\n \"files.preview.video.openFullscreen\",\n \"Open fullscreen\",\n )}\n >\n <Maximize className=\"size-5\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VideoPlayer.js","names":[],"sources":["../../../../src/files/components/viewers/VideoPlayer.tsx"],"sourcesContent":["import { useState, useRef, useEffect } from \"react\";\nimport { Play, Pause, Volume2, VolumeX, Maximize } from \"lucide-react\";\nimport type { FileItem } from \"../../types\";\nimport { useTr } from \"../../../shared/hooks/useTr\";\n\ninterface VideoPlayerProps {\n file: FileItem;\n url: string;\n}\n\nexport function VideoPlayer({ file, url }: VideoPlayerProps) {\n const tr = useTr();\n const videoRef = useRef<HTMLVideoElement>(null);\n const [isPlaying, setIsPlaying] = useState(false);\n const [currentTime, setCurrentTime] = useState(0);\n const [duration, setDuration] = useState(0);\n const [volume, setVolume] = useState(1);\n const [isMuted, setIsMuted] = useState(false);\n const [playbackRate, setPlaybackRate] = useState(1);\n\n useEffect(() => {\n const video = videoRef.current;\n if (!video) return;\n\n const handleTimeUpdate = () => setCurrentTime(video.currentTime);\n const handleLoadedMetadata = () => setDuration(video.duration);\n const handleEnded = () => setIsPlaying(false);\n\n video.addEventListener(\"timeupdate\", handleTimeUpdate);\n video.addEventListener(\"loadedmetadata\", handleLoadedMetadata);\n video.addEventListener(\"ended\", handleEnded);\n\n return () => {\n video.removeEventListener(\"timeupdate\", handleTimeUpdate);\n video.removeEventListener(\"loadedmetadata\", handleLoadedMetadata);\n video.removeEventListener(\"ended\", handleEnded);\n };\n }, []);\n\n const togglePlay = () => {\n if (videoRef.current) {\n if (isPlaying) {\n videoRef.current.pause();\n } else {\n videoRef.current.play();\n }\n setIsPlaying(!isPlaying);\n }\n };\n\n const handleSeek = (time: number) => {\n if (videoRef.current) {\n videoRef.current.currentTime = time;\n setCurrentTime(time);\n }\n };\n\n const handleVolumeChange = (newVolume: number) => {\n setVolume(newVolume);\n setIsMuted(newVolume === 0);\n if (videoRef.current) {\n videoRef.current.volume = newVolume;\n }\n };\n\n const toggleMute = () => {\n if (videoRef.current) {\n const newMuted = !isMuted;\n setIsMuted(newMuted);\n videoRef.current.muted = newMuted;\n }\n };\n\n const handlePlaybackRateChange = (rate: number) => {\n setPlaybackRate(rate);\n if (videoRef.current) {\n videoRef.current.playbackRate = rate;\n }\n };\n\n const handleFullscreen = () => {\n if (videoRef.current) {\n videoRef.current.requestFullscreen();\n }\n };\n\n const formatTime = (seconds: number): string => {\n const mins = Math.floor(seconds / 60);\n const secs = Math.floor(seconds % 60);\n return `${mins}:${secs.toString().padStart(2, \"0\")}`;\n };\n\n return (\n <div className=\"flex h-full flex-col bg-bg-canvas\">\n {/* Video */}\n <div className=\"flex-1 flex items-center justify-center\">\n <video\n ref={videoRef}\n src={url}\n aria-label={file.name}\n className=\"max-w-full max-h-full\"\n onClick={togglePlay}\n >\n <track kind=\"captions\" />\n </video>\n </div>\n\n {/* Controls */}\n <div className=\"bg-bg-surface border-t border-border-default p-4\">\n {/* Progress Bar */}\n <div className=\"mb-4\">\n <input\n type=\"range\"\n min={0}\n max={duration || 0}\n value={currentTime}\n onChange={(e) => handleSeek(Number(e.target.value))}\n aria-label={tr(\"files.preview.video.seek\", \"Seek\")}\n className=\"w-full h-1 bg-bg-sunken rounded-lg appearance-none cursor-pointer accent-action-primary-bg\"\n />\n <div className=\"flex justify-between mt-1 text-xs text-text-secondary\">\n <span>{formatTime(currentTime)}</span>\n <span>{formatTime(duration)}</span>\n </div>\n </div>\n\n {/* Control Buttons */}\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {/* Play/Pause */}\n <button\n type=\"button\"\n onClick={togglePlay}\n className=\"p-2 text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={\n isPlaying\n ? tr(\"files.preview.video.pause\", \"Pause\")\n : tr(\"files.preview.video.play\", \"Play\")\n }\n aria-label={\n isPlaying\n ? tr(\"files.preview.video.pauseVideo\", \"Pause video\")\n : tr(\"files.preview.video.playVideo\", \"Play video\")\n }\n >\n {isPlaying ? (\n <Pause className=\"size-5\" />\n ) : (\n <Play className=\"size-5\" />\n )}\n </button>\n\n {/* Volume */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={toggleMute}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={\n isMuted\n ? tr(\"files.preview.video.unmute\", \"Unmute\")\n : tr(\"files.preview.video.mute\", \"Mute\")\n }\n aria-label={\n isMuted\n ? tr(\"files.preview.video.unmuteVideo\", \"Unmute video\")\n : tr(\"files.preview.video.muteVideo\", \"Mute video\")\n }\n >\n {isMuted ? (\n <VolumeX className=\"size-5\" />\n ) : (\n <Volume2 className=\"size-5\" />\n )}\n </button>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.1}\n value={volume}\n onChange={(e) => handleVolumeChange(Number(e.target.value))}\n aria-label={tr(\"files.preview.video.volume\", \"Volume\")}\n className=\"w-20 h-1 bg-bg-sunken rounded-lg appearance-none cursor-pointer accent-action-primary-bg\"\n />\n </div>\n\n {/* Playback Speed */}\n <select\n aria-label={tr(\n \"files.preview.video.playbackRate\",\n \"Playback speed\",\n )}\n value={playbackRate}\n onChange={(e) => handlePlaybackRateChange(Number(e.target.value))}\n className=\"px-2 py-1 text-sm bg-bg-surface border border-border-default rounded text-text-primary scheme-normal dark:scheme-dark cursor-pointer [&_option]:bg-bg-surface [&_option]:text-text-primary\"\n >\n <option value=\"0.5\">0.5x</option>\n <option value=\"0.75\">0.75x</option>\n <option value=\"1\">1x</option>\n <option value=\"1.25\">1.25x</option>\n <option value=\"1.5\">1.5x</option>\n <option value=\"2\">2x</option>\n </select>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">{file.name}</span>\n <button\n type=\"button\"\n onClick={handleFullscreen}\n className=\"p-2 text-text-secondary hover:text-text-primary hover:bg-action-ghost-bg-hover rounded transition-colors duration-200\"\n title={tr(\"files.preview.video.fullscreen\", \"Fullscreen\")}\n aria-label={tr(\n \"files.preview.video.openFullscreen\",\n \"Open fullscreen\",\n )}\n >\n <Maximize className=\"size-5\" />\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-files",
3
- "version": "2026.830.2",
3
+ "version": "2026.1013.1",
4
4
  "description": "Burdenoff Files Microfrontend - Standalone file management module",
5
5
  "type": "module",
6
6
  "imports": {
@@ -15,10 +15,15 @@
15
15
  ".": {
16
16
  "types": "./dist/files.d.ts",
17
17
  "import": "./dist/files.js"
18
+ },
19
+ "./styles/dark.css": {
20
+ "style": "./src/styles/dark.css",
21
+ "default": "./src/styles/dark.css"
18
22
  }
19
23
  },
20
24
  "files": [
21
25
  "dist",
26
+ "src/styles/dark.css",
22
27
  "package.json",
23
28
  "README.md"
24
29
  ],
@@ -0,0 +1,27 @@
1
+ /*
2
+ * Class-based dark variant for hosts of @burdenoff/microfe-files.
3
+ *
4
+ * The dropdown triggers this package added on BOFF-7280
5
+ * (File Type / Status / Visibility in FilesRoot's SearchBar, the
6
+ * link-expiry select in ShareDialog, and the playback-rate select in
7
+ * VideoPlayer) use Tailwind's `dark:` variant on `color-scheme` so the
8
+ * browser/OS paints the native <option> rows in dark form-widget
9
+ * colours when the app's dark palette is active.
10
+ *
11
+ * Tailwind v4's default `dark:` variant follows the `prefers-color-scheme`
12
+ * media query. Hosts in this monorepo toggle dark mode through the `.dark`
13
+ * class on the document root (see `@burdenoff/fe-libs/shared/providers/shell/ThemeProvider`).
14
+ * Without this `@custom-variant` line, a user who picks dark mode on a
15
+ * light OS (or vice-versa) would still get `scheme-normal` for the
16
+ * dropdown chrome and the open `<select>` panel.
17
+ *
18
+ * The directive only needs to be present in ONE CSS file processed by
19
+ * Tailwind v4 during the host's build; importing this file from the host
20
+ * (or via the package's `./styles/dark.css` export) is enough.
21
+ *
22
+ * @example
23
+ * // host styles.css
24
+ * @import "tailwindcss";
25
+ * @import "@burdenoff/microfe-files/styles/dark.css";
26
+ */
27
+ @custom-variant dark (&:where(.dark, .dark *));