headline
This commit is contained in:
parent
541de21e66
commit
5002bc22f9
@ -1,27 +1,24 @@
|
|||||||
import React from 'react';
|
import React, { useRef, useState } from 'react';
|
||||||
import ReactDom from 'react-dom';
|
import ReactDom from 'react-dom';
|
||||||
import {BrowserRouter, Route, Switch as RouterSwitch} from 'react-router-dom';
|
import {BrowserRouter, Route, Switch as RouterSwitch} from 'react-router-dom';
|
||||||
import {Headline} from './page/headline';
|
import { Gallery, ContentAbout} from './page/mod';
|
||||||
import {Gallery} from './page/gallery';
|
|
||||||
import {ContentAbout} from './page/contentinfo';
|
|
||||||
|
|
||||||
import './css/style.css';
|
import './css/style.css';
|
||||||
|
|
||||||
const FooProfile = ()=><div>test profile</div>;
|
const FooProfile = ()=><div>test profile</div>;
|
||||||
const App = ()=> (
|
const App = () => {
|
||||||
<BrowserRouter>
|
return (<BrowserRouter>
|
||||||
<Headline>
|
|
||||||
<RouterSwitch>
|
<RouterSwitch>
|
||||||
<Route path="/" exact component={Gallery}></Route>
|
<Route path="/" exact render={()=><Gallery />}></Route>
|
||||||
<Route path="/doc" component={ContentAbout}></Route>
|
<Route path="/search" render={()=><Gallery />}></Route>
|
||||||
|
<Route path="/doc" render={(prop)=><ContentAbout {...prop}/>}></Route>
|
||||||
<Route path="/profile" component={FooProfile}></Route>
|
<Route path="/profile" component={FooProfile}></Route>
|
||||||
<Route>
|
<Route>
|
||||||
<div>404 Not Found</div>
|
<div>404 Not Found</div>
|
||||||
</Route>
|
</Route>
|
||||||
</RouterSwitch>
|
</RouterSwitch>
|
||||||
</Headline>
|
</BrowserRouter>);
|
||||||
</BrowserRouter>
|
};
|
||||||
);
|
|
||||||
|
|
||||||
ReactDom.render(
|
ReactDom.render(
|
||||||
<App/>,
|
<App/>,
|
||||||
|
@ -34,34 +34,93 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||||||
title: {
|
title: {
|
||||||
marginLeft: theme.spacing(1),
|
marginLeft: theme.spacing(1),
|
||||||
},
|
},
|
||||||
InfoContainer: {
|
infoContainer: {
|
||||||
|
padding: theme.spacing(2),
|
||||||
|
},
|
||||||
|
subinfoContainer: {
|
||||||
display: 'grid',
|
display: 'grid',
|
||||||
gridTemplateColumns: '100px auto',
|
gridTemplateColumns: '100px auto',
|
||||||
overflowY: 'hidden',
|
overflowY: 'hidden',
|
||||||
|
},
|
||||||
|
short_subinfoContainer:{
|
||||||
|
[theme.breakpoints.up("xs")]:{
|
||||||
|
display:'none',
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
short_root:{
|
||||||
|
overflowY:'hidden',
|
||||||
|
display:'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
[theme.breakpoints.up("sm")]:{
|
||||||
|
height:200,
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
short_thumbnail_anchor:{
|
||||||
|
background: '#272733',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
[theme.breakpoints.up("sm")]:{
|
||||||
|
width: theme.spacing(25),
|
||||||
|
height: theme.spacing(25),
|
||||||
|
flexShrink: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
short_thumbnail_content: {
|
||||||
|
maxWidth: '100%',
|
||||||
|
maxHeight: '100%',
|
||||||
|
},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
export const ContentInfo = (props: { content: Content, children?: React.ReactNode }) => {
|
export const ContentInfo = (props: {
|
||||||
|
content: Content, children?: React.ReactNode, classes?: {
|
||||||
|
root?: string,
|
||||||
|
thumbnail_anchor?: string,
|
||||||
|
thumbnail_content?: string,
|
||||||
|
tag_list?: string,
|
||||||
|
title?: string,
|
||||||
|
infoContainer?: string,
|
||||||
|
subinfoContainer?: string
|
||||||
|
},
|
||||||
|
gallery?:string,
|
||||||
|
short?:boolean
|
||||||
|
}) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const content = props?.content;
|
const content = props.content;
|
||||||
|
const propclasses = props.classes || {};
|
||||||
|
|
||||||
|
const rootName = props.short ? classes.short_root : classes.root;
|
||||||
|
const thumbnail_anchor = props.short ? classes.short_thumbnail_anchor : "";
|
||||||
|
const thumbnail_content = props.short ? classes.short_thumbnail_content :
|
||||||
|
classes.thumbnail_content;
|
||||||
|
const subinfoContainer = props.short ? classes.short_subinfoContainer :
|
||||||
|
classes.subinfoContainer;
|
||||||
let allTag = content.tags;
|
let allTag = content.tags;
|
||||||
const artists = allTag.filter(x => x.startsWith("artist:")).map(x => x.substr(7));
|
const artists = allTag.filter(x => x.startsWith("artist:")).map(x => x.substr(7));
|
||||||
allTag = allTag.filter(x => !x.startsWith("artist:"));
|
allTag = allTag.filter(x => !x.startsWith("artist:"));
|
||||||
return (<Paper className={classes.root}>
|
return (<Paper className={propclasses.root || rootName} elevation={4}>
|
||||||
<Link component={RouterLink} to={makeContentReaderUrl(content.id)}>
|
<Link className={propclasses.thumbnail_anchor || thumbnail_anchor} component={RouterLink} to={{
|
||||||
<ThumbnailContainer content={content} className={classes.thumbnail_content}></ThumbnailContainer>
|
pathname:makeContentReaderUrl(content.id),
|
||||||
|
state:props.gallery
|
||||||
|
}}>
|
||||||
|
<ThumbnailContainer content={content}
|
||||||
|
className={propclasses.thumbnail_content || thumbnail_content}/>
|
||||||
</Link>
|
</Link>
|
||||||
<Box style={{ padding: theme.spacing(1) }}>
|
<Box className={propclasses.infoContainer || classes.infoContainer}>
|
||||||
<Link variant='h5' color='inherit' component={RouterLink} to={makeContentReaderUrl(content.id)}
|
<Link variant='h5' color='inherit' component={RouterLink} to={{
|
||||||
className={classes.title}>
|
pathname: props.gallery === undefined ? makeContentReaderUrl(content.id) : makeContentInfoUrl(content.id),
|
||||||
|
state:props.gallery
|
||||||
|
}}
|
||||||
|
className={propclasses.title || classes.title}>
|
||||||
{content.title}
|
{content.title}
|
||||||
</Link>
|
</Link>
|
||||||
<Box className={classes.InfoContainer}>
|
<Box className={propclasses.subinfoContainer || subinfoContainer}>
|
||||||
<Typography variant='subtitle1'>Artist</Typography>
|
<Typography variant='subtitle1'>Artist</Typography>
|
||||||
<Box style={{ alignSelf: "center" }}>{artists.join(", ")}</Box>
|
<Box style={{ alignSelf: "center" }}>{artists.join(", ")}</Box>
|
||||||
<Typography variant='subtitle1'>Tags</Typography>
|
<Typography variant='subtitle1'>Tags</Typography>
|
||||||
<Box className={classes.tag_list}>
|
<Box className={propclasses.tag_list || classes.tag_list}>
|
||||||
{allTag.map(x => {
|
{allTag.map(x => {
|
||||||
return (<TagChip key={x} label={x} clickable={true} tagname={x} size="small"></TagChip>);
|
return (<TagChip key={x} label={x} clickable={true} tagname={x} size="small"></TagChip>);
|
||||||
})}
|
})}
|
||||||
|
71
src/client/component/galleryinfo.tsx
Normal file
71
src/client/component/galleryinfo.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { Box, Paper, Link, useMediaQuery, Portal, List, ListItem, ListItemIcon, Tooltip, ListItemText } from '@material-ui/core';
|
||||||
|
import {useTheme, makeStyles, Theme} from '@material-ui/core/styles';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import ContentAccessor,{QueryListOption, Content} from '../accessor/contents';
|
||||||
|
import {Link as RouterLink} from 'react-router-dom';
|
||||||
|
import { LoadingCircle, ContentInfo, NavList, NavItem } from './mod';
|
||||||
|
import {toQueryString} from '../accessor/util';
|
||||||
|
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme:Theme)=>({
|
||||||
|
root:{
|
||||||
|
display:"grid",
|
||||||
|
gridGap: theme.spacing(4),
|
||||||
|
},
|
||||||
|
anchor_thumbnail:{
|
||||||
|
background: '#272733',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
[theme.breakpoints.up("sm")]:{
|
||||||
|
width: theme.spacing(25),
|
||||||
|
height: theme.spacing(25),
|
||||||
|
flexShrink: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentPaper:{
|
||||||
|
overflowY:'hidden',
|
||||||
|
display:'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
[theme.breakpoints.up("sm")]:{
|
||||||
|
height:200,
|
||||||
|
flexDirection: 'row',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
content_thumnail: {
|
||||||
|
maxWidth: '100%',
|
||||||
|
maxHeight: '100%',
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export type GalleryProp = {
|
||||||
|
option?:QueryListOption;
|
||||||
|
};
|
||||||
|
type GalleryState = {
|
||||||
|
content:Content[]|undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GalleryInfo = (props: GalleryProp)=>{
|
||||||
|
const [state,setState]= useState<GalleryState>({content:undefined});
|
||||||
|
useEffect(()=>{
|
||||||
|
(async ()=>{
|
||||||
|
const c = await ContentAccessor.findList(props.option);
|
||||||
|
setState({content:c});
|
||||||
|
})()
|
||||||
|
},[props.option]);
|
||||||
|
const classes = useStyles();
|
||||||
|
const queryString = toQueryString(props.option||{});
|
||||||
|
|
||||||
|
if(state.content === undefined){
|
||||||
|
return (<LoadingCircle/>);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return (<Box className={classes.root}>{
|
||||||
|
state.content.map(x=>{
|
||||||
|
return (<ContentInfo content={x} key={x.id}
|
||||||
|
gallery={`/search?${queryString}`} short/>);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</Box>);
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,13 @@
|
|||||||
import ReactDom from 'react-dom';
|
import ReactDom from 'react-dom';
|
||||||
import React, { useState } from 'react';
|
import React, { ReactNode, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Button, CssBaseline, Divider, IconButton, List, ListItem, Drawer,
|
Button, CssBaseline, Divider, IconButton, List, ListItem, Drawer,
|
||||||
AppBar, Toolbar, Typography, InputBase, ListItemIcon, ListItemText, Menu, MenuItem,
|
AppBar, Toolbar, Typography, InputBase, ListItemIcon, ListItemText, Menu, MenuItem,
|
||||||
Hidden, Tooltip
|
Hidden, Tooltip, Link
|
||||||
} from '@material-ui/core';
|
} from '@material-ui/core';
|
||||||
import { makeStyles, Theme, useTheme, fade } from '@material-ui/core/styles';
|
import { makeStyles, Theme, useTheme, fade } from '@material-ui/core/styles';
|
||||||
import { ChevronLeft, ChevronRight, Menu as MenuIcon, Search as SearchIcon, ArrowBack as ArrowBackIcon, AccountCircle } from '@material-ui/icons';
|
import { ChevronLeft, ChevronRight, Menu as MenuIcon, Search as SearchIcon, AccountCircle } from '@material-ui/icons';
|
||||||
import {Link} from 'react-router-dom';
|
import { Link as RouterLink, useRouteMatch } from 'react-router-dom';
|
||||||
|
|
||||||
const drawerWidth = 240;
|
const drawerWidth = 240;
|
||||||
|
|
||||||
@ -104,7 +104,12 @@ const useStyles = makeStyles((theme: Theme) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const Headline = (prop: { children?: React.ReactNode, navListItem?: React.ReactNode, isLogin?:boolean}) => {
|
export const Headline = (prop: {
|
||||||
|
children?: React.ReactNode,
|
||||||
|
navListItem?: React.ReactNode,
|
||||||
|
isLogin?: boolean,
|
||||||
|
menu: React.ReactNode
|
||||||
|
}) => {
|
||||||
const [v, setv] = useState(false);
|
const [v, setv] = useState(false);
|
||||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
@ -124,7 +129,7 @@ export const Headline = (prop: { children?: React.ReactNode, navListItem?: React
|
|||||||
transformOrigin={{ horizontal: 'right', vertical: "top" }}
|
transformOrigin={{ horizontal: 'right', vertical: "top" }}
|
||||||
onClose={handleProfileMenuClose}
|
onClose={handleProfileMenuClose}
|
||||||
>
|
>
|
||||||
<MenuItem component={Link} to='/profile'>Profile</MenuItem>
|
<MenuItem component={RouterLink} to='/profile'>Profile</MenuItem>
|
||||||
<MenuItem>Logout</MenuItem>
|
<MenuItem>Logout</MenuItem>
|
||||||
</Menu>);
|
</Menu>);
|
||||||
const drawer_contents = (<>
|
const drawer_contents = (<>
|
||||||
@ -134,17 +139,7 @@ export const Headline = (prop: { children?: React.ReactNode, navListItem?: React
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
<Divider />
|
||||||
<List>
|
{prop.menu}
|
||||||
<ListItem button key="Back">
|
|
||||||
<ListItemIcon>
|
|
||||||
<Tooltip title="back" placement="bottom">
|
|
||||||
<ArrowBackIcon></ArrowBackIcon>
|
|
||||||
</Tooltip>
|
|
||||||
</ListItemIcon>
|
|
||||||
<ListItemText primary="Back"></ListItemText>
|
|
||||||
</ListItem>
|
|
||||||
{prop.navListItem}
|
|
||||||
</List>
|
|
||||||
</>);
|
</>);
|
||||||
return (<div className={classes.root}>
|
return (<div className={classes.root}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
@ -157,9 +152,9 @@ export const Headline = (prop: { children?: React.ReactNode, navListItem?: React
|
|||||||
className={classes.menuButton}>
|
className={classes.menuButton}>
|
||||||
<MenuIcon></MenuIcon>
|
<MenuIcon></MenuIcon>
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Typography variant="h5" noWrap className={classes.title}>
|
<Link variant="h5" noWrap className={classes.title} color="inherit" component={RouterLink} to="/">
|
||||||
Ionian
|
Ionian
|
||||||
</Typography>
|
</Link>
|
||||||
<div className={classes.grow}></div>
|
<div className={classes.grow}></div>
|
||||||
<div className={classes.search}>
|
<div className={classes.search}>
|
||||||
<div className={classes.searchIcon}>
|
<div className={classes.searchIcon}>
|
@ -1,3 +1,6 @@
|
|||||||
export * from './contentinfo';
|
export * from './contentinfo';
|
||||||
export * from './loading';
|
export * from './loading';
|
||||||
export * from './tagchip';
|
export * from './tagchip';
|
||||||
|
export * from './navlist';
|
||||||
|
export * from './galleryinfo';
|
||||||
|
export * from './headline';
|
21
src/client/component/navlist.tsx
Normal file
21
src/client/component/navlist.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import {List, ListItem, ListItemIcon, Tooltip, ListItemText} from '@material-ui/core';
|
||||||
|
import React from 'react';
|
||||||
|
import {Link as RouterLink} from 'react-router-dom';
|
||||||
|
import {LocationDescriptorObject} from 'history';
|
||||||
|
|
||||||
|
export const NavItem = (props:{name:string,to:string, icon:React.ReactElement<any,any>})=>{
|
||||||
|
return (<ListItem button key={props.name} component={RouterLink} to={props.to}>
|
||||||
|
<ListItemIcon>
|
||||||
|
<Tooltip title={props.name.toLocaleLowerCase()} placement="bottom">
|
||||||
|
{props.icon}
|
||||||
|
</Tooltip>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary={props.name}></ListItemText>
|
||||||
|
</ListItem>);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NavList = (props: {children?:React.ReactNode})=>{
|
||||||
|
return (<List>
|
||||||
|
{props.children}
|
||||||
|
</List>);
|
||||||
|
}
|
@ -1,10 +1,11 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Redirect, Route, Switch, useHistory, useRouteMatch, match as MatchType, Link as RouterLink } from 'react-router-dom';
|
import { Redirect, Route, Switch, useHistory, useRouteMatch, match as MatchType, Link as RouterLink, useParams, useLocation } from 'react-router-dom';
|
||||||
import ContentAccessor, { Content } from '../accessor/contents';
|
import ContentAccessor, { Content } from '../accessor/contents';
|
||||||
import { LoadingCircle } from '../component/loading';
|
import { LoadingCircle } from '../component/loading';
|
||||||
import { Link, Paper, makeStyles, Theme, Box, useTheme, Typography } from '@material-ui/core';
|
import { Link, Paper, makeStyles, Theme, Box, useTheme, Typography } from '@material-ui/core';
|
||||||
|
import {ArrowBack as ArrowBackIcon } from '@material-ui/icons';
|
||||||
import { MangaReader } from './reader/manga';
|
import { MangaReader } from './reader/manga';
|
||||||
import { ContentInfo } from '../component/mod';
|
import { ContentInfo, Headline, NavItem, NavList } from '../component/mod';
|
||||||
|
|
||||||
export const makeContentInfoUrl = (id: number) => `/doc/${id}`;
|
export const makeContentInfoUrl = (id: number) => `/doc/${id}`;
|
||||||
export const makeMangaReaderUrl = (id: number) => `/doc/${id}/reader`;
|
export const makeMangaReaderUrl = (id: number) => `/doc/${id}/reader`;
|
||||||
@ -15,39 +16,65 @@ type ContentState = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ContentAbout = (prop: { match: MatchType }) => {
|
export const ContentAbout = (prop: { match: MatchType }) => {
|
||||||
const match = useRouteMatch("/doc/:id");
|
const match = useRouteMatch<{id:string}>("/doc/:id");
|
||||||
if (match == null) {
|
if (match == null) {
|
||||||
throw new Error("unreachable");
|
throw new Error("unreachable");
|
||||||
}
|
}
|
||||||
const m = /\/doc\/(\d+)/.exec(match.url);
|
const id = Number.parseInt(match.params['id']);
|
||||||
const id = m !== null ? Number.parseInt(m[1]) : NaN;
|
|
||||||
const [info, setInfo] = useState<ContentState>({ content: undefined, notfound:false });
|
const [info, setInfo] = useState<ContentState>({ content: undefined, notfound:false });
|
||||||
|
const location = useLocation();
|
||||||
|
console.log("state : "+location.state);
|
||||||
|
const menu_list = (
|
||||||
|
<NavList>
|
||||||
|
<NavItem name="Back" to="/" icon={<ArrowBackIcon/>}></NavItem>
|
||||||
|
</NavList>
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
(async () => {
|
||||||
|
console.log("mount content about");
|
||||||
if (!isNaN(id)) {
|
if (!isNaN(id)) {
|
||||||
const c = await ContentAccessor.findById(id);
|
const c = await ContentAccessor.findById(id);
|
||||||
setInfo({ content: c, notfound: c === undefined });
|
setInfo({ content: c, notfound: c === undefined });
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
return ()=>{console.log("unmount content about")}
|
||||||
}, []);
|
}, []);
|
||||||
if (isNaN(id)) {
|
if (isNaN(id)) {
|
||||||
return (<Typography variant='h2'>Oops. Invalid ID</Typography>)
|
return (
|
||||||
|
<Headline menu={menu_list}>
|
||||||
|
<Typography variant='h2'>Oops. Invalid ID</Typography>
|
||||||
|
</Headline>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else if(info.notfound){
|
else if(info.notfound){
|
||||||
return (<Typography variant='h2'>Content has been removed.</Typography>)
|
return (
|
||||||
|
<Headline menu={menu_list}>
|
||||||
|
<Typography variant='h2'>Content has been removed.</Typography>
|
||||||
|
</Headline>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
else if (info.content === undefined) {
|
else if (info.content === undefined) {
|
||||||
return (<LoadingCircle />);
|
return (<Headline menu={menu_list}>
|
||||||
|
<LoadingCircle />
|
||||||
|
</Headline>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else return (<Switch>
|
else return (<Switch>
|
||||||
<Route exact path={`${prop.match.path}/:id`}>
|
<Route exact path={`${prop.match.path}/:id`}>
|
||||||
|
<Headline menu={menu_list}>
|
||||||
<ContentInfo content={info.content}></ContentInfo>
|
<ContentInfo content={info.content}></ContentInfo>
|
||||||
|
</Headline>
|
||||||
</Route>
|
</Route>
|
||||||
<Route exact path={`${prop.match.path}/:id/reader`}>
|
<Route exact path={`${prop.match.path}/:id/reader`}>
|
||||||
|
<Headline menu={menu_list}>
|
||||||
<MangaReader content={info.content}></MangaReader>
|
<MangaReader content={info.content}></MangaReader>
|
||||||
|
</Headline>
|
||||||
</Route>
|
</Route>
|
||||||
<Route>
|
<Route>
|
||||||
|
<Headline menu={menu_list}>
|
||||||
<div>404 Not Found invalid url : {prop.match.path}</div>
|
<div>404 Not Found invalid url : {prop.match.path}</div>
|
||||||
|
</Headline>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>);
|
</Switch>);
|
||||||
}
|
}
|
@ -1,108 +1,14 @@
|
|||||||
import { Box, Paper, Link, useMediaQuery } from '@material-ui/core';
|
import React from 'react';
|
||||||
import React, { useEffect, useState } from 'react';
|
import { NavList, NavItem, Headline } from '../component/mod';
|
||||||
import ContentAccessor,{makeThumbnailUrl, QueryListOption, Content} from '../accessor/contents';
|
import {ArrowBack as ArrowBackIcon} from '@material-ui/icons';
|
||||||
import {useTheme, makeStyles, Theme} from '@material-ui/core/styles';
|
import {GalleryInfo} from '../component/mod';
|
||||||
import {Link as RouterLink} from 'react-router-dom';
|
|
||||||
import {makeContentInfoUrl} from './contentinfo';
|
|
||||||
import { LoadingCircle } from '../component/loading';
|
|
||||||
import {ThumbnailContainer} from './reader/reader';
|
|
||||||
import {TagChip} from '../component/tagchip';
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme:Theme)=>({
|
export const Gallery = ()=>{
|
||||||
root:{
|
const menu_list = (<NavList>
|
||||||
display:"grid",
|
<NavItem name="Back" to="" icon={<ArrowBackIcon></ArrowBackIcon>}></NavItem>
|
||||||
gridGap: theme.spacing(4),
|
</NavList>);
|
||||||
},
|
|
||||||
anchor_thumbnail:{
|
|
||||||
background: '#272733',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
[theme.breakpoints.up("sm")]:{
|
|
||||||
width: theme.spacing(25),
|
|
||||||
height: theme.spacing(25),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
contentPaper:{
|
|
||||||
display:'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
[theme.breakpoints.up("sm")]:{
|
|
||||||
height:200,
|
|
||||||
flexDirection: 'row',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
content_thumnail: {
|
|
||||||
maxWidth: '100%',
|
|
||||||
maxHeight: '100%',
|
|
||||||
},
|
|
||||||
content_info:{
|
|
||||||
padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`,
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
},
|
|
||||||
content_info_title:{
|
|
||||||
marginLeft:theme.spacing(2),
|
|
||||||
},
|
|
||||||
tag_list:{
|
|
||||||
display:'none',
|
|
||||||
[theme.breakpoints.up("sm")]:{
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
flexWrap: 'wrap',
|
|
||||||
overflowY: 'hidden',
|
|
||||||
'& > *': {
|
|
||||||
margin: theme.spacing(0.5),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
export type GalleryProp = {
|
return (<Headline menu={menu_list}>
|
||||||
option?:QueryListOption;
|
<GalleryInfo></GalleryInfo>
|
||||||
};
|
</Headline>)
|
||||||
type GalleryState = {
|
|
||||||
content:Content[]|undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Gallery = (props: GalleryProp)=>{
|
|
||||||
const [state,setState]= useState<GalleryState>({content:undefined});
|
|
||||||
useEffect(()=>{
|
|
||||||
(async ()=>{
|
|
||||||
const c = await ContentAccessor.findList(props.option);
|
|
||||||
setState({content:c});
|
|
||||||
})()
|
|
||||||
},[props.option]);
|
|
||||||
const num = 1;
|
|
||||||
const classes = useStyles();
|
|
||||||
const theme = useTheme();
|
|
||||||
const isMobile = !useMediaQuery(theme.breakpoints.up("sm"));
|
|
||||||
if(state.content === undefined){
|
|
||||||
return (<LoadingCircle/>);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
return (<div className={classes.root}>{
|
|
||||||
state.content.map(x=>{
|
|
||||||
const thumbnail_url = makeThumbnailUrl(x);
|
|
||||||
return (<Paper key={x.id} elevation={4} className={classes.contentPaper}>
|
|
||||||
<Link className={classes.anchor_thumbnail}
|
|
||||||
component={RouterLink} to={makeContentInfoUrl(x.id)}>
|
|
||||||
<ThumbnailContainer content={x} className={classes.content_thumnail}></ThumbnailContainer>
|
|
||||||
</Link>
|
|
||||||
<Box className={classes.content_info}>
|
|
||||||
<Link component={RouterLink} to={makeContentInfoUrl(x.id)} variant="h5" color="inherit"
|
|
||||||
className={classes.content_info_title}>
|
|
||||||
{x.title}
|
|
||||||
</Link>
|
|
||||||
<Box className={classes.tag_list}>
|
|
||||||
{x.tags.map(x=>{
|
|
||||||
return (<TagChip key={x} label={x} clickable={true} tagname={x} size="small"></TagChip>);
|
|
||||||
})}
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Paper>);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</div>);
|
|
||||||
}
|
|
||||||
}
|
}
|
2
src/client/page/mod.ts
Normal file
2
src/client/page/mod.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './contentinfo';
|
||||||
|
export * from './gallery';
|
Loading…
Reference in New Issue
Block a user