Compare commits

...

5 Commits

Author SHA1 Message Date
79fe625ae7 modal prevent propagation 2021-07-30 23:45:46 +02:00
4d28da04a6 center modal 2021-07-30 23:36:50 +02:00
ab20c0693c modal and openers 2021-07-30 22:54:36 +02:00
baa04685be disable prop types in eslint 2021-07-30 22:54:16 +02:00
67cbf7ab28 basic file opening 2021-07-28 12:49:35 +02:00
15 changed files with 256 additions and 5 deletions

View File

@@ -27,3 +27,4 @@ rules:
semi:
- error
- never
react/prop-types: 0

9
src/App.module.scss Normal file
View File

@@ -0,0 +1,9 @@
.imageOpener{
}
.imageOpenerImage{
display: block;
margin-left: auto;
margin-right: auto;
}

View File

View File

@@ -1,5 +1,4 @@
import React from "react"
import "./App.scss"
import FileBrowser from "./components/FileBrowser"
const App: React.FC = () => {

View File

@@ -3,11 +3,14 @@ import { useState } from "react"
import { useOpenDirQuery } from "../generated/graphql"
import Breadcrum from "./Breadcrum"
import FileBrowserElement from "./FileBrowserElement"
import FileOpen from "./FileOpen"
const FileBrowser: React.FC = () => {
const [path,setPath] = useState("/")
const [openFileId, setOpenFileId] = useState("")
const [showFile, setShowFile] = useState(false)
const { data } = useOpenDirQuery({
const { data, loading } = useOpenDirQuery({
variables:{
path
}
@@ -19,6 +22,9 @@ const FileBrowser: React.FC = () => {
setPath(newPath)
}}/>
<div>
{loading &&
<div className="loading loading-lg"></div> // TODO: center
}
<table className="table table-striped table-hover">
<thead>
<tr>
@@ -31,18 +37,25 @@ const FileBrowser: React.FC = () => {
{ data?.directorys.map(v => (<FileBrowserElement
key={v?.id}
dir={v}
onClick={(data)=>{
setPath(data.id)
onClick={(dir)=>{
setPath(dir.id)
}}
/>))}
{ data?.files.map(v => (<FileBrowserElement
key={v?.id}
file={v}
onClick={(file)=>{
setOpenFileId(file.id)
setShowFile(true)
}}
/>))}
</tbody>
</table>
</div>
{<FileOpen id={openFileId} show={showFile} onCloseClick={()=>{
setShowFile(false)
}} />}
</div>
)
}

View File

@@ -0,0 +1,68 @@
import React from "react"
import PropTypes from "prop-types"
import { useGetFileQuery } from "../generated/graphql"
import ImageOpener from "./ImageOpener"
import TextOpener from "./TextOpener"
import Modal from "./Modal"
import { useState } from "react"
interface Props {
id: string
show: boolean
onCloseClick?: ()=>void
}
const FileOpen: React.FC<Props> = (props) => {
const { data } = useGetFileQuery({
variables:{
id: props.id
}
})
let opener = <div>TODO</div>
if(data?.file != null){
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
if (data.file.contentType?.startsWith("image/")){
opener = <ImageOpener file={data?.file} />
}else if (data.file.contentType?.startsWith("text/")){
opener = <TextOpener file={data.file} />
}else{
// Get Opener bases on file extension
const ext = data.file.name?.split(".").pop()
switch (ext) {
case "png":
case "jpg":
case "jpeg":
case "gif":
// TODO: more ext
opener = <ImageOpener file={data.file} />
break
case "txt":
case "md":
opener = <TextOpener file={data.file} />
break
default:
opener = <ImageOpener file={data?.file} />
break
}
}
}
return (
<Modal onCloseClick={()=>{
props.onCloseClick?.()
}} show={props.show} >
{opener}
</Modal>
)
}
FileOpen.propTypes = {
id: PropTypes.string.isRequired,
show: PropTypes.bool.isRequired,
onCloseClick: PropTypes.func
}
export default FileOpen

View File

@@ -0,0 +1,23 @@
import React from "react"
import PropTypes from "prop-types"
import { File } from "../generated/graphql"
import genDownloadLink from "../functions/genDownloadLink"
import style from "./../App.module.scss"
interface Props {
file: File
}
const ImageOpener: React.FC<Props> = (props) => {
return (
<div className={style.imageOpener}>
<img className={`img-responsive img-fit-contain ${style.imageOpenerImage}`} src={genDownloadLink(props.file.id)}/>
</div>
)
}
ImageOpener.propTypes = {
file: PropTypes.any.isRequired
}
export default ImageOpener

23
src/components/Modal.tsx Normal file
View File

@@ -0,0 +1,23 @@
import React from "react"
import style from "./../style/Modal.module.scss"
interface Props {
show: boolean
onCloseClick?: ()=>void
}
const Modal: React.FC<Props> = (props) => {
return (
<div className={`${style.overlay} ${!props.show && style.hidden}`} onClick={()=>{
props.onCloseClick?.()
}}>
<div className={style.body} onClick={(e)=>{
e.stopPropagation()
}}>
{props.children}
</div>
</div>
)
}
export default Modal

View File

@@ -0,0 +1,23 @@
import React from "react"
import PropTypes from "prop-types"
import { File } from "../generated/graphql"
interface Props {
file: File
}
const TextOpener: React.FC<Props> = (props) => {
return (
<div>
<textarea>
{props.file.name}
</textarea>
</div>
)
}
TextOpener.propTypes = {
file: PropTypes.any.isRequired,
}
export default TextOpener

View File

@@ -0,0 +1,5 @@
function genDownloadLink(id:string): string {
return `/api/file?id=${encodeURIComponent(id)}`
}
export default genDownloadLink

View File

@@ -58,6 +58,19 @@ export type RootQueryFilesArgs = {
path: Scalars["String"];
};
export type GetFileQueryVariables = Exact<{
id: Scalars["ID"];
}>;
export type GetFileQuery = (
{ __typename?: "RootQuery" }
& { file?: Maybe<(
{ __typename?: "File" }
& Pick<File, "id" | "name" | "size" | "contentType" | "etag">
)> }
);
export type OpenDirQueryVariables = Exact<{
path: Scalars["String"];
}>;
@@ -75,6 +88,45 @@ export type OpenDirQuery = (
);
export const GetFileDocument = gql`
query getFile($id: ID!) {
file(id: $id) {
id
name
size
contentType
etag
}
}
`
/**
* __useGetFileQuery__
*
* To run a query within a React component, call `useGetFileQuery` and pass it any options that fit your needs.
* When your component renders, `useGetFileQuery` 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 } = useGetFileQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useGetFileQuery(baseOptions: Apollo.QueryHookOptions<GetFileQuery, GetFileQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<GetFileQuery, GetFileQueryVariables>(GetFileDocument, options)
}
export function useGetFileLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<GetFileQuery, GetFileQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<GetFileQuery, GetFileQueryVariables>(GetFileDocument, options)
}
export type GetFileQueryHookResult = ReturnType<typeof useGetFileQuery>;
export type GetFileLazyQueryHookResult = ReturnType<typeof useGetFileLazyQuery>;
export type GetFileQueryResult = Apollo.QueryResult<GetFileQuery, GetFileQueryVariables>;
export const OpenDirDocument = gql`
query openDir($path: String!) {
files(path: $path) {

View File

@@ -0,0 +1,9 @@
query getFile($id: ID!){
file(id: $id){
id
name
size
contentType
etag
}
}

View File

@@ -0,0 +1,2 @@
@import "node_modules/spectre.css/src/spectre";

View File

@@ -3,7 +3,6 @@ import ReactDOM from "react-dom"
import "./index.scss"
import App from "./App"
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client"
import "spectre.css"
const client = new ApolloClient({
uri: "/graphql",

View File

@@ -0,0 +1,25 @@
.overlay{
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.4);
display: flex;
justify-content: center;
align-items: center;
}
.body{
background-color: #fefefe;
margin-left: auto;
margin-right: auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}
.hidden{
display: none;
}