Compare commits
7 Commits
83c28a8d5e
...
fcb34aeacc
| Author | SHA1 | Date | |
|---|---|---|---|
| fcb34aeacc | |||
| a287df5e0c | |||
| 9ca6770cd4 | |||
| 06b7c323ae | |||
| c12181fe16 | |||
| 9664eb07b8 | |||
| f38a34eb2a |
@@ -20,7 +20,7 @@ const App: React.FC = () => {
|
||||
},[data])
|
||||
|
||||
return (
|
||||
<div className="dark:text-gray-300">
|
||||
<div className="dark:text-gray-300 px-2 pt-1">
|
||||
{
|
||||
ready&&
|
||||
<Router>
|
||||
|
||||
@@ -30,7 +30,7 @@ const Breadcrum: React.FC<Props> = ({path,onDirClick}) => {
|
||||
</Link>
|
||||
</div>
|
||||
<div className="breadcrum-item">
|
||||
<BreadcrumImage className="h-5 w-auto text-gray-400" />
|
||||
<BreadcrumImage className="h-5 w-auto text-gray-400 cursor-default" />
|
||||
<li>
|
||||
<a className={!keyParts.length?"text-blue-500":""} onClick={()=>{
|
||||
onDirClick?.(new ObjID(path.bucket,"/"))
|
||||
@@ -43,7 +43,7 @@ const Breadcrum: React.FC<Props> = ({path,onDirClick}) => {
|
||||
{keyParts.map((e,i,arr)=>{
|
||||
const last = i == arr.length - 1
|
||||
return <div key={e} className="breadcrum-item">
|
||||
<BreadcrumImage className="h-5 w-auto text-gray-400" />
|
||||
<BreadcrumImage className="h-5 w-auto text-gray-400 cursor-default" />
|
||||
<li>
|
||||
<a
|
||||
className={`${last?"text-blue-500":""}`}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React from "react"
|
||||
import { useState } from "react"
|
||||
import { Link } from "react-router-dom"
|
||||
import { useListBucktesQuery } from "../generated/graphql"
|
||||
import Breadcrum from "./Breadcrum"
|
||||
import MoreMenu from "./MoreMenu"
|
||||
import { ReactComponent as Spinner } from "./../assets/spinner.svg"
|
||||
|
||||
const BucketSelect: React.FC = () => {
|
||||
|
||||
const [buckets] = useState(["dev"])
|
||||
const {data: buckets, loading} = useListBucktesQuery()
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -17,12 +18,17 @@ const BucketSelect: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
<ul>
|
||||
{buckets.map((e)=>
|
||||
{buckets?.buckets.map((e)=>
|
||||
<li key={e}>
|
||||
<Link to={`/f/${e}/`} >{e}</Link>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
{loading &&
|
||||
<div className="flex justify-center mt-4">
|
||||
<Spinner className="animate-spin h-6 w-6 dark:text-white" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import React from "react"
|
||||
import { Directory } from "../generated/graphql"
|
||||
import { MdFolderOpen } from "react-icons/md"
|
||||
|
||||
interface Props {
|
||||
dir: Directory
|
||||
}
|
||||
|
||||
const DirectoryElement: React.FC<Props> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<td>
|
||||
<MdFolderOpen className="inline"/> {props.dir.name}
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
<td>
|
||||
</td>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default DirectoryElement
|
||||
@@ -8,7 +8,7 @@ import uploadFile from "../functions/uploadFile"
|
||||
import { useCopyMutation, useCreateDirMutation, useDeleteDirMutation, useDeleteFileMutation, useMoveMutation, useOpenDirQuery } from "../generated/graphql"
|
||||
import Breadcrum from "./Breadcrum"
|
||||
import CreateDirButton from "./CreateDirButton"
|
||||
import DragAndDrop from "./DragAndDrop"
|
||||
import GlobalDragAndDrop from "./GlobalDragAndDrop"
|
||||
import FileBrowserContextMenu, { Action, CONTEXT_MENU_DIR, CONTEXT_MENU_FILE } from "./FileBrowserContextMenu"
|
||||
import FileOpen from "./FileOpen"
|
||||
import FileUploadButton from "./FileUploadButton"
|
||||
@@ -22,9 +22,9 @@ const FileBrowser: React.FC<RouteComponentProps> = (props) => {
|
||||
const [openFileId, setOpenFileId] = useState<ObjID>()
|
||||
const [showFile, setShowFile] = useState(false)
|
||||
|
||||
const [srcID,setSrcID] = useState<ObjID>(path)
|
||||
const [srcID,setSrcID] = useState<ObjID | null>()
|
||||
const [pasteAction,setPasteAction] = useState<Action>()
|
||||
const [editID,setEditID] = useState<ObjID>(path)
|
||||
const [editID,setEditID] = useState<ObjID>()
|
||||
const [editEnable,setEditEnable] = useState(false)
|
||||
|
||||
const [deleteMutation] = useDeleteFileMutation()
|
||||
@@ -70,14 +70,15 @@ const FileBrowser: React.FC<RouteComponentProps> = (props) => {
|
||||
setPasteAction(action)
|
||||
break
|
||||
case Action.FilePaste:
|
||||
if (pasteAction === Action.FileCopy){
|
||||
if (pasteAction === Action.FileCopy && srcID){
|
||||
await copyMutation({variables:{src:srcID,dest:path}})
|
||||
refetchDir()
|
||||
}
|
||||
|
||||
if (pasteAction === Action.FileMove){
|
||||
if (pasteAction === Action.FileMove && srcID){
|
||||
await moveMutation({variables:{src:srcID,dest:path}})
|
||||
refetchDir()
|
||||
setSrcID(null)
|
||||
}
|
||||
break
|
||||
case Action.DirDelete:
|
||||
@@ -102,84 +103,82 @@ const FileBrowser: React.FC<RouteComponentProps> = (props) => {
|
||||
onSelect={onContextSelect}
|
||||
pasteActive={!!srcID}
|
||||
/>
|
||||
<DragAndDrop
|
||||
<GlobalDragAndDrop
|
||||
handleDrop={async (files)=>{
|
||||
await handleDrop(files)
|
||||
}}
|
||||
>
|
||||
<div className="flex justify-between h-12">
|
||||
<Breadcrum path={path} onDirClick={(newPath)=>{
|
||||
props.history.push(newPath.toURI())
|
||||
}}/>
|
||||
<div className="ml-auto">
|
||||
<CreateDirButton
|
||||
onPressed={async (dirName)=>{
|
||||
const dirID = new ObjID(path.bucket,path.key + dirName + "/")
|
||||
await createDirMutation({variables:{path: dirID}})
|
||||
refetchDir()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FileUploadButton
|
||||
onUpload={(files)=>handleDrop(files)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<MoreMenu />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<FileBrowserList
|
||||
directorys={data?.directories || []}
|
||||
files={data?.files || []}
|
||||
|
||||
onDirClick={(e,path)=>{
|
||||
props.history.push(path.toURI())
|
||||
}}
|
||||
|
||||
onDirContext={(e,path)=>{
|
||||
e.preventDefault()
|
||||
showDirContext(e,{props:{id:path}})
|
||||
}}
|
||||
|
||||
onFileClick={(e,id)=>{
|
||||
setOpenFileId(id)
|
||||
setShowFile(true)
|
||||
}}
|
||||
|
||||
onFileContext={(e,id)=>{
|
||||
e.preventDefault()
|
||||
showFileContext(e,{props:{id}})
|
||||
}}
|
||||
|
||||
editId={editID}
|
||||
editEnable={editEnable}
|
||||
|
||||
onRenameDone={async (id,changed,newName)=>{
|
||||
setEditEnable(false)
|
||||
if (changed){
|
||||
// TODO: maybe change the name on client so it seems more smooth rather then haveing it refetch
|
||||
// TODO: input check & error handling
|
||||
await moveMutation({variables:{
|
||||
src:id,
|
||||
dest: id.rename(newName)
|
||||
}})
|
||||
refetchDir()
|
||||
}
|
||||
}}/>
|
||||
<div className="flex justify-between h-12">
|
||||
<Breadcrum path={path} onDirClick={(newPath)=>{
|
||||
props.history.push(newPath.toURI())
|
||||
}}/>
|
||||
<div className="ml-auto">
|
||||
<CreateDirButton
|
||||
onPressed={async (dirName)=>{
|
||||
const dirID = new ObjID(path.bucket,path.key + dirName + "/")
|
||||
await createDirMutation({variables:{path: dirID}})
|
||||
refetchDir()
|
||||
}}
|
||||
/>
|
||||
{loading &&
|
||||
<div className="flex justify-center mt-4">
|
||||
<Spinner className="animate-spin h-6 w-6 dark:text-white" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<FileOpen id={openFileId} show={showFile} onCloseClick={()=>{
|
||||
setShowFile(false)
|
||||
}} />
|
||||
</DragAndDrop>
|
||||
<div>
|
||||
<FileUploadButton
|
||||
onUpload={(files)=>handleDrop(files)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<MoreMenu />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<FileBrowserList
|
||||
directorys={data?.directories || []}
|
||||
files={data?.files || []}
|
||||
|
||||
onDirClick={(e,path)=>{
|
||||
props.history.push(path.toURI())
|
||||
}}
|
||||
|
||||
onDirContext={(e,path)=>{
|
||||
e.preventDefault()
|
||||
showDirContext(e,{props:{id:path}})
|
||||
}}
|
||||
|
||||
onFileClick={(e,id)=>{
|
||||
setOpenFileId(id)
|
||||
setShowFile(true)
|
||||
}}
|
||||
|
||||
onFileContext={(e,id)=>{
|
||||
e.preventDefault()
|
||||
showFileContext(e,{props:{id}})
|
||||
}}
|
||||
|
||||
editId={editID}
|
||||
editEnable={editEnable}
|
||||
|
||||
onRenameDone={async (id,changed,newName)=>{
|
||||
setEditEnable(false)
|
||||
if (changed){
|
||||
// TODO: maybe change the name on client so it seems more smooth rather then haveing it refetch
|
||||
// TODO: input check & error handling
|
||||
await moveMutation({variables:{
|
||||
src:id,
|
||||
dest: id.rename(newName)
|
||||
}})
|
||||
refetchDir()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{loading &&
|
||||
<div className="flex justify-center mt-4">
|
||||
<Spinner className="animate-spin h-6 w-6 dark:text-white" />
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<FileOpen id={openFileId} show={showFile} onCloseClick={()=>{
|
||||
setShowFile(false)
|
||||
}} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import React from "react"
|
||||
import { Directory, File } from "../generated/graphql"
|
||||
import DirectoryComponent from "./DirectoryElement"
|
||||
import FileElement from "./FileElement"
|
||||
|
||||
interface Props {
|
||||
file?: File
|
||||
dir?: Directory
|
||||
onClick?: (event: React.MouseEvent ,data: File | Directory) => void
|
||||
onContextMenu?: (e:React.MouseEvent) => void
|
||||
edit: boolean
|
||||
onRename?: (newName: string)=>void
|
||||
onCancleRename?: ()=>void
|
||||
}
|
||||
|
||||
const FileBrowserElement: React.FC<Props> = (props) => {
|
||||
return (
|
||||
<tr
|
||||
className="hover:bg-gray-100 dark:hover:bg-gray-900 text-lg"
|
||||
onClick={(e)=>{
|
||||
if(props.file){
|
||||
props.onClick?.(e,props.file)
|
||||
}else if(props.dir){
|
||||
props.onClick?.(e,props.dir)
|
||||
}
|
||||
}}
|
||||
|
||||
onContextMenu={(e)=>props.onContextMenu?.(e)}
|
||||
|
||||
>
|
||||
{(props.file) ? <FileElement
|
||||
edit={props.edit}
|
||||
file={props.file}
|
||||
onCancleRename={props.onCancleRename}
|
||||
onRename={props.onRename}
|
||||
/>:(props.dir)?<DirectoryComponent
|
||||
dir={props.dir}
|
||||
/>:<></>}
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
export default FileBrowserElement
|
||||
@@ -1,7 +1,12 @@
|
||||
import React from "react"
|
||||
import { FaRegFileAlt } from "react-icons/fa"
|
||||
import { MdFolderOpen } from "react-icons/md"
|
||||
import dateFormat from "../functions/dateFomat"
|
||||
import sizeToReadable from "../functions/sizeToReadable"
|
||||
import { Directory, File } from "../generated/graphql"
|
||||
import ObjID from "../types/ObjID"
|
||||
import FileBrowserElement from "./FileBrowserElement"
|
||||
import Renameable from "./Renameable"
|
||||
import Table, { Data, Row } from "./Table"
|
||||
|
||||
interface Props{
|
||||
directorys: Directory[]
|
||||
@@ -17,65 +22,59 @@ interface Props{
|
||||
onRenameDone?: (id: ObjID, changed: boolean, newName: string)=>void
|
||||
}
|
||||
|
||||
const FileBrowserList: React.FC<Props> = (props) => {
|
||||
return <>
|
||||
<table className="w-full">
|
||||
<thead className="border-b-2 dark:border-gray-900">
|
||||
<tr>
|
||||
<th className="text-left">Name</th>
|
||||
<th className="text-left">Last Modified</th>
|
||||
<th className="text-left">Size</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y dark:divide-gray-900">
|
||||
{ props.directorys.map(v => (<FileBrowserElement
|
||||
key={v.id.toString()}
|
||||
dir={v}
|
||||
onClick={(e,dir)=>{
|
||||
props.onDirClick?.(e,dir.id)
|
||||
}}
|
||||
onContextMenu={(e)=>{
|
||||
props.onDirContext?.(e,v.id)
|
||||
}}
|
||||
const FileBrowserList: React.FC<Props> = ({
|
||||
files,directorys,onDirClick,onDirContext,onFileClick,onFileContext,onRenameDone,editId,editEnable
|
||||
}) => {
|
||||
|
||||
edit={props.editEnable && (v.id === props.editId)}
|
||||
const tableData: Data = {
|
||||
headers: [
|
||||
{name: "Name"},
|
||||
{name: "Last Modified"},
|
||||
{name: "Size"}
|
||||
],
|
||||
body:[
|
||||
...directorys.map((e):Row<Directory>=>{return {
|
||||
cells:[
|
||||
{
|
||||
name:e.name || "",
|
||||
component: <div>
|
||||
<MdFolderOpen className="inline" />
|
||||
<Renameable
|
||||
text={e.name || ""}
|
||||
edit={editEnable && (e.id === editId)}
|
||||
onRename={(newName)=>{e.name != newName?onRenameDone?.(e.id,true,newName):onRenameDone?.(e.id,false,newName)}}
|
||||
onCancleRename={()=>onRenameDone?.(e.id,false,"")}
|
||||
/>
|
||||
</div>
|
||||
}],
|
||||
data: e,
|
||||
onClick: ((e,data)=>onDirClick?.(e,data.id)),
|
||||
onContextMenu: ((e,data)=>onDirContext?.(e,data.id))
|
||||
}}),
|
||||
...files.map((e):Row<File>=>{return {
|
||||
cells:[
|
||||
{
|
||||
name:e.name || "",
|
||||
component: <div>
|
||||
<FaRegFileAlt className="inline" />
|
||||
<Renameable
|
||||
text={e.name || ""}
|
||||
edit={editEnable && (e.id === editId)}
|
||||
onRename={(newName)=>{e.name != newName?onRenameDone?.(e.id,true,newName):onRenameDone?.(e.id,false,newName)}}
|
||||
onCancleRename={()=>onRenameDone?.(e.id,false,"")}
|
||||
/>
|
||||
</div>
|
||||
},
|
||||
{name:dateFormat(e.lastModified)},{name:sizeToReadable(e.size)}],
|
||||
data: e,
|
||||
onClick: ((e,data)=>onFileClick?.(e,data.id)),
|
||||
onContextMenu: ((e,data)=>onFileContext?.(e,data.id))
|
||||
}}),
|
||||
],
|
||||
|
||||
}
|
||||
|
||||
onRename={(newName)=>{
|
||||
if (v.name != newName){
|
||||
props.onRenameDone?.(v.id,true,newName)
|
||||
}else{
|
||||
props.onRenameDone?.(v.id,false,newName)
|
||||
}
|
||||
}}
|
||||
|
||||
onCancleRename={()=>props.onRenameDone?.(v.id,false,"")}
|
||||
/>))}
|
||||
|
||||
{ props.files.map(v => (<FileBrowserElement
|
||||
key={v.id.toString()}
|
||||
file={v}
|
||||
onClick={(e,file)=>{
|
||||
props.onFileClick?.(e,file.id)
|
||||
}}
|
||||
onContextMenu={(e)=>{
|
||||
props.onFileContext?.(e,v.id)
|
||||
}}
|
||||
|
||||
edit={props.editEnable && (v.id === props.editId)}
|
||||
|
||||
onRename={(newName)=>{
|
||||
if (v.name != newName){
|
||||
props.onRenameDone?.(v.id,true,newName)
|
||||
}else{
|
||||
props.onRenameDone?.(v.id,false,newName)
|
||||
}
|
||||
}}
|
||||
|
||||
onCancleRename={()=>props.onRenameDone?.(v.id,false,"")}
|
||||
/>))}
|
||||
</tbody>
|
||||
</table>
|
||||
</>
|
||||
return <Table data={tableData} />
|
||||
}
|
||||
|
||||
export default FileBrowserList
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import React from "react"
|
||||
import { File } from "../generated/graphql"
|
||||
import sizeToReadable from "../functions/sizeToReadable"
|
||||
import dateFormat from "../functions/dateFomat"
|
||||
import { FaRegFileAlt } from "react-icons/fa"
|
||||
import Renameable from "./Renameable"
|
||||
|
||||
interface Props {
|
||||
file: File,
|
||||
edit: boolean
|
||||
onRename?: (newName: string)=>void
|
||||
onCancleRename?: ()=>void
|
||||
}
|
||||
|
||||
const FileElement: React.FC<Props> = (props) => {
|
||||
return (
|
||||
<>
|
||||
<td>
|
||||
<FaRegFileAlt className="inline" />
|
||||
<Renameable
|
||||
text={props.file.name || ""}
|
||||
edit={props.edit}
|
||||
onCancleRename={props.onCancleRename}
|
||||
onRename={props.onRename}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{dateFormat(props.file.lastModified)}
|
||||
</td>
|
||||
<td>
|
||||
{sizeToReadable(props.file.size)}
|
||||
</td>
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default FileElement
|
||||
@@ -10,12 +10,10 @@ interface Props {
|
||||
handleDrop?: (files: FileList)=>void
|
||||
}
|
||||
|
||||
const DragAndDrop: React.FC<Props> = (props) => {
|
||||
const dropRef = React.createRef<HTMLDivElement>()
|
||||
const GlobalDragAndDrop: React.FC<Props> = (props) => {
|
||||
const [hover,setHover] = useState(false)
|
||||
|
||||
function handleDragEnter(event: DragEvent) {
|
||||
// console.debug("dragenter",event)
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setHover(true)
|
||||
@@ -23,7 +21,6 @@ const DragAndDrop: React.FC<Props> = (props) => {
|
||||
}
|
||||
|
||||
function handleDragLeave(event: DragEvent) {
|
||||
// console.debug("dragleave",event)
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setHover(false)
|
||||
@@ -38,7 +35,6 @@ const DragAndDrop: React.FC<Props> = (props) => {
|
||||
}
|
||||
|
||||
function handleDrop(event: DragEvent) {
|
||||
console.debug("drop",event)
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
setHover(false)
|
||||
@@ -49,31 +45,25 @@ const DragAndDrop: React.FC<Props> = (props) => {
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
if(dropRef.current){
|
||||
dropRef.current.addEventListener("dragenter",handleDragEnter)
|
||||
dropRef.current.addEventListener("dragleave",handleDragLeave)
|
||||
dropRef.current.addEventListener("dragover",handleDragOver)
|
||||
dropRef.current.addEventListener("drop",handleDrop)
|
||||
document.addEventListener("dragenter",handleDragEnter)
|
||||
document.addEventListener("dragleave",handleDragLeave)
|
||||
document.addEventListener("dragover",handleDragOver)
|
||||
document.addEventListener("drop",handleDrop)
|
||||
|
||||
return ()=>{
|
||||
if (dropRef.current){
|
||||
dropRef.current.removeEventListener("dragenter",handleDragEnter)
|
||||
dropRef.current.removeEventListener("dragleave",handleDragLeave)
|
||||
dropRef.current.removeEventListener("dragover",handleDragOver)
|
||||
dropRef.current.removeEventListener("drop",handleDrop)
|
||||
}
|
||||
}
|
||||
return ()=>{
|
||||
document.removeEventListener("dragenter",handleDragEnter)
|
||||
document.removeEventListener("dragleave",handleDragLeave)
|
||||
document.removeEventListener("dragover",handleDragOver)
|
||||
document.removeEventListener("drop",handleDrop)
|
||||
|
||||
}
|
||||
|
||||
},[])
|
||||
|
||||
return (
|
||||
<div ref={dropRef}
|
||||
className={`fixed top-0 left-0 w-full h-full z-10
|
||||
${hover? "border-dashed border-gray-600 border-4":""}`}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
<div className={`fixed top-0 left-0 w-full h-full z-10 pointer-events-none
|
||||
${hover? "border-dashed border-gray-600 border-4":""}`} />
|
||||
)
|
||||
}
|
||||
|
||||
export default DragAndDrop
|
||||
export default GlobalDragAndDrop
|
||||
59
src/components/Table.tsx
Normal file
59
src/components/Table.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import React from "react"
|
||||
|
||||
export interface Header {
|
||||
name: string
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export interface Row<T = any> {
|
||||
cells: Cell[]
|
||||
data?: T,
|
||||
// TODO: maybe there is some TS magic to apply here so that rowData has the same type as data without using generics
|
||||
onClick?: (e: React.MouseEvent, rowData: T ) => void
|
||||
onContextMenu?: (e: React.MouseEvent, data: T)=>void
|
||||
}
|
||||
|
||||
export interface Cell {
|
||||
name: string
|
||||
component?: React.ReactNode
|
||||
}
|
||||
|
||||
export interface Data {
|
||||
headers: Header[]
|
||||
body: Row[]
|
||||
}
|
||||
|
||||
interface Props {
|
||||
data: Data
|
||||
}
|
||||
|
||||
const Table: React.FC<Props> = ({data:{headers,body}}) => {
|
||||
const numCol = headers.length
|
||||
return (
|
||||
<table className="w-full">
|
||||
<thead className="border-b-2 dark:border-gray-900">
|
||||
{headers.map((e,i)=><th key={i} className="text-left">{e.name}</th>)}
|
||||
</thead>
|
||||
<tbody className="divide-y dark:divide-gray-900">
|
||||
{body.map((row,rowIndex)=>
|
||||
<tr
|
||||
className="hover:bg-gray-100 dark:hover:bg-gray-900 text-lg"
|
||||
key={rowIndex}
|
||||
onClick={e=>row.onClick?.(e,row.data)}
|
||||
onContextMenu={e=>row.onContextMenu?.(e,row.data)}
|
||||
>
|
||||
{(row.cells.length >= numCol?
|
||||
row.cells:
|
||||
row.cells.concat(Array(numCol - row.cells.length).fill({name:""}))) // Pad array to fit numCol
|
||||
.map((cell,cellIndex)=>
|
||||
<td key={cellIndex}>
|
||||
{cell.component || cell.name}
|
||||
</td>
|
||||
)}
|
||||
</tr>)}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
export default Table
|
||||
@@ -102,6 +102,8 @@ export type RootQuery = {
|
||||
__typename?: "RootQuery";
|
||||
/** True if the user is authorized */
|
||||
authorized: Scalars["Boolean"];
|
||||
/** List available buckets */
|
||||
buckets: Array<Maybe<Scalars["String"]>>;
|
||||
directories: Array<Directory>;
|
||||
file?: Maybe<File>;
|
||||
files: Array<File>;
|
||||
@@ -191,6 +193,14 @@ export type IsAuthQuery = (
|
||||
& Pick<RootQuery, "authorized">
|
||||
);
|
||||
|
||||
export type ListBucktesQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ListBucktesQuery = (
|
||||
{ __typename?: "RootQuery" }
|
||||
& Pick<RootQuery, "buckets">
|
||||
);
|
||||
|
||||
export type LoginMutationVariables = Exact<{
|
||||
username: Scalars["String"];
|
||||
password: Scalars["String"];
|
||||
@@ -436,6 +446,38 @@ export function useIsAuthLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<IsA
|
||||
export type IsAuthQueryHookResult = ReturnType<typeof useIsAuthQuery>;
|
||||
export type IsAuthLazyQueryHookResult = ReturnType<typeof useIsAuthLazyQuery>;
|
||||
export type IsAuthQueryResult = Apollo.QueryResult<IsAuthQuery, IsAuthQueryVariables>;
|
||||
export const ListBucktesDocument = gql`
|
||||
query listBucktes {
|
||||
buckets
|
||||
}
|
||||
`
|
||||
|
||||
/**
|
||||
* __useListBucktesQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useListBucktesQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useListBucktesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useListBucktesQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useListBucktesQuery(baseOptions?: Apollo.QueryHookOptions<ListBucktesQuery, ListBucktesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ListBucktesQuery, ListBucktesQueryVariables>(ListBucktesDocument, options)
|
||||
}
|
||||
export function useListBucktesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ListBucktesQuery, ListBucktesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ListBucktesQuery, ListBucktesQueryVariables>(ListBucktesDocument, options)
|
||||
}
|
||||
export type ListBucktesQueryHookResult = ReturnType<typeof useListBucktesQuery>;
|
||||
export type ListBucktesLazyQueryHookResult = ReturnType<typeof useListBucktesLazyQuery>;
|
||||
export type ListBucktesQueryResult = Apollo.QueryResult<ListBucktesQuery, ListBucktesQueryVariables>;
|
||||
export const LoginDocument = gql`
|
||||
mutation Login($username: String!, $password: String!) {
|
||||
login(username: $username, password: $password) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
3
src/graphql/listBuckets.graphql
Normal file
3
src/graphql/listBuckets.graphql
Normal file
@@ -0,0 +1,3 @@
|
||||
query listBucktes{
|
||||
buckets
|
||||
}
|
||||
Reference in New Issue
Block a user