- Updated .env.example to include configuration for OnlyOffice Document Server. - Modified the workspace configuration to remove the drive-suite path. - Adjusted TypeScript environment imports for consistency. - Enhanced Next.js configuration to disable canvas in Webpack. - Updated package.json to include new dependencies for OnlyOffice and PDF.js. - Added global styles for OnlyOffice theme integration in the CSS. - Created new layout and page components for the Drive feature, including public sharing and editing functionalities. - Updated metadata handling across various layouts to reflect the new app structure.
165 lines
4.9 KiB
TypeScript
165 lines
4.9 KiB
TypeScript
"use client"
|
|
|
|
import { useRef, useState } from "react"
|
|
import { useRouter } from "next/navigation"
|
|
import { Search, X } from "lucide-react"
|
|
import { DriveSearchSuggestionsPanel } from "@/components/drive/drive-search-suggestions"
|
|
import { Button } from "@/components/ui/button"
|
|
import type { DriveFileInfo } from "@/lib/api/types"
|
|
import { useDriveSearchSuggestions } from "@/lib/api/hooks/use-drive-queries"
|
|
import { openDriveItem } from "@/lib/drive/drive-open-item"
|
|
import {
|
|
type DriveSearchScope,
|
|
buildDriveSearchUrl,
|
|
defaultDriveSearchScope,
|
|
fileBrowserViewForSearchScope,
|
|
} from "@/lib/drive/drive-search"
|
|
import type { DriveView } from "@/lib/drive/drive-url"
|
|
import { useDebouncedValue } from "@/lib/hooks/use-debounced-value"
|
|
import { useDriveUIStore } from "@/lib/stores/drive-ui-store"
|
|
import { DRIVE_SEARCH_INPUT_WRAP_CLASS } from "@/lib/drive/drive-chrome-classes"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
interface DriveSearchBarProps {
|
|
value: string
|
|
onChange: (value: string) => void
|
|
scope: DriveSearchScope
|
|
onScopeChange: (scope: DriveSearchScope) => void
|
|
folderPath: string
|
|
contextView: DriveView
|
|
/** When true, suggestions panel hidden (search results page). */
|
|
resultsMode?: boolean
|
|
className?: string
|
|
autoFocus?: boolean
|
|
}
|
|
|
|
export function DriveSearchBar({
|
|
value,
|
|
onChange,
|
|
scope,
|
|
onScopeChange,
|
|
folderPath,
|
|
contextView,
|
|
resultsMode = false,
|
|
className,
|
|
autoFocus = false,
|
|
}: DriveSearchBarProps) {
|
|
const router = useRouter()
|
|
const openPreview = useDriveUIStore((s) => s.openPreview)
|
|
const inputRef = useRef<HTMLInputElement>(null)
|
|
const [focused, setFocused] = useState(false)
|
|
const debouncedQuery = useDebouncedValue(value, 250)
|
|
const showFolderScope = folderPath !== "/"
|
|
const effectiveScope =
|
|
scope === "folder" && !showFolderScope
|
|
? defaultDriveSearchScope(contextView, folderPath)
|
|
: scope
|
|
const searchPath = effectiveScope === "folder" ? folderPath : "/"
|
|
|
|
const { data, isFetching } = useDriveSearchSuggestions(
|
|
debouncedQuery,
|
|
effectiveScope,
|
|
searchPath,
|
|
focused && !resultsMode && debouncedQuery.trim().length >= 2
|
|
)
|
|
|
|
const suggestions = data?.files ?? []
|
|
const showPanel =
|
|
!resultsMode && focused && value.trim().length >= 2
|
|
|
|
const submitSearch = (query?: string) => {
|
|
const q = (query ?? value).trim()
|
|
if (!q) return
|
|
router.push(
|
|
buildDriveSearchUrl({
|
|
query: q,
|
|
scope: effectiveScope,
|
|
folderPath: effectiveScope === "folder" ? folderPath : "/",
|
|
})
|
|
)
|
|
inputRef.current?.blur()
|
|
}
|
|
|
|
const openSuggestion = (item: DriveFileInfo) => {
|
|
openDriveItem(item, {
|
|
router,
|
|
openPreview,
|
|
view: fileBrowserViewForSearchScope(effectiveScope),
|
|
contextItems: suggestions,
|
|
})
|
|
inputRef.current?.blur()
|
|
}
|
|
|
|
return (
|
|
<div
|
|
data-drive-search
|
|
className={cn(
|
|
"relative flex w-full min-w-0 flex-col overflow-visible",
|
|
className
|
|
)}
|
|
>
|
|
<div className={cn(DRIVE_SEARCH_INPUT_WRAP_CLASS, "text-muted-foreground")}>
|
|
<div className="pointer-events-none absolute left-3.5 flex items-center">
|
|
<Search className="size-5 shrink-0" />
|
|
</div>
|
|
|
|
<input
|
|
ref={inputRef}
|
|
type="text"
|
|
enterKeyHint="search"
|
|
placeholder="Rechercher dans Drive"
|
|
value={value}
|
|
onChange={(e) => onChange(e.target.value)}
|
|
onFocus={() => setFocused(true)}
|
|
onBlur={() => window.setTimeout(() => setFocused(false), 150)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault()
|
|
submitSearch()
|
|
}
|
|
if (e.key === "Escape") {
|
|
onChange("")
|
|
inputRef.current?.blur()
|
|
}
|
|
}}
|
|
autoFocus={autoFocus}
|
|
className={cn(
|
|
"h-full w-full rounded-full border-0 bg-transparent text-sm text-foreground outline-none placeholder:text-muted-foreground",
|
|
value ? "pl-11 pr-12" : "pl-11 pr-4"
|
|
)}
|
|
autoComplete="off"
|
|
/>
|
|
|
|
{value && (
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
className="absolute right-2 text-gray-600"
|
|
onMouseDown={(e) => e.preventDefault()}
|
|
onClick={() => {
|
|
onChange("")
|
|
inputRef.current?.focus()
|
|
}}
|
|
aria-label="Effacer la recherche"
|
|
>
|
|
<X className="h-5 w-5" />
|
|
</Button>
|
|
)}
|
|
</div>
|
|
|
|
{showPanel ? (
|
|
<DriveSearchSuggestionsPanel
|
|
query={value}
|
|
scope={effectiveScope}
|
|
onScopeChange={onScopeChange}
|
|
showFolderScope={showFolderScope}
|
|
suggestions={suggestions}
|
|
loading={isFetching && suggestions.length === 0}
|
|
onPickItem={openSuggestion}
|
|
onSubmitSearch={() => submitSearch()}
|
|
/>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|