add gallery page
This commit is contained in:
parent
5864bc1547
commit
1fd3c974e0
11
package.json
11
package.json
@ -11,6 +11,15 @@
|
|||||||
"start": "ts-node src/server.ts",
|
"start": "ts-node src/server.ts",
|
||||||
"check-types": "tsc"
|
"check-types": "tsc"
|
||||||
},
|
},
|
||||||
|
"browserslist":{
|
||||||
|
"production":[
|
||||||
|
"> 10%"
|
||||||
|
],
|
||||||
|
"development":[
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version"
|
||||||
|
]
|
||||||
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -24,6 +33,7 @@
|
|||||||
"node-stream-zip": "^1.12.0",
|
"node-stream-zip": "^1.12.0",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
|
"react-router-dom": "^5.2.0",
|
||||||
"sqlite3": "^5.0.0",
|
"sqlite3": "^5.0.0",
|
||||||
"ts-node": "^9.1.1"
|
"ts-node": "^9.1.1"
|
||||||
},
|
},
|
||||||
@ -41,6 +51,7 @@
|
|||||||
"@types/node": "^14.14.16",
|
"@types/node": "^14.14.16",
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "^17.0.0",
|
||||||
"@types/react-dom": "^17.0.0",
|
"@types/react-dom": "^17.0.0",
|
||||||
|
"@types/react-router-dom": "^5.1.7",
|
||||||
"babel-core": "^6.26.3",
|
"babel-core": "^6.26.3",
|
||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
"css-loader": "^5.0.1",
|
"css-loader": "^5.0.1",
|
||||||
|
67
src/client/accessor/contents.ts
Normal file
67
src/client/accessor/contents.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import {Content, ContentAccessor, ContentContent, QueryListOption} from "../../model/contents";
|
||||||
|
import {toQueryString} from './util';
|
||||||
|
const baseurl = "/content";
|
||||||
|
|
||||||
|
export class ClientContentAccessesor implements ContentAccessor{
|
||||||
|
async findList(option?: QueryListOption | undefined): Promise<Content[]>{
|
||||||
|
let res = await fetch(`${baseurl}/search?${option !== undefined ? toQueryString(option) : ""}`);
|
||||||
|
//if(res.status !== 200);
|
||||||
|
let ret = await res.json();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
async findById(id: number, tagload?: boolean | undefined): Promise<Content | undefined>{
|
||||||
|
let res = await fetch(`${baseurl}/${id}`);
|
||||||
|
if(res.status !== 200) return undefined;
|
||||||
|
let ret = await res.json();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* not implement
|
||||||
|
*/
|
||||||
|
async findListByBasePath(basepath: string): Promise<Content[]>{
|
||||||
|
throw new Error("");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
async update(c: Partial<Content> & { id: number; }): Promise<boolean>{
|
||||||
|
const {id,...rest} = c;
|
||||||
|
const res = await fetch(`${baseurl}/${id}`,{
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(rest)
|
||||||
|
});
|
||||||
|
const ret = await res.json();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
async add(c: ContentContent): Promise<number>{
|
||||||
|
const res = await fetch(`${baseurl}`,{
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(c)
|
||||||
|
});
|
||||||
|
const ret = await res.json();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
async del(id: number): Promise<boolean>{
|
||||||
|
const res = await fetch(`${baseurl}/${id}`,{
|
||||||
|
method: "DELETE"
|
||||||
|
});
|
||||||
|
const ret = await res.json();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
async addTag(c: Content, tag_name: string): Promise<boolean>{
|
||||||
|
const {id,...rest} = c;
|
||||||
|
const res = await fetch(`${baseurl}/${id}/tags/${tag_name}`,{
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(rest)
|
||||||
|
});
|
||||||
|
const ret = await res.json();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
async delTag(c: Content, tag_name: string): Promise<boolean>{
|
||||||
|
const {id,...rest} = c;
|
||||||
|
const res = await fetch(`${baseurl}/${id}/tags/${tag_name}`,{
|
||||||
|
method: "DELETE",
|
||||||
|
body: JSON.stringify(rest)
|
||||||
|
});
|
||||||
|
const ret = await res.json();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
@ -2,11 +2,14 @@
|
|||||||
type Representable = string|number|boolean;
|
type Representable = string|number|boolean;
|
||||||
|
|
||||||
type ToQueryStringA = {
|
type ToQueryStringA = {
|
||||||
[name:string]:Representable|Representable[]
|
[name:string]:Representable|Representable[]|undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toQueryString = (obj:ToQueryStringA)=> {
|
export const toQueryString = (obj:ToQueryStringA)=> {
|
||||||
return Object.entries(obj).map(e =>
|
return Object.entries(obj)
|
||||||
|
.filter((e): e is [string,Representable|Representable[]] =>
|
||||||
|
e[1] !== undefined)
|
||||||
|
.map(e =>
|
||||||
e[1] instanceof Array
|
e[1] instanceof Array
|
||||||
? e[1].map(f=>`${e[0]}[]=${encodeURIComponent(f)}`).join('&')
|
? e[1].map(f=>`${e[0]}[]=${encodeURIComponent(f)}`).join('&')
|
||||||
: `${e[0]}=${encodeURIComponent(e[1])}`)
|
: `${e[0]}=${encodeURIComponent(e[1])}`)
|
@ -1,11 +1,24 @@
|
|||||||
import hello from './hello'
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDom from 'react-dom';
|
import ReactDom from 'react-dom';
|
||||||
import {Headline} from './test';
|
import {BrowserRouter, Route} from 'react-router-dom';
|
||||||
|
import {Headline} from './headline';
|
||||||
|
import {Gallery} from './gallery';
|
||||||
|
|
||||||
import style from '../css/style.css';
|
import style from '../css/style.css';
|
||||||
hello();
|
|
||||||
|
|
||||||
|
const App = ()=> (
|
||||||
|
<BrowserRouter>
|
||||||
|
<Headline>
|
||||||
|
<Route path="/" exact>
|
||||||
|
<Gallery></Gallery>
|
||||||
|
</Route>
|
||||||
|
<Route path="/profile"><div>test profile</div></Route>
|
||||||
|
</Headline>
|
||||||
|
</BrowserRouter>
|
||||||
|
);
|
||||||
|
|
||||||
ReactDom.render(
|
ReactDom.render(
|
||||||
<Headline />,
|
<App/>,
|
||||||
document.getElementById("root")
|
document.getElementById("root")
|
||||||
)
|
)
|
150
src/client/js/gallery.tsx
Normal file
150
src/client/js/gallery.tsx
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import { Box, CircularProgress, Typography, Paper, Chip, withStyles, colors } from '@material-ui/core';
|
||||||
|
import {ChipProps} from '@material-ui/core/Chip';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import {QueryListOption,Content} from '../../model/contents';
|
||||||
|
import {ClientContentAccessesor} from '../accessor/contents';
|
||||||
|
import {useTheme, makeStyles, Theme, emphasize, fade} from '@material-ui/core/styles';
|
||||||
|
import {blue , pink} from '@material-ui/core/colors';
|
||||||
|
|
||||||
|
type TagChipStyleProp = {
|
||||||
|
color: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const useTagStyles = makeStyles((theme:Theme)=>({
|
||||||
|
root:(props:TagChipStyleProp)=>({
|
||||||
|
color: theme.palette.getContrastText(props.color),
|
||||||
|
backgroundColor: props.color,
|
||||||
|
}),
|
||||||
|
clickable:(props:TagChipStyleProp)=>({
|
||||||
|
'&:hover, &:focus':{
|
||||||
|
backgroundColor:emphasize(props.color,0.08)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
deletable: {
|
||||||
|
'&:focus': {
|
||||||
|
backgroundColor: (props:TagChipStyleProp)=>emphasize(props.color, 0.2),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
outlined:{
|
||||||
|
color: (props:TagChipStyleProp)=>props.color,
|
||||||
|
border: (props:TagChipStyleProp)=> `1px solid ${props.color}`,
|
||||||
|
'$clickable&:hover, $clickable&:focus, $deletable&:focus': {
|
||||||
|
backgroundColor:(props:TagChipStyleProp)=>fade(props.color,theme.palette.action.hoverOpacity),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon:{
|
||||||
|
color:"inherit",
|
||||||
|
},
|
||||||
|
deleteIcon:{
|
||||||
|
color:(props:TagChipStyleProp)=>fade(theme.palette.getContrastText(props.color),0.7),
|
||||||
|
"&:hover, &:active":{
|
||||||
|
color:(props:TagChipStyleProp)=>theme.palette.getContrastText(props.color),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const getTagColorName = (tagname :string):string=>{
|
||||||
|
if(tagname.startsWith("female")){
|
||||||
|
return pink['700'];
|
||||||
|
}
|
||||||
|
else if(tagname.startsWith("male")){
|
||||||
|
return blue[600];
|
||||||
|
}
|
||||||
|
else return "default";
|
||||||
|
}
|
||||||
|
|
||||||
|
const TagChip = (props:Omit<ChipProps,"color"> & {color: string})=>{
|
||||||
|
const {color,...rest} = props;
|
||||||
|
const classes = useTagStyles({color : color !== "default" ? color : "#000"});
|
||||||
|
return <Chip color="default" classes={{
|
||||||
|
...(color !== "default" ? classes : {})
|
||||||
|
}} {...rest}></Chip>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme:Theme)=>({
|
||||||
|
root:{
|
||||||
|
display:"grid",
|
||||||
|
gridGap: theme.spacing(4),
|
||||||
|
},
|
||||||
|
table_thumbnail:{
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
background: '#272733',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
contentPaper:{
|
||||||
|
display:'flex',
|
||||||
|
height:200,
|
||||||
|
},
|
||||||
|
content_thumnail: {
|
||||||
|
maxWidth: '100%',
|
||||||
|
maxHeight: '100%',
|
||||||
|
},
|
||||||
|
content_info:{
|
||||||
|
padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`,
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
},
|
||||||
|
tag_list:{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-start',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
'& > *': {
|
||||||
|
margin: theme.spacing(0.5),
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
export type GalleryProp = {
|
||||||
|
option?:QueryListOption;
|
||||||
|
};
|
||||||
|
type GalleryState = {
|
||||||
|
content:Content[]|undefined;
|
||||||
|
}
|
||||||
|
const content_accessor = new ClientContentAccessesor;
|
||||||
|
|
||||||
|
export const Gallery = (props: GalleryProp)=>{
|
||||||
|
const [state,setState]= useState<GalleryState>({content:undefined});
|
||||||
|
useEffect(()=>{
|
||||||
|
(async ()=>{
|
||||||
|
const c = await content_accessor.findList(props.option);
|
||||||
|
setState({content:c});
|
||||||
|
})()
|
||||||
|
},[props.option]);
|
||||||
|
const num = 1;
|
||||||
|
const classes = useStyles();
|
||||||
|
const theme = useTheme();
|
||||||
|
if(state.content === undefined){
|
||||||
|
return (<div><Box>
|
||||||
|
<CircularProgress title="loading"/>
|
||||||
|
</Box></div>);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return (<div className={classes.root}>{
|
||||||
|
state.content.map(x=>{
|
||||||
|
const thumbnail_url = `/content/${x.id}/${x.content_type}/thumbnail`;
|
||||||
|
return (<Paper key={x.id} elevation={4} className={classes.contentPaper}>
|
||||||
|
<a className={classes.table_thumbnail}>{
|
||||||
|
x.content_type === "manga" ? <img className={classes.content_thumnail} src={thumbnail_url}></img> :
|
||||||
|
<video className={classes.content_thumnail} src={thumbnail_url}></video>
|
||||||
|
}</a>
|
||||||
|
<Box className={classes.content_info}>
|
||||||
|
<Typography variant="h5" style={{marginLeft:theme.spacing(2)}}>{x.title}</Typography>
|
||||||
|
<Box className={classes.tag_list}>
|
||||||
|
{x.tags.map(x=>{
|
||||||
|
const tagcolor = getTagColorName(x);
|
||||||
|
console.log(tagcolor);
|
||||||
|
return (<TagChip key={x} label={x} clickable={true} color={tagcolor}></TagChip>);
|
||||||
|
})}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Paper>);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
}
|
206
src/client/js/headline.tsx
Normal file
206
src/client/js/headline.tsx
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
import ReactDom from 'react-dom';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
Button, CssBaseline, Divider, IconButton, List, ListItem, Drawer,
|
||||||
|
AppBar, Toolbar, Typography, InputBase, ListItemIcon, ListItemText, Menu, MenuItem,
|
||||||
|
Hidden, useMediaQuery, Tooltip
|
||||||
|
} from '@material-ui/core';
|
||||||
|
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 {Link} from 'react-router-dom';
|
||||||
|
|
||||||
|
const drawerWidth = 240;
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) => ({
|
||||||
|
root: {
|
||||||
|
display: 'flex'
|
||||||
|
},
|
||||||
|
appBar: {
|
||||||
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
|
transition: theme.transitions.create(['width', 'margin'], {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen
|
||||||
|
})
|
||||||
|
},
|
||||||
|
menuButton: {
|
||||||
|
marginRight: 36
|
||||||
|
},
|
||||||
|
hide: {
|
||||||
|
display: "none"
|
||||||
|
},
|
||||||
|
drawer: {
|
||||||
|
flexShrink: 0,
|
||||||
|
whiteSpace: "nowrap",
|
||||||
|
[theme.breakpoints.up("sm")]:{
|
||||||
|
width: drawerWidth,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
drawerPaper: {
|
||||||
|
width: drawerWidth
|
||||||
|
},
|
||||||
|
drawerClose: {
|
||||||
|
overflowX: 'hidden',
|
||||||
|
[theme.breakpoints.up("sm")]:{
|
||||||
|
width: theme.spacing(7) + 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
padding: theme.spacing(0, 1),
|
||||||
|
...theme.mixins.toolbar,
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
flexGrow: 1,
|
||||||
|
padding: theme.spacing(3),
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: 'none',
|
||||||
|
[theme.breakpoints.up("sm")]: {
|
||||||
|
display: 'block'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
position: 'relative',
|
||||||
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
backgroundColor: fade(theme.palette.common.white, 0.15),
|
||||||
|
'&:hover': {
|
||||||
|
backgroundColor: fade(theme.palette.common.white, 0.25),
|
||||||
|
},
|
||||||
|
marginRight: theme.spacing(2),
|
||||||
|
marginLeft: 0,
|
||||||
|
width: '100%',
|
||||||
|
[theme.breakpoints.up('sm')]: {
|
||||||
|
marginLeft: theme.spacing(3),
|
||||||
|
width: 'auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
searchIcon: {
|
||||||
|
padding: theme.spacing(0, 2),
|
||||||
|
height: '100%',
|
||||||
|
position: 'absolute',
|
||||||
|
pointerEvents: 'none',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
inputRoot: {
|
||||||
|
color: 'inherit'
|
||||||
|
},
|
||||||
|
inputInput: {
|
||||||
|
padding: theme.spacing(1, 1, 1, 0),
|
||||||
|
// vertical padding + font size from searchIcon
|
||||||
|
paddingLeft: `calc(1em + ${theme.spacing(4)}px)`,
|
||||||
|
transition: theme.transitions.create('width'),
|
||||||
|
width: '100%',
|
||||||
|
[theme.breakpoints.up('md')]: {
|
||||||
|
width: '20ch',
|
||||||
|
"&:hover": {
|
||||||
|
width: '25ch'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
grow: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const Headline = (prop: { children?: React.ReactNode }) => {
|
||||||
|
const [v, setv] = useState(false);
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||||
|
const classes = useStyles();
|
||||||
|
const theme = useTheme();
|
||||||
|
const toggleV = () => setv(!v);
|
||||||
|
const handleProfileMenuOpen = (e:React.MouseEvent<HTMLElement>)=>setAnchorEl(e.currentTarget);
|
||||||
|
const handleProfileMenuClose = ()=>setAnchorEl(null);
|
||||||
|
const isProfileMenuOpened = Boolean(anchorEl);
|
||||||
|
const menuId = 'primary-search-account-menu';
|
||||||
|
|
||||||
|
const renderProfileMenu = (<Menu
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
anchorOrigin={{horizontal:'right',vertical:"top"}}
|
||||||
|
id={menuId}
|
||||||
|
open={isProfileMenuOpened}
|
||||||
|
keepMounted
|
||||||
|
transformOrigin={{horizontal:'right',vertical:"top"}}
|
||||||
|
onClose={handleProfileMenuClose}
|
||||||
|
>
|
||||||
|
<MenuItem component={Link} to='/profile'>Profile</MenuItem>
|
||||||
|
<MenuItem>Logout</MenuItem>
|
||||||
|
</Menu>);
|
||||||
|
const drawer_contents = (<>
|
||||||
|
<div className={classes.toolbar}>
|
||||||
|
<IconButton onClick={toggleV}>
|
||||||
|
{theme.direction === "ltr" ? <ChevronLeft /> : <ChevronRight />}
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
<List>
|
||||||
|
<ListItem button key="Back">
|
||||||
|
<ListItemIcon>
|
||||||
|
<Tooltip title="back" placement="bottom">
|
||||||
|
<ArrowBackIcon></ArrowBackIcon>
|
||||||
|
</Tooltip>
|
||||||
|
</ListItemIcon>
|
||||||
|
<ListItemText primary="Back"></ListItemText>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
</>);
|
||||||
|
return (<div className={classes.root}>
|
||||||
|
<CssBaseline />
|
||||||
|
<AppBar position="fixed" className={classes.appBar}>
|
||||||
|
<Toolbar>
|
||||||
|
<IconButton color="inherit"
|
||||||
|
aria-label="open drawer"
|
||||||
|
onClick={toggleV}
|
||||||
|
edge="start"
|
||||||
|
className={classes.menuButton}>
|
||||||
|
<MenuIcon></MenuIcon>
|
||||||
|
</IconButton>
|
||||||
|
<Typography variant="h5" noWrap className={classes.title}>
|
||||||
|
Ionian
|
||||||
|
</Typography>
|
||||||
|
<div className={classes.grow}></div>
|
||||||
|
<div className={classes.search}>
|
||||||
|
<div className={classes.searchIcon}>
|
||||||
|
<SearchIcon />
|
||||||
|
</div>
|
||||||
|
<InputBase placeholder="search"
|
||||||
|
classes={{ root: classes.inputRoot, input: classes.inputInput }}></InputBase>
|
||||||
|
</div>
|
||||||
|
<IconButton
|
||||||
|
edge="end"
|
||||||
|
aria-label="account of current user"
|
||||||
|
aria-controls={menuId}
|
||||||
|
aria-haspopup="true"
|
||||||
|
onClick={handleProfileMenuOpen}
|
||||||
|
color="inherit">
|
||||||
|
<AccountCircle />
|
||||||
|
</IconButton>
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
{renderProfileMenu}
|
||||||
|
<nav>
|
||||||
|
<Hidden smUp implementation="css">
|
||||||
|
<Drawer variant="temporary" anchor='left' open={v} onClose={toggleV}
|
||||||
|
classes={{paper: classes.drawerPaper}}>
|
||||||
|
{drawer_contents}
|
||||||
|
</Drawer>
|
||||||
|
</Hidden>
|
||||||
|
<Hidden xsDown implementation="css">
|
||||||
|
<Drawer variant='permanent' anchor='left' className={[classes.drawer, classes.drawerClose].join(" ").trim()} classes={{
|
||||||
|
paper: classes.drawerClose
|
||||||
|
}}>
|
||||||
|
{drawer_contents}
|
||||||
|
</Drawer>
|
||||||
|
</Hidden>
|
||||||
|
</nav>
|
||||||
|
<main className={classes.content}>
|
||||||
|
<div className={classes.toolbar}></div>
|
||||||
|
{prop.children}
|
||||||
|
</main>
|
||||||
|
</div>);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Headline;
|
@ -1,4 +0,0 @@
|
|||||||
export default function (){
|
|
||||||
console.log("hello");
|
|
||||||
console.log("???");
|
|
||||||
};
|
|
@ -1,88 +0,0 @@
|
|||||||
import ReactDom from 'react-dom';
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import Drawer from '@material-ui/core/Drawer';
|
|
||||||
import { Button, Divider, IconButton, List, ListItem } from '@material-ui/core';
|
|
||||||
import { makeStyles, Theme, useTheme } from '@material-ui/core/styles';
|
|
||||||
import {ChevronLeft, ChevronRight} from '@material-ui/icons';
|
|
||||||
|
|
||||||
const drawerWidth = 240;
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) => ({
|
|
||||||
root: {
|
|
||||||
display: 'flex'
|
|
||||||
},
|
|
||||||
appBar: {
|
|
||||||
transition: theme.transitions.create(['width', 'margin'], {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.leavingScreen
|
|
||||||
})
|
|
||||||
},
|
|
||||||
appBarShift: {
|
|
||||||
width: `calc(100% - ${drawerWidth}px)`,
|
|
||||||
marginLeft: drawerWidth,
|
|
||||||
transition: theme.transitions.create(['margin', 'width'], {
|
|
||||||
easing: theme.transitions.easing.easeOut,
|
|
||||||
duration: theme.transitions.duration.enteringScreen,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
drawer: {
|
|
||||||
width: drawerWidth,
|
|
||||||
flexShrink: 0,
|
|
||||||
},
|
|
||||||
drawerPaper: {
|
|
||||||
width: drawerWidth,
|
|
||||||
},
|
|
||||||
drawerHeader: {
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
padding: theme.spacing(0, 1),
|
|
||||||
// necessary for content to be below app bar
|
|
||||||
...theme.mixins.toolbar,
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
},
|
|
||||||
content: {
|
|
||||||
flexGrow: 1,
|
|
||||||
padding: theme.spacing(3),
|
|
||||||
transition: theme.transitions.create('margin', {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.leavingScreen,
|
|
||||||
}),
|
|
||||||
marginLeft: -drawerWidth,
|
|
||||||
},
|
|
||||||
contentShift: {
|
|
||||||
transition: theme.transitions.create('margin', {
|
|
||||||
easing: theme.transitions.easing.easeOut,
|
|
||||||
duration: theme.transitions.duration.enteringScreen,
|
|
||||||
}),
|
|
||||||
marginLeft: 0,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const Headline = () => {
|
|
||||||
const [v, setv] = useState(false);
|
|
||||||
const classes = useStyles();
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
return (<div className={classes.root}>
|
|
||||||
<Drawer variant='persistent' anchor='left' open={v} className={classes.drawer} classes={{paper:classes.drawerPaper}}>
|
|
||||||
<div className={classes.drawerHeader}>
|
|
||||||
<IconButton onClick={()=>setv(false)}>
|
|
||||||
{theme.direction === "ltr" ? <ChevronLeft /> : <ChevronRight />}
|
|
||||||
</IconButton>
|
|
||||||
</div>
|
|
||||||
<Divider />
|
|
||||||
<List>
|
|
||||||
<ListItem>
|
|
||||||
|
|
||||||
<div>NO</div>
|
|
||||||
</ListItem>
|
|
||||||
</List>
|
|
||||||
</Drawer>
|
|
||||||
<main className={([classes.content, v ? classes.contentShift: ""].join(" ").trim())}>
|
|
||||||
<h1>aaa{`a${v} ${classes.content}`}aaa</h1>
|
|
||||||
<Button onClick={() => setv(!v)}>open</Button>
|
|
||||||
</main>
|
|
||||||
</div>);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Headline;
|
|
Loading…
Reference in New Issue
Block a user